Browse Source

Monotonicity for computing extremal value and parameter space partitioning

main
Jip Spel 4 years ago
parent
commit
5a37a40cea
  1. 14
      resources/3rdparty/include_cudd.cmake
  2. 148
      resources/examples/testfiles/pdtmc/brp16_2_mon_incr.pm
  3. 34
      resources/examples/testfiles/pdtmc/casestudy1.pm
  4. 39
      resources/examples/testfiles/pdtmc/casestudy2.pm
  5. 33
      resources/examples/testfiles/pdtmc/casestudy3.pm
  6. 35
      resources/examples/testfiles/pdtmc/parametric_die_2.pm
  7. 16
      resources/examples/testfiles/pdtmc/simple1.pm
  8. 18
      resources/examples/testfiles/pdtmc/simple3.pm
  9. 17
      resources/examples/testfiles/pdtmc/simple4.pm
  10. 3
      resources/examples/testfiles/pdtmc/simple5.pm
  11. 26
      resources/examples/testfiles/pdtmc/zeroconf4.pm
  12. 421
      src/storm-pars-cli/storm-pars.cpp
  13. 389
      src/storm-pars/analysis/AssumptionChecker.cpp
  14. 58
      src/storm-pars/analysis/AssumptionChecker.h
  15. 116
      src/storm-pars/analysis/AssumptionMaker.cpp
  16. 56
      src/storm-pars/analysis/AssumptionMaker.h
  17. 175
      src/storm-pars/analysis/LocalMonotonicityResult.cpp
  18. 113
      src/storm-pars/analysis/LocalMonotonicityResult.h
  19. 757
      src/storm-pars/analysis/MonotonicityChecker.cpp
  20. 148
      src/storm-pars/analysis/MonotonicityChecker.h
  21. 325
      src/storm-pars/analysis/MonotonicityHelper.cpp
  22. 171
      src/storm-pars/analysis/MonotonicityHelper.h
  23. 231
      src/storm-pars/analysis/MonotonicityResult.cpp
  24. 130
      src/storm-pars/analysis/MonotonicityResult.h
  25. 912
      src/storm-pars/analysis/Order.cpp
  26. 324
      src/storm-pars/analysis/Order.h
  27. 1104
      src/storm-pars/analysis/OrderExtender.cpp
  28. 129
      src/storm-pars/analysis/OrderExtender.h
  29. 10
      src/storm-pars/api/analysis.h
  30. 113
      src/storm-pars/api/region.h
  31. 3
      src/storm-pars/api/storm-pars.h
  32. 263
      src/storm-pars/modelchecker/region/RegionModelChecker.cpp
  33. 46
      src/storm-pars/modelchecker/region/RegionModelChecker.h
  34. 347
      src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.cpp
  35. 51
      src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.h
  36. 10
      src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.cpp
  37. 2
      src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.h
  38. 482
      src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.cpp
  39. 35
      src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.h
  40. 8
      src/storm-pars/modelchecker/region/ValidatingSparseDtmcParameterLiftingModelChecker.cpp
  41. 2
      src/storm-pars/modelchecker/region/ValidatingSparseDtmcParameterLiftingModelChecker.h
  42. 10
      src/storm-pars/modelchecker/region/ValidatingSparseMdpParameterLiftingModelChecker.cpp
  43. 2
      src/storm-pars/modelchecker/region/ValidatingSparseMdpParameterLiftingModelChecker.h
  44. 4
      src/storm-pars/modelchecker/region/ValidatingSparseParameterLiftingModelChecker.cpp
  45. 2
      src/storm-pars/modelchecker/region/ValidatingSparseParameterLiftingModelChecker.h
  46. 73
      src/storm-pars/parser/MonotonicityParser.cpp
  47. 16
      src/storm-pars/parser/MonotonicityParser.h
  48. 34
      src/storm-pars/parser/ParameterRegionParser.cpp
  49. 7
      src/storm-pars/parser/ParameterRegionParser.h
  50. 76
      src/storm-pars/settings/modules/MonotonicitySettings.cpp
  51. 45
      src/storm-pars/settings/modules/MonotonicitySettings.h
  52. 11
      src/storm-pars/settings/modules/ParametricSettings.cpp
  53. 10
      src/storm-pars/settings/modules/ParametricSettings.h
  54. 57
      src/storm-pars/settings/modules/RegionSettings.cpp
  55. 22
      src/storm-pars/settings/modules/RegionSettings.h
  56. 145
      src/storm-pars/storage/ParameterRegion.cpp
  57. 25
      src/storm-pars/storage/ParameterRegion.h
  58. 106
      src/storm-pars/transformer/ParameterLifter.cpp
  59. 58
      src/storm-pars/transformer/ParameterLifter.h
  60. 9
      src/storm/settings/modules/GeneralSettings.cpp
  61. 6
      src/storm/settings/modules/GeneralSettings.h
  62. 14
      src/storm/settings/modules/IOSettings.cpp
  63. 15
      src/storm/settings/modules/IOSettings.h
  64. 61
      src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp
  65. 17
      src/storm/solver/MinMaxLinearEquationSolver.cpp
  66. 9
      src/storm/solver/MinMaxLinearEquationSolver.h
  67. 128
      src/storm/solver/TopologicalMinMaxLinearEquationSolver.cpp
  68. 40
      src/storm/utility/graph.cpp
  69. 4
      src/storm/utility/graph.h
  70. 2
      src/test/storm-pars/CMakeLists.txt
  71. 284
      src/test/storm-pars/analysis/AssumptionCheckerTest.cpp
  72. 341
      src/test/storm-pars/analysis/AssumptionMakerTest.cpp
  73. 375
      src/test/storm-pars/analysis/MonotonicityCheckerTest.cpp
  74. 454
      src/test/storm-pars/analysis/MonotonicityHelperTest.cpp
  75. 364
      src/test/storm-pars/analysis/OrderExtenderTest.cpp
  76. 97
      src/test/storm-pars/analysis/OrderTest.cpp
  77. 732
      src/test/storm-pars/modelchecker/SparseDtmcParameterLiftingTest.cpp

14
resources/3rdparty/include_cudd.cmake

@ -34,25 +34,13 @@ if (CMAKE_OSX_SYSROOT)
set(CUDD_INCLUDE_FLAGS "CPPFLAGS=--sysroot=${CMAKE_OSX_SYSROOT}")
endif()
set(CUDD_CXX_COMPILER "${CMAKE_CXX_COMPILER}")
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")
if (CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 12.0.0.12000032)
if (CMAKE_HOST_SYSTEM_VERSION VERSION_GREATER_EQUAL 20.1.0)
message(WARNING "There are some known issues compiling CUDD on some setups. We implemented a workaround that mostly works, but if you still have problems compiling CUDD, especially if you do not use the default compiler of your system, please contact the Storm developers.")
# The issue is known to occur using the Command Line Tools for XCode 12.2. Apparently, it is fixed in the beta for XCode 12.3.
set(CUDD_CXX_COMPILER "c++")
endif()
endif()
endif()
ExternalProject_Add(
cudd3
DOWNLOAD_COMMAND ""
SOURCE_DIR ${STORM_3RDPARTY_SOURCE_DIR}/cudd-3.0.0
PREFIX ${STORM_3RDPARTY_BINARY_DIR}/cudd-3.0.0
PATCH_COMMAND ${AUTORECONF}
CONFIGURE_COMMAND ${STORM_3RDPARTY_SOURCE_DIR}/cudd-3.0.0/configure --enable-shared --enable-obj --with-pic=yes --prefix=${STORM_3RDPARTY_BINARY_DIR}/cudd-3.0.0 --libdir=${CUDD_LIB_DIR} CC=${CMAKE_C_COMPILER} CXX=${CUDD_CXX_COMPILER} ${CUDD_INCLUDE_FLAGS}
CONFIGURE_COMMAND ${STORM_3RDPARTY_SOURCE_DIR}/cudd-3.0.0/configure --enable-shared --enable-obj --with-pic=yes --prefix=${STORM_3RDPARTY_BINARY_DIR}/cudd-3.0.0 --libdir=${CUDD_LIB_DIR} CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} ${CUDD_INCLUDE_FLAGS}
BUILD_COMMAND make ${STORM_CUDD_FLAGS}
INSTALL_COMMAND make install
BUILD_IN_SOURCE 0

148
resources/examples/testfiles/pdtmc/brp16_2_mon_incr.pm

@ -0,0 +1,148 @@
// bounded retransmission protocol [D'AJJL01]
// gxn/dxp 23/05/2001
dtmc
// number of chunks
const int N = 16;
// maximum number of retransmissions
const int MAX = 2;
// reliability of channels
const double pL;
const double pK;
// timeouts
const double TOMsg;
const double TOAck;
module sender
s : [0..6];
// 0 idle
// 1 next_frame
// 2 wait_ack
// 3 retransmit
// 4 success
// 5 error
// 6 wait sync
srep : [0..3];
// 0 bottom
// 1 not ok (nok)
// 2 do not know (dk)
// 3 ok (ok)
nrtr : [0..MAX];
i : [0..N];
bs : bool;
s_ab : bool;
fs : bool;
ls : bool;
// idle
[NewFile] (s=0) -> (s'=1) & (i'=1) & (srep'=0);
// next_frame
[aF] (s=1) -> (s'=2) & (fs'=(i=1)) & (ls'=(i=N)) & (bs'=s_ab) & (nrtr'=0);
// wait_ack
[aB] (s=2) -> (s'=4) & (s_ab'=!s_ab);
[TO_Msg] (s=2) -> (s'=3);
[TO_Ack] (s=2) -> (s'=3);
// retransmit
[aF] (s=3) & (nrtr<MAX) -> (s'=2) & (fs'=(i=1)) & (ls'=(i=N)) & (bs'=s_ab) & (nrtr'=nrtr+1);
[] (s=3) & (nrtr=MAX) & (i<N) -> (s'=5) & (srep'=1);
[] (s=3) & (nrtr=MAX) & (i=N) -> (s'=5) & (srep'=2);
// success
[] (s=4) & (i<N) -> (s'=1) & (i'=i+1);
[] (s=4) & (i=N) -> (s'=0) & (srep'=3);
// error
[SyncWait] (s=5) -> (s'=6);
// wait sync
[SyncWait] (s=6) -> (s'=0) & (s_ab'=false);
endmodule
module receiver
r : [0..5];
// 0 new_file
// 1 fst_safe
// 2 frame_received
// 3 frame_reported
// 4 idle
// 5 resync
rrep : [0..4];
// 0 bottom
// 1 fst
// 2 inc
// 3 ok
// 4 nok
fr : bool;
lr : bool;
br : bool;
r_ab : bool;
recv : bool;
// new_file
[SyncWait] (r=0) -> (r'=0);
[aG] (r=0) -> (r'=1) & (fr'=fs) & (lr'=ls) & (br'=bs) & (recv'=T);
// fst_safe_frame
[] (r=1) -> (r'=2) & (r_ab'=br);
// frame_received
[] (r=2) & (r_ab=br) & (fr=true) & (lr=false) -> (r'=3) & (rrep'=1);
[] (r=2) & (r_ab=br) & (fr=false) & (lr=false) -> (r'=3) & (rrep'=2);
[] (r=2) & (r_ab=br) & (fr=false) & (lr=true) -> (r'=3) & (rrep'=3);
[aA] (r=2) & !(r_ab=br) -> (r'=4);
// frame_reported
[aA] (r=3) -> (r'=4) & (r_ab'=!r_ab);
// idle
[aG] (r=4) -> (r'=2) & (fr'=fs) & (lr'=ls) & (br'=bs) & (recv'=T);
[SyncWait] (r=4) & (ls=true) -> (r'=5);
[SyncWait] (r=4) & (ls=false) -> (r'=5) & (rrep'=4);
// resync
[SyncWait] (r=5) -> (r'=0) & (rrep'=0);
endmodule
// prevents more than one file being sent
module tester
T : bool;
[NewFile] (T=false) -> (T'=true);
endmodule
module channelK
k : [0..2];
// idle
[aF] (k=0) -> 1-pK : (k'=1) + pK : (k'=2);
// sending
[aG] (k=1) -> (k'=0);
// lost
[TO_Msg] (k=2) -> (k'=0);
endmodule
module channelL
l : [0..2];
// idle
[aA] (l=0) -> 1-pL : (l'=1) + pL : (l'=2);
// sending
[aB] (l=1) -> (l'=0);
// lost
[TO_Ack] (l=2) -> (l'=0);
endmodule
label "error" = s=5;
rewards
[TO_Msg] true : TOMsg;
[TO_Ack] true : TOAck;
endrewards

34
resources/examples/testfiles/pdtmc/casestudy1.pm

@ -0,0 +1,34 @@
dtmc
const double p;
module test
// local state
s : [0..4] init 0;
[] s=0 -> p : (s'=1) + (1-p) : (s'=2);
[] s=1 -> p : (s'=3) + (1-p) : (s'=4);
[] s=2 -> 0.5*p : (s'=3) + (1-0.5*p) : (s'=4);
[] s=3 -> 1 : (s'=3);
[] s=4 -> 1 : (s'=4);
endmodule
// Dot output:
//digraph model {
// 0 [ label = "0: {init}" ];
// 1 [ label = "1: {}" ];
// 2 [ label = "2: {}" ];
// 3 [ label = "3: {}" ];
// 4 [ label = "4: {}" ];
// 0 -> 1 [ label= "(p)/(1)" ];
// 0 -> 2 [ label= "(-1 * (p+(-1)))/(1)" ];
// 1 -> 3 [ label= "(p)/(1)" ];
// 1 -> 4 [ label= "(-1 * (p+(-1)))/(1)" ];
// 2 -> 3 [ label= "(p)/(2)" ];
// 2 -> 4 [ label= "(-1 * (p+(-2)))/(2)" ];
// 3 -> 3 [ label= "1" ];
// 4 -> 4 [ label= "1" ];
//}

39
resources/examples/testfiles/pdtmc/casestudy2.pm

@ -0,0 +1,39 @@
dtmc
const double p;
module test
// local state
s : [0..5] init 0;
[] s=0 -> 0.4*p : (s'=1) + (1-p) : (s'=2) + 0.6*p : (s'=3);
[] s=1 -> 0.5*p : (s'=4) + 0.5*p : (s'=3) + (1-p) : (s'=5);
[] s=2 -> 0.3*p : (s'=4) + (1-0.3*p) : (s'=5);
[] s=3 -> 0.7*p : (s'=4) + (1-0.7*p) : (s'=5);
[] s=4 -> 1 : (s'=4);
[] s=5 -> 1 : (s'=5);
endmodule
// Dot output:
//digraph model {
// 0 [ label = "0: {init}" ];
// 1 [ label = "1: {}" ];
// 2 [ label = "2: {}" ];
// 3 [ label = "3: {}" ];
// 4 [ label = "4: {}" ];
// 5 [ label = "5: {}" ];
// 0 -> 1 [ label= "(2 * (p))/(5)" ];
// 0 -> 2 [ label= "(-1 * (p+(-1)))/(1)" ];
// 0 -> 3 [ label= "(3 * (p))/(5)" ];
// 1 -> 3 [ label= "(p)/(2)" ];
// 1 -> 4 [ label= "(p)/(2)" ];
// 1 -> 5 [ label= "(-1 * (p+(-1)))/(1)" ];
// 2 -> 4 [ label= "(3 * (p))/(10)" ];
// 2 -> 5 [ label= "(-1 * (3*p+(-10)))/(10)" ];
// 3 -> 4 [ label= "(7 * (p))/(10)" ];
// 3 -> 5 [ label= "(-1 * (7*p+(-10)))/(10)" ];
// 4 -> 4 [ label= "1" ];
// 5 -> 5 [ label= "1" ];
//}

33
resources/examples/testfiles/pdtmc/casestudy3.pm

@ -0,0 +1,33 @@
dtmc
const double p;
module test
// local state
s : [0..4] init 0;
[] s=0 -> p*(1-p) : (s'=1) + (1-p*(1-p)) : (s'=2);
[] s=1 -> p : (s'=3) + (1-p) : (s'=4);
[] s=2 -> 0.5*p : (s'=3) + (1-0.5*p) : (s'=4);
[] s=3 -> 1 : (s'=3);
[] s=4 -> 1 : (s'=4);
endmodule
// Dot output:
//digraph model {
// 0 [ label = "0: {init}" ];
// 1 [ label = "1: {}" ];
// 2 [ label = "2: {}" ];
// 3 [ label = "3: {}" ];
// 4 [ label = "4: {}" ];
// 0 -> 1 [ label= "(-1 * ((p) * (p+(-1))))/(1)" ];
// 0 -> 2 [ label= "(p^2+(-1)*p+1)/(1)" ];
// 1 -> 3 [ label= "(p)/(1)" ];
// 1 -> 4 [ label= "(1-p)/(1)" ];
// 2 -> 3 [ label= "(1-(p)/(2))" ];
// 2 -> 4 [ label= "(p)/(2)" ];
// 3 -> 3 [ label= "1" ];
// 4 -> 4 [ label= "1" ];
//}

35
resources/examples/testfiles/pdtmc/parametric_die_2.pm

@ -0,0 +1,35 @@
// Knuth's model of a fair die using only fair coins
dtmc
const double p;
const double q;
module die
// local state
s : [0..7] init 0;
// value of the dice
d : [0..6] init 0;
[] s=0 -> p : (s'=1) + (1-p) : (s'=2);
[] s=1 -> q : (s'=3) + (1-q) : (s'=4);
[] s=2 -> q : (s'=5) + (1-q) : (s'=6);
[] s=3 -> p : (s'=1) + (1-p) : (s'=7) & (d'=1);
[] s=4 -> p : (s'=7) & (d'=2) + (1-p) : (s'=7) & (d'=3);
[] s=5 -> p : (s'=7) & (d'=4) + (1-p) : (s'=7) & (d'=5);
[] s=6 -> p : (s'=2) + (1-p) : (s'=7) & (d'=6);
[] s=7 -> 1: (s'=7);
endmodule
rewards "coin_flips"
[] s<7 : 1;
endrewards
label "one" = s=7&d=1;
label "two" = s=7&d=2;
label "three" = s=7&d=3;
label "four" = s=7&d=4;
label "five" = s=7&d=5;
label "six" = s=7&d=6;
label "done" = s=7;

16
resources/examples/testfiles/pdtmc/simple1.pm

@ -15,3 +15,19 @@ module test
endmodule
// Dot output:
//digraph model {
// 0 [ label = "0: {init}" ];
// 1 [ label = "1: {}" ];
// 2 [ label = "2: {}" ];
// 3 [ label = "3: {}" ];
// 4 [ label = "4: {}" ];
// 0 -> 1 [ label= "(p)/(1)" ];
// 0 -> 2 [ label= "(-1 * (p+(-1)))/(1)" ];
// 1 -> 3 [ label= "(p)/(1)" ];
// 1 -> 4 [ label= "(-1 * (p+(-1)))/(1)" ];
// 2 -> 3 [ label= "(-1 * (p+(-1)))/(1)" ];
// 2 -> 4 [ label= "(p)/(1)" ];
// 3 -> 3 [ label= "1" ];
// 4 -> 4 [ label= "1" ];
//}

18
resources/examples/testfiles/pdtmc/simple3.pm

@ -1,18 +0,0 @@
dtmc
const double p;
module test
// local state
s : [0..5] init 0;
[] s=0 -> 0.4*p : (s'=1) + (1-p) : (s'=2) + 0.6*p : (s'=3);
[] s=1 -> 0.5*p : (s'=4) + 0.5*p : (s'=3) + (1-p) : (s'=5);
[] s=2 -> 0.3*p : (s'=4) + (1-0.3*p) : (s'=5);
[] s=3 -> 0.7*p : (s'=4) + (1-0.7*p) : (s'=5);
[] s=4 -> 1 : (s'=4);
[] s=5 -> 1 : (s'=5);
endmodule

17
resources/examples/testfiles/pdtmc/simple4.pm

@ -1,17 +0,0 @@
dtmc
const double p;
module test
// local state
s : [0..4] init 0;
[] s=0 -> p*(1-p) : (s'=1) + (1-p*(1-p)) : (s'=2);
[] s=1 -> p : (s'=3) + (1-p) : (s'=4);
[] s=2 -> (1-p) : (s'=3) + (p) : (s'=4);
[] s=3 -> 1 : (s'=3);
[] s=4 -> 1 : (s'=4);
endmodule

3
resources/examples/testfiles/pdtmc/simple2.pm → resources/examples/testfiles/pdtmc/simple5.pm

@ -1,6 +1,7 @@
dtmc
const double p;
const double q;
module test
@ -8,7 +9,7 @@ module test
s : [0..4] init 0;
[] s=0 -> p : (s'=1) + (1-p) : (s'=2);
[] s=1 -> p : (s'=3) + (1-p) : (s'=4);
[] s=1 -> q : (s'=3) + (1-q) : (s'=4);
[] s=2 -> 0.5*p : (s'=3) + (1-0.5*p) : (s'=4);
[] s=3 -> 1 : (s'=3);
[] s=4 -> 1 : (s'=4);

26
resources/examples/testfiles/pdtmc/zeroconf4.pm

@ -0,0 +1,26 @@
// Model taken from Daws04
// This version by Ernst Moritz Hahn (emh@cs.uni-sb.de)
dtmc
const double pK;
const double pL;
const int n;
module main
s: [-2..n+1];
[b] (s=-1) -> (s'=-2);
[a] (s=0) -> 1-pL : (s'=-1) + pL : (s'=1);
[a] (s>0) & (s<n+1) -> 1-pK : (s'=0) + pK : (s'=s+1);
endmodule
init
s = 0
endinit
rewards
[a] true : 1;
[b] true : n-1;
endrewards

421
src/storm-pars-cli/storm-pars.cpp

@ -1,11 +1,11 @@
#include "storm-pars/analysis/MonotonicityChecker.h"
#include "storm-cli-utilities/cli.h"
#include "storm-cli-utilities/model-handling.h"
#include "storm-pars/api/storm-pars.h"
#include "storm-pars/api/region.h"
#include "storm-pars/analysis/MonotonicityHelper.h"
#include "storm-pars/modelchecker/instantiation/SparseCtmcInstantiationModelChecker.h"
#include "storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.h"
@ -81,8 +81,14 @@ namespace storm {
std::vector<storm::storage::ParameterRegion<ValueType>> parseRegions(std::shared_ptr<storm::models::ModelBase> const& model) {
std::vector<storm::storage::ParameterRegion<ValueType>> result;
auto regionSettings = storm::settings::getModule<storm::settings::modules::RegionSettings>();
boost::optional<int> splittingThreshold;
if (regionSettings.isSplittingThresholdSet()) {
splittingThreshold = regionSettings.getSplittingThreshold();
}
if (regionSettings.isRegionSet()) {
result = storm::api::parseRegions<ValueType>(regionSettings.getRegionString(), *model);
result = storm::api::parseRegions<ValueType>(regionSettings.getRegionString(), *model, splittingThreshold);
} else if (regionSettings.isRegionBoundSet()) {
result = storm::api::createRegion<ValueType>(regionSettings.getRegionBoundString(), *model, splittingThreshold);
}
return result;
}
@ -157,13 +163,123 @@ namespace storm {
return sampleInfo;
}
template <typename ValueType>
std::shared_ptr<storm::models::ModelBase> eliminateScc(std::shared_ptr<storm::models::ModelBase> const& model) {
storm::utility::Stopwatch eliminationWatch(true);
std::shared_ptr<storm::models::ModelBase> result;
if (model->isOfType(storm::models::ModelType::Dtmc)) {
STORM_PRINT("Applying scc elimination" << std::endl);
auto sparseModel = model->as<storm::models::sparse::Model<ValueType>>();
auto matrix = sparseModel->getTransitionMatrix();
auto backwardsTransitionMatrix = matrix.transpose();
storm::storage::StronglyConnectedComponentDecompositionOptions const options;
auto decomposition = storm::storage::StronglyConnectedComponentDecomposition<ValueType>(matrix, options);
storm::storage::BitVector selectedStates(matrix.getRowCount());
storm::storage::BitVector selfLoopStates(matrix.getRowCount());
for (size_t i = 0; i < decomposition.size(); ++i) {
auto scc = decomposition.getBlock(i);
if (scc.size() > 1) {
auto statesScc = scc.getStates();
std::vector<uint_fast64_t> entryStates;
for (auto state : statesScc) {
auto row = backwardsTransitionMatrix.getRow(state);
bool found = false;
for (auto backState : row) {
if (!scc.containsState(backState.getColumn())) {
found = true;
}
}
if (found) {
entryStates.push_back(state);
selfLoopStates.set(state);
} else {
selectedStates.set(state);
}
}
if (entryStates.size() != 1) {
STORM_LOG_THROW(entryStates.size() > 1, storm::exceptions::NotImplementedException,
"state elimination not implemented for scc with more than 1 entry points");
}
}
}
storm::storage::FlexibleSparseMatrix<ValueType> flexibleMatrix(matrix);
storm::storage::FlexibleSparseMatrix<ValueType> flexibleBackwardTransitions(backwardsTransitionMatrix, true);
auto actionRewards = std::vector<ValueType>(matrix.getRowCount(), storm::utility::zero<ValueType>());
storm::solver::stateelimination::NondeterministicModelStateEliminator<ValueType> stateEliminator(flexibleMatrix, flexibleBackwardTransitions, actionRewards);
for(auto state : selectedStates) {
stateEliminator.eliminateState(state, true);
}
for (auto state : selfLoopStates) {
auto row = flexibleMatrix.getRow(state);
stateEliminator.eliminateLoop(state);
}
selectedStates.complement();
auto keptRows = matrix.getRowFilter(selectedStates);
storm::storage::SparseMatrix<ValueType> newTransitionMatrix = flexibleMatrix.createSparseMatrix(keptRows, selectedStates);
// TODO @Jip: note that rewards get lost
result = std::make_shared<storm::models::sparse::Dtmc<ValueType>>(std::move(newTransitionMatrix), sparseModel->getStateLabeling().getSubLabeling(selectedStates));
eliminationWatch.stop();
STORM_PRINT(std::endl << "Time for scc elimination: " << eliminationWatch << "." << std::endl << std::endl);
result->printModelInformationToStream(std::cout);
} else if (model->isOfType(storm::models::ModelType::Mdp)) {
STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Unable to perform SCC elimination for monotonicity analysis on MDP: Not mplemented");
} else {
STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Unable to perform monotonicity analysis on the provided model type.");
}
return result;
}
template <typename ValueType>
std::shared_ptr<storm::models::ModelBase> simplifyModel(std::shared_ptr<storm::models::ModelBase> const& model, SymbolicInput const& input) {
storm::utility::Stopwatch simplifyingWatch(true);
std::shared_ptr<storm::models::ModelBase> result;
if (model->isOfType(storm::models::ModelType::Dtmc)) {
storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<ValueType>> simplifier(*(model->template as<storm::models::sparse::Dtmc<ValueType>>()));
std::vector<std::shared_ptr<storm::logic::Formula const>> formulas = storm::api::extractFormulasFromProperties(input.properties);
STORM_LOG_THROW(formulas.begin()!=formulas.end(), storm::exceptions::NotSupportedException, "Only one formula at the time supported");
if (!simplifier.simplify(*(formulas[0]))) {
STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Simplifying the model was not successfull.");
}
result = simplifier.getSimplifiedModel();
} else if (model->isOfType(storm::models::ModelType::Mdp)) {
storm::transformer::SparseParametricMdpSimplifier<storm::models::sparse::Mdp<ValueType>> simplifier(*(model->template as<storm::models::sparse::Mdp<ValueType>>()));
std::vector<std::shared_ptr<storm::logic::Formula const>> formulas = storm::api::extractFormulasFromProperties(input.properties);
STORM_LOG_THROW(formulas.begin()!=formulas.end(), storm::exceptions::NotSupportedException, "Only one formula at the time supported");
if (!simplifier.simplify(*(formulas[0]))) {
STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Simplifying the model was not successfull.");
}
result = simplifier.getSimplifiedModel();
} else {
STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Unable to perform monotonicity analysis on the provided model type.");
}
simplifyingWatch.stop();
STORM_PRINT(std::endl << "Time for model simplification: " << simplifyingWatch << "." << std::endl << std::endl);
result->printModelInformationToStream(std::cout);
return result;
}
template <typename ValueType>
PreprocessResult preprocessSparseModel(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, SymbolicInput const& input, storm::cli::ModelProcessingInformation const& mpi) {
auto bisimulationSettings = storm::settings::getModule<storm::settings::modules::BisimulationSettings>();
auto parametricSettings = storm::settings::getModule<storm::settings::modules::ParametricSettings>();
auto transformationSettings = storm::settings::getModule<storm::settings::modules::TransformationSettings>();
auto monSettings = storm::settings::getModule<storm::settings::modules::MonotonicitySettings>();
PreprocessResult result(model, false);
if (monSettings.isMonotonicityAnalysisSet() || parametricSettings.isUseMonotonicitySet()) {
result.model = storm::pars::simplifyModel<ValueType>(result.model, input);
result.changed = true;
}
if (result.model->isOfType(storm::models::ModelType::MarkovAutomaton)) {
result.model = storm::cli::preprocessSparseMarkovAutomaton(result.model->template as<storm::models::sparse::MarkovAutomaton<ValueType>>());
@ -195,6 +311,11 @@ namespace storm {
result.changed = true;
}
if (monSettings.isSccEliminationSet()) {
result.model = storm::pars::eliminateScc<ValueType>(result.model);
result.changed = true;
}
return result;
}
@ -424,36 +545,133 @@ namespace storm {
}
template <typename ValueType>
void computeRegionExtremumWithSparseEngine(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, SymbolicInput const& input, std::vector<storm::storage::ParameterRegion<ValueType>> const& regions) {
void analyzeMonotonicity(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, SymbolicInput const& input, std::vector<storm::storage::ParameterRegion<ValueType>> const& regions) {
std::ofstream outfile;
auto monSettings = storm::settings::getModule<storm::settings::modules::MonotonicitySettings>();
if (monSettings.isExportMonotonicitySet()) {
utility::openFile(monSettings.getExportMonotonicityFilename(), outfile);
}
std::vector<std::shared_ptr<storm::logic::Formula const>> formulas = storm::api::extractFormulasFromProperties(input.properties);
storm::utility::Stopwatch monotonicityWatch(true);
STORM_LOG_THROW(regions.size() <= 1, storm::exceptions::InvalidArgumentException, "Monotonicity analysis only allowed on single region");
if (!monSettings.isMonSolutionSet()) {
auto monotonicityHelper = storm::analysis::MonotonicityHelper<ValueType, double>(model, formulas, regions, monSettings.getNumberOfSamples(), storm::settings::getModule<storm::settings::modules::GeneralSettings>().getPrecision(), monSettings.isDotOutputSet());
if (monSettings.isExportMonotonicitySet()) {
monotonicityHelper.checkMonotonicityInBuild(outfile, monSettings.isUsePLABoundsSet(), monSettings.getDotOutputFilename());
} else {
monotonicityHelper.checkMonotonicityInBuild(std::cout, monSettings.isUsePLABoundsSet(), monSettings.getDotOutputFilename());
}
} else {
// Checking monotonicity based on solution function
auto parametricSettings = storm::settings::getModule<storm::settings::modules::ParametricSettings>();
auto regionSettings = storm::settings::getModule<storm::settings::modules::RegionSettings>();
auto engine = regionSettings.getRegionCheckEngine();
std::function<std::unique_ptr<storm::modelchecker::CheckResult>(std::shared_ptr<storm::logic::Formula const> const& formula)> verificationCallback;
std::function<void(std::unique_ptr<storm::modelchecker::CheckResult> const&)> postprocessingCallback;
// Check the given set of regions with or without refinement
verificationCallback = [&] (std::shared_ptr<storm::logic::Formula const> const& formula) {
std::unique_ptr<storm::modelchecker::CheckResult> result = storm::api::verifyWithSparseEngine<ValueType>(model, storm::api::createTask<ValueType>(formula, true));
return result;
};
for (auto & property : input.properties) {
auto result = verificationCallback(property.getRawFormula())->asExplicitQuantitativeCheckResult<ValueType>().getValueVector();
ValueType valuation;
auto states= model->getInitialStates();
for (auto state : states) {
valuation += result[state];
}
storm::analysis::MonotonicityResult<storm::RationalFunctionVariable> monRes;
for (auto & var : storm::models::sparse::getProbabilityParameters(*model)) {
auto res = storm::analysis::MonotonicityChecker<ValueType>::checkDerivative(valuation.derivative(var), regions[0]);
if (res.first && res.second) {
monRes.addMonotonicityResult(var, analysis::MonotonicityResult<storm::RationalFunctionVariable>::Monotonicity::Constant);
} else if (res.first) {
monRes.addMonotonicityResult(var, analysis::MonotonicityResult<storm::RationalFunctionVariable>::Monotonicity::Incr);
} else if (res.second) {
monRes.addMonotonicityResult(var, analysis::MonotonicityResult<storm::RationalFunctionVariable>::Monotonicity::Decr);
} else {
monRes.addMonotonicityResult(var, analysis::MonotonicityResult<storm::RationalFunctionVariable>::Monotonicity::Not);
}
}
if (monSettings.isExportMonotonicitySet()) {
outfile << monRes.toString();
} else {
STORM_PRINT(monRes.toString());
}
}
}
if (monSettings.isExportMonotonicitySet()) {
utility::closeFile(outfile);
}
monotonicityWatch.stop();
STORM_PRINT(std::endl << "Total time for monotonicity checking: " << monotonicityWatch << "." << std::endl << std::endl);
return;
}
template <typename ValueType>
void computeRegionExtremumWithSparseEngine(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, SymbolicInput const& input, std::vector<storm::storage::ParameterRegion<ValueType>> const& regions, storm::api::MonotonicitySetting monotonicitySettings = storm::api::MonotonicitySetting(), boost::optional<std::pair<std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>, std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>>>& monotoneParameters = boost::none) {
STORM_LOG_ASSERT(!regions.empty(), "Can not analyze an empty set of regions.");
auto regionSettings = storm::settings::getModule<storm::settings::modules::RegionSettings>();
auto monSettings = storm::settings::getModule<storm::settings::modules::MonotonicitySettings>();
auto engine = regionSettings.getRegionCheckEngine();
storm::solver::OptimizationDirection direction = regionSettings.getExtremumDirection();
ValueType precision = storm::utility::convertNumber<ValueType>(regionSettings.getExtremumValuePrecision());
bool generateSplitEstimates = regionSettings.isSplittingThresholdSet();
for (auto const& property : input.properties) {
for (auto const& region : regions) {
STORM_PRINT_AND_LOG("Computing extremal value for property " << property.getName() << ": " << *property.getRawFormula() << " within region " << region << "..." << std::endl);
if (monotonicitySettings.useMonotonicity) {
STORM_PRINT_AND_LOG("Computing extremal value for property " << property.getName() << ": "
<< *property.getRawFormula()
<< " within region " << region
<< " and using monotonicity ..." << std::endl);
} else {
STORM_PRINT_AND_LOG("Computing extremal value for property " << property.getName() << ": "
<< *property.getRawFormula()
<< " within region " << region
<< "..." << std::endl);
}
storm::utility::Stopwatch watch(true);
auto valueValuation = storm::api::computeExtremalValue<ValueType>(model, storm::api::createTask<ValueType>(property.getRawFormula(), true), region, engine, direction, precision);
watch.stop();
std::stringstream valuationStr;
bool first = true;
for (auto const& v : valueValuation.second) {
if (first) {
first = false;
// TODO: hier eventueel checkExtremalValue van maken
if (regionSettings.isExtremumSuggestionSet()) {
ValueType suggestion = storm::utility::convertNumber<ValueType>(regionSettings.getExtremumSuggestion());
if (storm::api::checkExtremalValue<ValueType>(model, storm::api::createTask<ValueType>(property.getRawFormula(), true), region, engine, direction, precision, suggestion, monotonicitySettings, generateSplitEstimates, monotoneParameters)) {
STORM_PRINT_AND_LOG(suggestion << " is the extremum ");
} else {
valuationStr << ", ";
STORM_PRINT_AND_LOG(suggestion << " is NOT the extremum ");
}
valuationStr << v.first << "=" << v.second;
} else {
auto valueValuation = storm::api::computeExtremalValue<ValueType>(model, storm::api::createTask<ValueType>(property.getRawFormula(), true), region, engine, direction, precision, monotonicitySettings, generateSplitEstimates, monotoneParameters);
watch.stop();
std::stringstream valuationStr;
bool first = true;
for (auto const& v : valueValuation.second) {
if (first) {
first = false;
} else {
valuationStr << ", ";
}
valuationStr << v.first << "=" << v.second;
}
STORM_PRINT_AND_LOG("Result at initial state: " << valueValuation.first << " ( approx. " << storm::utility::convertNumber<double>(valueValuation.first) << ") at [" << valuationStr.str() << "]." << std::endl)
STORM_PRINT_AND_LOG("Time for model checking: " << watch << "." << std::endl);
}
STORM_PRINT_AND_LOG("Result at initial state: " << valueValuation.first << " ( approx. " << storm::utility::convertNumber<double>(valueValuation.first) << ") at [" << valuationStr.str() << "]." << std::endl)
STORM_PRINT_AND_LOG("Time for model checking: " << watch << "." << std::endl);
}
}
}
template <typename ValueType>
void verifyRegionsWithSparseEngine(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, SymbolicInput const& input, std::vector<storm::storage::ParameterRegion<ValueType>> const& regions) {
void verifyRegionsWithSparseEngine(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, SymbolicInput const& input, std::vector<storm::storage::ParameterRegion<ValueType>> const& regions, storm::api::MonotonicitySetting monotonicitySettings = storm::api::MonotonicitySetting(), uint64_t monThresh = 0) {
STORM_LOG_ASSERT(!regions.empty(), "Can not analyze an empty set of regions.");
auto parametricSettings = storm::settings::getModule<storm::settings::modules::ParametricSettings>();
@ -475,6 +693,9 @@ namespace storm {
}
auto engine = regionSettings.getRegionCheckEngine();
STORM_PRINT_AND_LOG(" using " << engine);
if (monotonicitySettings.useMonotonicity) {
STORM_PRINT_AND_LOG(" with local monotonicity and");
}
// Check the given set of regions with or without refinement
if (regionSettings.isRefineSet()) {
@ -486,7 +707,8 @@ namespace storm {
if (regionSettings.isDepthLimitSet()) {
optionalDepthLimit = regionSettings.getDepthLimit();
}
std::unique_ptr<storm::modelchecker::RegionRefinementCheckResult<ValueType>> result = storm::api::checkAndRefineRegionWithSparseEngine<ValueType>(model, storm::api::createTask<ValueType>(formula, true), regions.front(), engine, refinementThreshold, optionalDepthLimit, regionSettings.getHypothesis());
// TODO @Jip: change allow model simplification when not using monotonicity, for benchmarking purposes simplification is moved forward.
std::unique_ptr<storm::modelchecker::RegionRefinementCheckResult<ValueType>> result = storm::api::checkAndRefineRegionWithSparseEngine<ValueType>(model, storm::api::createTask<ValueType>(formula, true), regions.front(), engine, refinementThreshold, optionalDepthLimit, regionSettings.getHypothesis(), false, monotonicitySettings, monThresh);
return result;
};
} else {
@ -507,15 +729,21 @@ namespace storm {
}
template <typename ValueType>
void verifyWithSparseEngine(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, SymbolicInput const& input, std::vector<storm::storage::ParameterRegion<ValueType>> const& regions, SampleInformation<ValueType> const& samples) {
void verifyWithSparseEngine(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, SymbolicInput const& input, std::vector<storm::storage::ParameterRegion<ValueType>> const& regions, SampleInformation<ValueType> const& samples, storm::api::MonotonicitySetting monotonicitySettings = storm::api::MonotonicitySetting(), boost::optional<std::pair<std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>, std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>>>& monotoneParameters = boost::none, uint64_t monThresh = 0) {
if (regions.empty()) {
storm::pars::verifyPropertiesWithSparseEngine(model, input, samples);
} else {
auto regionSettings = storm::settings::getModule<storm::settings::modules::RegionSettings>();
if (regionSettings.isExtremumSet()) {
storm::pars::computeRegionExtremumWithSparseEngine(model, input, regions);
auto monSettings = storm::settings::getModule<storm::settings::modules::MonotonicitySettings>();
if (monSettings.isMonotonicityAnalysisSet()) {
storm::pars::analyzeMonotonicity(model, input, regions);
} else if (regionSettings.isExtremumSet()) {
storm::pars::computeRegionExtremumWithSparseEngine(model, input, regions, monotonicitySettings, monotoneParameters);
} else {
storm::pars::verifyRegionsWithSparseEngine(model, input, regions);
assert (monotoneParameters == boost::none);
assert (!monotonicitySettings.useOnlyGlobalMonotonicity);
assert (!monotonicitySettings.useBoundsFromPLA);
storm::pars::verifyRegionsWithSparseEngine(model, input, regions, monotonicitySettings, monThresh);
}
}
}
@ -554,10 +782,12 @@ namespace storm {
}
template <storm::dd::DdType DdType, typename ValueType>
void verifyParametricModel(std::shared_ptr<storm::models::ModelBase> const& model, SymbolicInput const& input, std::vector<storm::storage::ParameterRegion<ValueType>> const& regions, SampleInformation<ValueType> const& samples) {
void verifyParametricModel(std::shared_ptr<storm::models::ModelBase> const& model, SymbolicInput const& input, std::vector<storm::storage::ParameterRegion<ValueType>> const& regions, SampleInformation<ValueType> const& samples, storm::api::MonotonicitySetting monotonicitySettings = storm::api::MonotonicitySetting(), boost::optional<std::pair<std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>, std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>>>& monotoneParameters = boost::none, uint64_t monThresh = 0) {
if (model->isSparseModel()) {
storm::pars::verifyWithSparseEngine<ValueType>(model->as<storm::models::sparse::Model<ValueType>>(), input, regions, samples);
storm::pars::verifyWithSparseEngine<ValueType>(model->as<storm::models::sparse::Model<ValueType>>(), input, regions, samples, monotonicitySettings, monotoneParameters, monThresh);
} else {
assert (!monotonicitySettings.useMonotonicity);
assert (monotoneParameters == boost::none);
storm::pars::verifyWithDdEngine<DdType, ValueType>(model->as<storm::models::symbolic::Model<DdType, ValueType>>(), input, regions, samples);
}
}
@ -582,40 +812,6 @@ namespace storm {
STORM_LOG_THROW(model || input.properties.empty(), storm::exceptions::InvalidSettingsException, "No input model.");
if (monSettings.isMonotonicityAnalysisSet()) {
// Simplify the model
storm::utility::Stopwatch simplifyingWatch(true);
if (model->isOfType(storm::models::ModelType::Dtmc)) {
auto consideredModel = (model->as<storm::models::sparse::Dtmc<ValueType>>());
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<ValueType>>(*consideredModel);
std::vector<std::shared_ptr<storm::logic::Formula const>> formulas = storm::api::extractFormulasFromProperties(input.properties);
STORM_LOG_THROW(formulas.begin()!=formulas.end(), storm::exceptions::NotSupportedException, "Only one formula at the time supported");
if (!simplifier.simplify(*(formulas[0]))) {
STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Simplifying the model was not successfull.");
}
model = simplifier.getSimplifiedModel();
} else if (model->isOfType(storm::models::ModelType::Mdp)) {
auto consideredModel = (model->as<storm::models::sparse::Mdp<ValueType>>());
auto simplifier = storm::transformer::SparseParametricMdpSimplifier<storm::models::sparse::Mdp<ValueType>>(*consideredModel);
std::vector<std::shared_ptr<storm::logic::Formula const>> formulas = storm::api::extractFormulasFromProperties(input.properties);
STORM_LOG_THROW(formulas.begin()!=formulas.end(), storm::exceptions::NotSupportedException, "Only one formula at the time supported");
if (!simplifier.simplify(*(formulas[0]))) {
STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Simplifying the model was not successfull.");
}
model = simplifier.getSimplifiedModel();
} else {
STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Unable to perform monotonicity analysis on the provided model type.");
}
simplifyingWatch.stop();
STORM_PRINT(std::endl << "Time for model simplification: " << simplifyingWatch << "." << std::endl << std::endl);
model->printModelInformationToStream(std::cout);
}
if (model) {
auto preprocessingResult = storm::pars::preprocessModel<DdType, ValueType>(model, input, mpi);
if (preprocessingResult.changed) {
@ -636,103 +832,13 @@ namespace storm {
}
}
if (model && monSettings.isSccEliminationSet()) {
storm::utility::Stopwatch eliminationWatch(true);
if (model->isOfType(storm::models::ModelType::Dtmc)) {
STORM_PRINT("Applying scc elimination" << std::endl);
auto sparseModel = model->as<storm::models::sparse::Model<ValueType>>();
auto matrix = sparseModel->getTransitionMatrix();
auto backwardsTransitionMatrix = matrix.transpose();
storm::storage::StronglyConnectedComponentDecompositionOptions const options;
auto decomposition = storm::storage::StronglyConnectedComponentDecomposition<ValueType>(matrix, options);
storm::storage::BitVector selectedStates(matrix.getRowCount());
storm::storage::BitVector selfLoopStates(matrix.getRowCount());
for (size_t i = 0; i < decomposition.size(); ++i) {
auto scc = decomposition.getBlock(i);
if (scc.size() > 1) {
auto statesScc = scc.getStates();
std::vector<uint_fast64_t> entryStates;
for (auto state : statesScc) {
auto row = backwardsTransitionMatrix.getRow(state);
bool found = false;
for (auto backState : row) {
if (!scc.containsState(backState.getColumn())) {
found = true;
}
}
if (found) {
entryStates.push_back(state);
selfLoopStates.set(state);
} else {
selectedStates.set(state);
}
}
if (entryStates.size() != 1) {
STORM_LOG_THROW(entryStates.size() > 1, storm::exceptions::NotImplementedException,
"state elimination not implemented for scc with more than 1 entry points");
}
}
}
storm::storage::FlexibleSparseMatrix<ValueType> flexibleMatrix(matrix);
storm::storage::FlexibleSparseMatrix<ValueType> flexibleBackwardTransitions(backwardsTransitionMatrix, true);
auto actionRewards = std::vector<ValueType>(matrix.getRowCount(), storm::utility::zero<ValueType>());
storm::solver::stateelimination::NondeterministicModelStateEliminator<ValueType> stateEliminator(flexibleMatrix, flexibleBackwardTransitions, actionRewards);
for(auto state : selectedStates) {
stateEliminator.eliminateState(state, true);
}
for (auto state : selfLoopStates) {
auto row = flexibleMatrix.getRow(state);
stateEliminator.eliminateLoop(state);
}
selectedStates.complement();
auto keptRows = matrix.getRowFilter(selectedStates);
storm::storage::SparseMatrix<ValueType> newTransitionMatrix = flexibleMatrix.createSparseMatrix(keptRows, selectedStates);
// TODO: note that rewards get lost
model = std::make_shared<storm::models::sparse::Dtmc<ValueType>>(std::move(newTransitionMatrix), sparseModel->getStateLabeling().getSubLabeling(selectedStates));
eliminationWatch.stop();
STORM_PRINT(std::endl << "Time for scc elimination: " << eliminationWatch << "." << std::endl << std::endl);
model->printModelInformationToStream(std::cout);
} else if (model->isOfType(storm::models::ModelType::Mdp)) {
STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Unable to perform SCC elimination for monotonicity analysis on MDP: Not mplemented");
} else {
STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Unable to perform monotonicity analysis on the provided model type.");
}
}
std::vector<storm::storage::ParameterRegion<ValueType>> regions = parseRegions<ValueType>(model);
if (monSettings.isMonotonicityAnalysisSet()) {
std::vector<std::shared_ptr<storm::logic::Formula const>> formulas = storm::api::extractFormulasFromProperties(input.properties);
// Monotonicity
storm::utility::Stopwatch monotonicityWatch(true);
STORM_LOG_THROW(regions.size() <= 1, storm::exceptions::InvalidArgumentException, "Monotonicity analysis only allowed on single region");
storm::analysis::MonotonicityChecker<ValueType> monotonicityChecker = storm::analysis::MonotonicityChecker<ValueType>(model, formulas, regions, monSettings.isValidateAssumptionsSet(), monSettings.getNumberOfSamples(), monSettings.getMonotonicityAnalysisPrecision());
if (ioSettings.isExportMonotonicitySet()) {
std::ofstream outfile;
utility::openFile(ioSettings.getExportMonotonicityFilename(), outfile);
monotonicityChecker.checkMonotonicity(outfile);
utility::closeFile(outfile);
} else {
monotonicityChecker.checkMonotonicity(std::cout);
}
monotonicityWatch.stop();
STORM_PRINT(std::endl << "Total time for monotonicity checking: " << monotonicityWatch << "." << std::endl
<< std::endl);
return;
}
std::string samplesAsString = parSettings.getSamples();
SampleInformation<ValueType> samples;
if (!samplesAsString.empty()) {
samples = parseSamples<ValueType>(model, samplesAsString, parSettings.isSamplesAreGraphPreservingSet());
samples = parseSamples<ValueType>(model, samplesAsString,
parSettings.isSamplesAreGraphPreservingSet());
samples.exact = parSettings.isSampleExactSet();
}
@ -741,13 +847,24 @@ namespace storm {
}
if (parSettings.onlyObtainConstraints()) {
STORM_LOG_THROW(parSettings.exportResultToFile(), storm::exceptions::InvalidSettingsException, "When computing constraints, export path has to be specified.");
storm::api::exportParametricResultToFile<ValueType>(boost::none, storm::analysis::ConstraintCollector<ValueType>(*(model->as<storm::models::sparse::Model<ValueType>>())), parSettings.exportResultPath());
STORM_LOG_THROW(parSettings.exportResultToFile(), storm::exceptions::InvalidSettingsException,
"When computing constraints, export path has to be specified.");
storm::api::exportParametricResultToFile<ValueType>(boost::none,
storm::analysis::ConstraintCollector<ValueType>(
*(model->as<storm::models::sparse::Model<ValueType>>())),
parSettings.exportResultPath());
return;
}
if (model) {
verifyParametricModel<DdType, ValueType>(model, input, regions, samples);
boost::optional<std::pair<std::set<storm::RationalFunctionVariable>, std::set<storm::RationalFunctionVariable>>> monotoneParameters;
if (monSettings.isMonotoneParametersSet()) {
monotoneParameters = std::move(
storm::api::parseMonotoneParameters<ValueType>(monSettings.getMonotoneParameterFilename(),
model->as<storm::models::sparse::Model<ValueType>>()));
}
// TODO: is onlyGlobalSet was used here
verifyParametricModel<DdType, ValueType>(model, input, regions, samples, storm::api::MonotonicitySetting(parSettings.isUseMonotonicitySet(), false, monSettings.isUsePLABoundsSet()), monotoneParameters, monSettings.getMonotonicityThreshold());
}
}

389
src/storm-pars/analysis/AssumptionChecker.cpp

@ -2,33 +2,30 @@
#include "AssumptionChecker.h"
#include "storm-pars/utility/ModelInstantiator.h"
#include "storm-pars/analysis/MonotonicityChecker.h"
#include "storm/environment/Environment.h"
#include "storm/exceptions/NotSupportedException.h"
#include "storm/modelchecker/CheckTask.h"
#include "storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h"
#include "storm/modelchecker/prctl/SparseMdpPrctlModelChecker.h"
#include "storm/modelchecker/results/CheckResult.h"
#include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h"
#include "storm/storage/expressions/SimpleValuation.h"
#include "storm/storage/expressions/ExpressionManager.h"
#include "storm/storage/expressions/VariableExpression.h"
#include "storm/storage/expressions/RationalFunctionToExpression.h"
#include "storm/utility/constants.h"
namespace storm {
namespace analysis {
template <typename ValueType>
AssumptionChecker<ValueType>::AssumptionChecker(std::shared_ptr<logic::Formula const> formula, std::shared_ptr<models::sparse::Dtmc<ValueType>> model, storm::storage::ParameterRegion<ValueType> region, uint_fast64_t numberOfSamples) {
this->formula = formula;
this->matrix = model->getTransitionMatrix();
this->region = region;
template <typename ValueType, typename ConstantType>
AssumptionChecker<ValueType, ConstantType>::AssumptionChecker(storage::SparseMatrix<ValueType> matrix){
this->matrix = matrix;
useSamples = false;
}
template <typename ValueType, typename ConstantType>
void AssumptionChecker<ValueType, ConstantType>::initializeCheckingOnSamples(std::shared_ptr<logic::Formula const> formula, std::shared_ptr<models::sparse::Dtmc<ValueType>> model, storage::ParameterRegion<ValueType> region, uint_fast64_t numberOfSamples) {
// Create sample points
auto instantiator = utility::ModelInstantiator<models::sparse::Dtmc<ValueType>, models::sparse::Dtmc<double>>(*model);
auto instantiator = utility::ModelInstantiator<models::sparse::Dtmc<ValueType>, models::sparse::Dtmc<ConstantType>>(*model);
auto matrix = model->getTransitionMatrix();
std::set<typename utility::parametric::VariableType<ValueType>::type> variables = models::sparse::getProbabilityParameters(*model);
std::set<VariableType> variables = models::sparse::getProbabilityParameters(*model);
for (uint_fast64_t i = 0; i < numberOfSamples; ++i) {
auto valuation = utility::parametric::Valuation<ValueType>();
@ -36,106 +33,77 @@ namespace storm {
auto lb = region.getLowerBoundary(var.name());
auto ub = region.getUpperBoundary(var.name());
// Creates samples between lb and ub, that is: lb, lb + (ub-lb)/(#samples -1), lb + 2* (ub-lb)/(#samples -1), ..., ub
auto val =
std::pair<typename utility::parametric::VariableType<ValueType>::type, typename utility::parametric::CoefficientType<ValueType>::type>
(var,utility::convertNumber<typename utility::parametric::CoefficientType<ValueType>::type>(lb + i*(ub-lb)/(numberOfSamples-1)));
auto val = std::pair<VariableType, CoefficientType>(var, utility::convertNumber<CoefficientType>(lb + i * (ub - lb) / (numberOfSamples - 1)));
valuation.insert(val);
}
models::sparse::Dtmc<double> sampleModel = instantiator.instantiate(valuation);
auto checker = modelchecker::SparseDtmcPrctlModelChecker<models::sparse::Dtmc<double>>(sampleModel);
models::sparse::Dtmc<ConstantType> sampleModel = instantiator.instantiate(valuation);
auto checker = modelchecker::SparseDtmcPrctlModelChecker<models::sparse::Dtmc<ConstantType>>(sampleModel);
std::unique_ptr<modelchecker::CheckResult> checkResult;
if (formula->isProbabilityOperatorFormula() &&
formula->asProbabilityOperatorFormula().getSubformula().isUntilFormula()) {
const modelchecker::CheckTask<logic::UntilFormula, double> checkTask = modelchecker::CheckTask<logic::UntilFormula, double>(
const modelchecker::CheckTask<logic::UntilFormula, ConstantType> checkTask = modelchecker::CheckTask<logic::UntilFormula, ConstantType>(
(*formula).asProbabilityOperatorFormula().getSubformula().asUntilFormula());
checkResult = checker.computeUntilProbabilities(Environment(), checkTask);
} else if (formula->isProbabilityOperatorFormula() &&
formula->asProbabilityOperatorFormula().getSubformula().isEventuallyFormula()) {
const modelchecker::CheckTask<logic::EventuallyFormula, double> checkTask = modelchecker::CheckTask<logic::EventuallyFormula, double>(
const modelchecker::CheckTask<logic::EventuallyFormula, ConstantType> checkTask = modelchecker::CheckTask<logic::EventuallyFormula, ConstantType>(
(*formula).asProbabilityOperatorFormula().getSubformula().asEventuallyFormula());
checkResult = checker.computeReachabilityProbabilities(Environment(), checkTask);
} else {
STORM_LOG_THROW(false, exceptions::NotSupportedException,
"Expecting until or eventually formula");
}
auto quantitativeResult = checkResult->asExplicitQuantitativeCheckResult<double>();
std::vector<double> values = quantitativeResult.getValueVector();
auto quantitativeResult = checkResult->asExplicitQuantitativeCheckResult<ConstantType>();
std::vector<ConstantType> values = quantitativeResult.getValueVector();
samples.push_back(values);
}
useSamples = true;
}
template <typename ValueType>
AssumptionChecker<ValueType>::AssumptionChecker(std::shared_ptr<logic::Formula const> formula, std::shared_ptr<models::sparse::Mdp<ValueType>> model, uint_fast64_t numberOfSamples) {
STORM_LOG_THROW(false, exceptions::NotImplementedException, "Assumption checking for mdps not yet implemented");
this->formula = formula;
this->matrix = model->getTransitionMatrix();
// Create sample points
auto instantiator = utility::ModelInstantiator<models::sparse::Mdp<ValueType>, models::sparse::Mdp<double>>(*model);
auto matrix = model->getTransitionMatrix();
std::set<typename utility::parametric::VariableType<ValueType>::type> variables = models::sparse::getProbabilityParameters(*model);
for (auto i = 0; i < numberOfSamples; ++i) {
auto valuation = utility::parametric::Valuation<ValueType>();
for (auto itr = variables.begin(); itr != variables.end(); ++itr) {
auto val = std::pair<typename utility::parametric::VariableType<ValueType>::type,
typename utility::parametric::CoefficientType<ValueType>::type>((*itr), utility::convertNumber<typename utility::parametric::CoefficientType<ValueType>::type>(boost::lexical_cast<std::string>((i+1)/(double (numberOfSamples + 1)))));
valuation.insert(val);
}
models::sparse::Mdp<double> sampleModel = instantiator.instantiate(valuation);
auto checker = modelchecker::SparseMdpPrctlModelChecker<models::sparse::Mdp<double>>(sampleModel);
std::unique_ptr<modelchecker::CheckResult> checkResult;
if (formula->isProbabilityOperatorFormula() &&
formula->asProbabilityOperatorFormula().getSubformula().isUntilFormula()) {
const modelchecker::CheckTask<logic::UntilFormula, double> checkTask = modelchecker::CheckTask<logic::UntilFormula, double>(
(*formula).asProbabilityOperatorFormula().getSubformula().asUntilFormula());
checkResult = checker.computeUntilProbabilities(Environment(), checkTask);
} else if (formula->isProbabilityOperatorFormula() &&
formula->asProbabilityOperatorFormula().getSubformula().isEventuallyFormula()) {
const modelchecker::CheckTask<logic::EventuallyFormula, double> checkTask = modelchecker::CheckTask<logic::EventuallyFormula, double>(
(*formula).asProbabilityOperatorFormula().getSubformula().asEventuallyFormula());
checkResult = checker.computeReachabilityProbabilities(Environment(), checkTask);
} else {
STORM_LOG_THROW(false, exceptions::NotSupportedException,
"Expecting until or eventually formula");
}
auto quantitativeResult = checkResult->asExplicitQuantitativeCheckResult<double>();
std::vector<double> values = quantitativeResult.getValueVector();
samples.push_back(values);
}
template <typename ValueType, typename ConstantType>
void AssumptionChecker<ValueType, ConstantType>::setSampleValues(std::vector<std::vector<ConstantType>> samples) {
this->samples = samples;
useSamples = true;
}
template <typename ValueType>
AssumptionStatus AssumptionChecker<ValueType>::checkOnSamples(std::shared_ptr<expressions::BinaryRelationExpression> assumption) {
auto result = AssumptionStatus::UNKNOWN;
std::set<expressions::Variable> vars = std::set<expressions::Variable>({});
assumption->gatherVariables(vars);
for (auto itr = samples.begin(); result == AssumptionStatus::UNKNOWN && itr != samples.end(); ++itr) {
std::shared_ptr<expressions::ExpressionManager const> manager = assumption->getManager().getSharedPointer();
auto valuation = expressions::SimpleValuation(manager);
auto values = (*itr);
for (auto var = vars.begin(); result == AssumptionStatus::UNKNOWN && var != vars.end(); ++var) {
expressions::Variable par = *var;
auto index = std::stoi(par.getName());
valuation.setRationalValue(par, values[index]);
}
assert(assumption->hasBooleanType());
if (!assumption->evaluateAsBool(&valuation)) {
result = AssumptionStatus::INVALID;
}
}
return result;
template <typename ValueType, typename ConstantType>
AssumptionChecker<ValueType, ConstantType>::AssumptionChecker(std::shared_ptr<logic::Formula const> formula, std::shared_ptr<models::sparse::Mdp<ValueType>> model, uint_fast64_t numberOfSamples) {
STORM_LOG_THROW(false, exceptions::NotSupportedException, "Assumption checking for mdps not yet implemented");
}
template <typename ValueType>
AssumptionStatus AssumptionChecker<ValueType>::validateAssumption(std::shared_ptr<expressions::BinaryRelationExpression> assumption, Order* order) {
template <typename ValueType, typename ConstantType>
AssumptionStatus AssumptionChecker<ValueType, ConstantType>::validateAssumption(uint_fast64_t val1, uint_fast64_t val2,std::shared_ptr<expressions::BinaryRelationExpression> assumption, std::shared_ptr<Order> order, storage::ParameterRegion<ValueType> region, std::vector<ConstantType>const minValues, std::vector<ConstantType>const maxValues) const {
// First check if based on sample points the assumption can be discharged
auto result = checkOnSamples(assumption);
assert (val1 == std::stoi(assumption->getFirstOperand()->asVariableExpression().getVariableName()));
assert (val2 == std::stoi(assumption->getSecondOperand()->asVariableExpression().getVariableName()));
AssumptionStatus result = AssumptionStatus::UNKNOWN;
if (useSamples) {
result = checkOnSamples(assumption);
}
assert (result != AssumptionStatus::VALID);
if (minValues.size() != 0) {
if (assumption->getRelationType() == expressions::BinaryRelationExpression::RelationType::Greater) {
if (minValues[val1] > maxValues[val2]) {
return AssumptionStatus::VALID;
} else if (minValues[val1] == maxValues[val2] && minValues[val1] == maxValues[val1] && minValues[val2] == maxValues[val2]) {
return AssumptionStatus::INVALID;
} else if (minValues[val2] > maxValues[val1]) {
return AssumptionStatus::INVALID;
}
} else {
if (minValues[val1] == maxValues[val2] && minValues[val1] == maxValues[val1] && minValues[val2] == maxValues[val2]) {
return AssumptionStatus::VALID;
} else if (minValues[val1] > maxValues[val2]) {
return AssumptionStatus::INVALID;
} else if (minValues[val2] > maxValues[val1]) {
return AssumptionStatus::INVALID;
}
}
}
if (result == AssumptionStatus::UNKNOWN) {
// If result from sample checking was unknown, the assumption might hold, so we continue,
// otherwise we return INVALID
// If result from sample checking was unknown, the assumption might hold
std::set<expressions::Variable> vars = std::set<expressions::Variable>({});
assumption->gatherVariables(vars);
@ -145,141 +113,111 @@ namespace storm {
expressions::BinaryRelationExpression::RelationType::Equal,
exceptions::NotSupportedException,
"Only Greater Or Equal assumptions supported");
// Row with successors of the first state
auto row1 = matrix.getRow(
std::stoi(assumption->getFirstOperand()->asVariableExpression().getVariableName()));
// Row with successors of the second state
auto row2 = matrix.getRow(
std::stoi(assumption->getSecondOperand()->asVariableExpression().getVariableName()));
if (row1.getNumberOfEntries() == 2 && row2.getNumberOfEntries() == 2) {
// If the states have the same successors for which we know the position in the order
// We can check with a function if the assumption holds
auto state1succ1 = row1.begin();
auto state1succ2 = (++row1.begin());
auto state2succ1 = row2.begin();
auto state2succ2 = (++row2.begin());
if (state1succ1->getColumn() == state2succ2->getColumn()
&& state2succ1->getColumn() == state1succ2->getColumn()) {
std::swap(state1succ1, state1succ2);
}
if (state1succ1->getColumn() == state2succ1->getColumn() && state1succ2->getColumn() == state2succ2->getColumn()) {
if (assumption->getRelationType() == expressions::BinaryRelationExpression::RelationType::Greater
&& order->compare(state1succ1->getColumn(), state1succ2->getColumn()) != Order::NodeComparison::UNKNOWN) {
// The assumption should be the greater assumption
// If the result is unknown, we cannot compare, also SMTSolver will not help
result = validateAssumptionSMTSolver(assumption, order);
// result = validateAssumptionFunction(order, state1succ1, state1succ2, state2succ1,
// state2succ2);
} else if (assumption->getRelationType() == expressions::BinaryRelationExpression::RelationType::Equal) {
// The assumption is equal, the successors are the same,
// so if the probability of reaching the successors is the same, we have a valid assumption
if (state1succ1->getValue() == state2succ1->getValue()) {
result = AssumptionStatus::VALID;
}
} else {
result = AssumptionStatus::UNKNOWN;
}
} else {
result = validateAssumptionSMTSolver(assumption, order);
}
} else {
result = validateAssumptionSMTSolver(assumption, order);
}
result = validateAssumptionSMTSolver(val1, val2, assumption, order, region, minValues, maxValues);
}
return result;
}
template <typename ValueType>
AssumptionStatus AssumptionChecker<ValueType>::validateAssumptionFunction(Order* order,
typename storage::SparseMatrix<ValueType>::iterator state1succ1,
typename storage::SparseMatrix<ValueType>::iterator state1succ2,
typename storage::SparseMatrix<ValueType>::iterator state2succ1,
typename storage::SparseMatrix<ValueType>::iterator state2succ2) {
assert((state1succ1->getColumn() == state2succ1->getColumn()
&& state1succ2->getColumn() == state2succ2->getColumn())
|| (state1succ1->getColumn() == state2succ2->getColumn()
&& state1succ2->getColumn() == state2succ1->getColumn()));
AssumptionStatus result;
// Calculate the difference in probability for the "highest" successor state
ValueType prob;
auto comp = order->compare(state1succ1->getColumn(), state1succ2->getColumn());
assert (comp == Order::NodeComparison::ABOVE || comp == Order::NodeComparison::BELOW);
if (comp == Order::NodeComparison::ABOVE) {
prob = state1succ1->getValue() - state2succ1->getValue();
} else if (comp == Order::NodeComparison::BELOW) {
prob = state1succ2->getValue() - state2succ2->getValue();
}
auto vars = prob.gatherVariables();
// If the result in monotone increasing (decreasing), then choose 0 (1) for the substitutions
// This will give the smallest result
std::map<typename utility::parametric::VariableType<ValueType>::type, typename utility::parametric::CoefficientType<ValueType>::type> substitutions;
for (auto var : vars) {
auto monotonicity = MonotonicityChecker<ValueType>::checkDerivative(prob.derivative(var), region);
if (monotonicity.first) {
// monotone increasing
substitutions[var] = 0;
} else if (monotonicity.second) {
// monotone increasing
substitutions[var] = 1;
} else {
result = AssumptionStatus::UNKNOWN;
template <typename ValueType, typename ConstantType>
AssumptionStatus AssumptionChecker<ValueType, ConstantType>::checkOnSamples(std::shared_ptr<expressions::BinaryRelationExpression> assumption) const {
auto result = AssumptionStatus::UNKNOWN;
std::set<expressions::Variable> vars = std::set<expressions::Variable>({});
assumption->gatherVariables(vars);
for (auto values : samples) {
auto valuation = expressions::SimpleValuation(assumption->getManager().getSharedPointer());
for (auto var : vars) {
auto index = std::stoi(var.getName());
valuation.setRationalValue(var, utility::convertNumber<double>(values[index]));
}
}
if (prob.evaluate(substitutions) >= 0) {
result = AssumptionStatus::VALID;
assert (assumption->hasBooleanType());
if (!assumption->evaluateAsBool(&valuation)) {
result = AssumptionStatus::INVALID;
break;
}
}
return result;
}
template <typename ValueType>
AssumptionStatus AssumptionChecker<ValueType>::validateAssumptionSMTSolver(std::shared_ptr<expressions::BinaryRelationExpression> assumption, Order* order) {
template <typename ValueType, typename ConstantType>
AssumptionStatus AssumptionChecker<ValueType, ConstantType>::validateAssumptionSMTSolver(uint_fast64_t val1, uint_fast64_t val2, std::shared_ptr<expressions::BinaryRelationExpression> assumption, std::shared_ptr<Order> order, storage::ParameterRegion<ValueType> region, std::vector<ConstantType>const minValues, std::vector<ConstantType>const maxValues) const {
std::shared_ptr<utility::solver::SmtSolverFactory> smtSolverFactory = std::make_shared<utility::solver::MathsatSmtSolverFactory>();
std::shared_ptr<expressions::ExpressionManager> manager(new expressions::ExpressionManager());
AssumptionStatus result;
AssumptionStatus result = AssumptionStatus::UNKNOWN;
auto var1 = assumption->getFirstOperand()->asVariableExpression().getVariableName();
auto var2 = assumption->getSecondOperand()->asVariableExpression().getVariableName();
auto row1 = matrix.getRow(std::stoi(var1));
auto row2 = matrix.getRow(std::stoi(var2));
auto row1 = matrix.getRow(val1);
auto row2 = matrix.getRow(val2);
bool orderKnown = true;
// if the state with number var1 (var2) occurs in the successors of the state with number var2 (var1) we need to add var1 == expr1 (var2 == expr2) to the bounds
bool addVar1 = false;
bool addVar2 = false;
// Check if the order between the different successors is known
// Also start creating expression for order of states
expressions::Expression exprOrderSucc = manager->boolean(true);
auto exprOrderSucc = manager->boolean(true);
std::set<expressions::Variable> stateVariables;
std::set<expressions::Variable> topVariables;
std::set<expressions::Variable> bottomVariables;
for (auto itr1 = row1.begin(); orderKnown && itr1 != row1.end(); ++itr1) {
addVar2 |= std::to_string(itr1->getColumn()) == var2;
auto varname1 = "s" + std::to_string(itr1->getColumn());
if (!manager->hasVariable(varname1)) {
stateVariables.insert(manager->declareRationalVariable(varname1));
if (order->isTopState(itr1->getColumn())) {
topVariables.insert(manager->declareRationalVariable(varname1));
} else if (order->isBottomState(itr1->getColumn())) {
bottomVariables.insert(manager->declareRationalVariable(varname1));
} else {
stateVariables.insert(manager->declareRationalVariable(varname1));
}
}
for (auto itr2 = row2.begin(); orderKnown && itr2 != row2.end(); ++itr2) {
addVar1 |= std::to_string(itr2->getColumn()) == var1;
if (itr1->getColumn() != itr2->getColumn()) {
auto varname2 = "s" + std::to_string(itr2->getColumn());
if (!manager->hasVariable(varname2)) {
stateVariables.insert(manager->declareRationalVariable(varname2));
if (order->isTopState(itr2->getColumn())) {
topVariables.insert(manager->declareRationalVariable(varname2));
} else if (order->isBottomState(itr2->getColumn())) {
bottomVariables.insert(manager->declareRationalVariable(varname2));
} else {
stateVariables.insert(manager->declareRationalVariable(varname2));
}
}
auto comp = order->compare(itr1->getColumn(), itr2->getColumn());
if (minValues.size() > 0 && comp == Order::NodeComparison::UNKNOWN) {
// Couldn't add relation between varname1 and varname2 but maybe we can based on min/max values;
if (minValues[itr2->getColumn()] > maxValues[itr1->getColumn()]) {
if (!order->contains(itr1->getColumn())) {
order->add(itr1->getColumn());
order->addStateToHandle(itr1->getColumn());
}
if (!order->contains(itr2->getColumn())) {
order->add(itr2->getColumn());
order->addStateToHandle(itr2->getColumn());
}
order->addRelation(itr2->getColumn(), itr1->getColumn());
comp = Order::NodeComparison::BELOW;
} else if (minValues[itr1->getColumn()] > maxValues[itr2->getColumn()]) {
if (!order->contains(itr1->getColumn())) {
order->add(itr1->getColumn());
order->addStateToHandle(itr1->getColumn());
}
if (!order->contains(itr2->getColumn())) {
order->add(itr2->getColumn());
order->addStateToHandle(itr2->getColumn());
}
order->addRelation(itr1->getColumn(), itr2->getColumn());
comp = Order::NodeComparison::ABOVE;
}
}
if (comp == Order::NodeComparison::ABOVE) {
exprOrderSucc = exprOrderSucc && !(manager->getVariable(varname1) <=
manager->getVariable(varname2));
exprOrderSucc = exprOrderSucc && !(manager->getVariable(varname1) <= manager->getVariable(varname2));
} else if (comp == Order::NodeComparison::BELOW) {
exprOrderSucc = exprOrderSucc && !(manager->getVariable(varname1) >=
manager->getVariable(varname2));
exprOrderSucc = exprOrderSucc && !(manager->getVariable(varname1) >= manager->getVariable(varname2));
} else if (comp == Order::NodeComparison::SAME) {
exprOrderSucc = exprOrderSucc &&
(manager->getVariable(varname1) = manager->getVariable(varname2));
exprOrderSucc = exprOrderSucc && (manager->getVariable(varname1) >= manager->getVariable(varname2)) && (manager->getVariable(varname1) <= manager->getVariable(varname2));
} else {
orderKnown = false;
}
@ -292,63 +230,96 @@ namespace storm {
auto valueTypeToExpression = expressions::RationalFunctionToExpression<ValueType>(manager);
expressions::Expression expr1 = manager->rational(0);
for (auto itr1 = row1.begin(); itr1 != row1.end(); ++itr1) {
expr1 = expr1 + (valueTypeToExpression.toExpression(itr1->getValue()) * manager->getVariable("s" + std::to_string(itr1->getColumn())));
expr1 = expr1 + (valueTypeToExpression.toExpression(itr1->getValue()) *
manager->getVariable("s" + std::to_string(itr1->getColumn())));
}
expressions::Expression expr2 = manager->rational(0);
for (auto itr2 = row2.begin(); itr2 != row2.end(); ++itr2) {
expr2 = expr2 + (valueTypeToExpression.toExpression(itr2->getValue()) * manager->getVariable("s" + std::to_string(itr2->getColumn())));
expr2 = expr2 + (valueTypeToExpression.toExpression(itr2->getValue()) *
manager->getVariable("s" + std::to_string(itr2->getColumn())));
}
// Create expression for the assumption based on the relation to successors
// It is the negation of actual assumption
expressions::Expression exprToCheck ;
if (assumption->getRelationType() ==
expressions::BinaryRelationExpression::RelationType::Greater) {
expressions::Expression exprToCheck;
if (assumption->getRelationType() == expressions::BinaryRelationExpression::RelationType::Greater) {
exprToCheck = expr1 <= expr2;
} else {
assert (assumption->getRelationType() ==
expressions::BinaryRelationExpression::RelationType::Equal);
exprToCheck = expr1 != expr2 ;
assert (assumption->getRelationType() == expressions::BinaryRelationExpression::RelationType::Equal);
exprToCheck = expr1 != expr2;
}
auto variables = manager->getVariables();
// Bounds for the state probabilities and parameters
expressions::Expression exprBounds = manager->boolean(true);
expressions::Expression exprBounds = manager->boolean(true);
if (addVar1) {
exprBounds = exprBounds && (manager->getVariable("s" + var1) == expr1);
}
if (addVar2) {
exprBounds = exprBounds && (manager->getVariable("s" + var2) == expr2);
}
for (auto var : variables) {
if (find(stateVariables.begin(), stateVariables.end(), var) != stateVariables.end()) {
// the var is a state
exprBounds = exprBounds && manager->rational(0) <= var && var <= manager->rational(1);
if (minValues.size() > 0) {
std::string test = var.getName();
auto val = std::stoi(test.substr(1,test.size()-1));
exprBounds = exprBounds && manager->rational(minValues[val]) <= var &&
var <= manager->rational(maxValues[val]);
} else {
exprBounds = exprBounds && manager->rational(0) <= var &&
var <= manager->rational(1);
}
} else if (find(topVariables.begin(), topVariables.end(), var) != topVariables.end()) {
// the var is =)
exprBounds = exprBounds && var == manager->rational(1);
} else if (find(bottomVariables.begin(), bottomVariables.end(), var) != bottomVariables.end()) {
// the var is =(
exprBounds = exprBounds && var == manager->rational(0);
} else {
// the var is a parameter
auto lb = storm::utility::convertNumber<storm::RationalNumber>(region.getLowerBoundary(var.getName()));
auto ub = storm::utility::convertNumber<storm::RationalNumber>(region.getUpperBoundary(var.getName()));
auto lb = utility::convertNumber<RationalNumber>(region.getLowerBoundary(var.getName()));
auto ub = utility::convertNumber<RationalNumber>(region.getUpperBoundary(var.getName()));
exprBounds = exprBounds && manager->rational(lb) < var && var < manager->rational(ub);
}
}
s.add(exprOrderSucc);
s.add(exprBounds);
s.setTimeout(100);
// assert that sorting of successors in the order and the bounds on the expression are at least satisfiable
assert (s.check() == solver::SmtSolver::CheckResult::Sat);
// when this is not the case, the order is invalid
// however, it could be that the sat solver didn't finish in time, in that case we just continue.
if (s.check() == solver::SmtSolver::CheckResult::Unsat) {
return AssumptionStatus::INVALID;
}
assert (s.check() != solver::SmtSolver::CheckResult::Unsat);
s.add(exprToCheck);
auto smtRes = s.check();
if (smtRes == solver::SmtSolver::CheckResult::Unsat) {
// If there is no thing satisfying the negation we are safe.
result = AssumptionStatus::VALID;
} else if (smtRes == solver::SmtSolver::CheckResult::Sat) {
assert (smtRes == solver::SmtSolver::CheckResult::Sat);
result = AssumptionStatus::INVALID;
} else {
result = AssumptionStatus::UNKNOWN;
result = AssumptionStatus::INVALID;
}
} else {
result = AssumptionStatus::UNKNOWN;
}
return result;
}
template class AssumptionChecker<RationalFunction>;
template<typename ValueType, typename ConstantType>
AssumptionStatus AssumptionChecker<ValueType, ConstantType>::validateAssumption(
std::shared_ptr<expressions::BinaryRelationExpression> assumption, std::shared_ptr<Order> order,
storage::ParameterRegion<ValueType> region) const {
auto var1 = std::stoi(assumption->getFirstOperand()->asVariableExpression().getVariableName());
auto var2 = std::stoi(assumption->getSecondOperand()->asVariableExpression().getVariableName());
std::vector<ConstantType> vals;
return validateAssumption(var1, var2, assumption, order, region, vals, vals);
}
template class AssumptionChecker<RationalFunction, double>;
template class AssumptionChecker<RationalFunction, RationalNumber>;
}
}

58
src/storm-pars/analysis/AssumptionChecker.h

@ -8,6 +8,8 @@
#include "storm/storage/expressions/BinaryRelationExpression.h"
#include "storm-pars/storage/ParameterRegion.h"
#include "Order.h"
#include "storm/storage/SparseMatrix.h"
namespace storm {
namespace analysis {
@ -19,18 +21,19 @@ namespace storm {
INVALID,
UNKNOWN,
};
template<typename ValueType>
template<typename ValueType, typename ConstantType>
class AssumptionChecker {
public:
typedef typename utility::parametric::VariableType<ValueType>::type VariableType;
typedef typename utility::parametric::CoefficientType<ValueType>::type CoefficientType;
/*!
* Constructs an AssumptionChecker based on the number of samples, for the given formula and model.
* Constructs an AssumptionChecker.
*
* @param formula The formula to check.
* @param model The dtmc model to check the formula on.
* @param numberOfSamples Number of sample points.
* @param matrix The matrix of the considered model.
*/
AssumptionChecker(std::shared_ptr<logic::Formula const> formula, std::shared_ptr<models::sparse::Dtmc<ValueType>> model, storm::storage::ParameterRegion<ValueType> region, uint_fast64_t numberOfSamples);
AssumptionChecker(storage::SparseMatrix<ValueType> matrix);
/*!
* Constructs an AssumptionChecker based on the number of samples, for the given formula and model.
@ -39,51 +42,46 @@ namespace storm {
* @param model The mdp model to check the formula on.
* @param numberOfSamples Number of sample points.
*/
AssumptionChecker(std::shared_ptr<logic::Formula const> formula, std::shared_ptr<models::sparse::Mdp<ValueType>> model, uint_fast64_t numberOfSamples);
AssumptionChecker(std::shared_ptr<logic::Formula const> formula, std::shared_ptr<models::sparse::Mdp<ValueType>> model, uint_fast64_t const numberOfSamples);
/*!
* Checks if the assumption holds at the sample points of the AssumptionChecker.
* Initializes the given number of sample points for a given model, formula and region.
*
* @param assumption The assumption to check.
* @return AssumptionStatus::UNKNOWN or AssumptionStatus::INVALID
* @param formula The formula to compute the samples for.
* @param model The considered model.
* @param region The region of the model's parameters.
* @param numberOfSamples Number of sample points.
*/
AssumptionStatus checkOnSamples(std::shared_ptr<expressions::BinaryRelationExpression> assumption);
void initializeCheckingOnSamples(std::shared_ptr<logic::Formula const> formula, std::shared_ptr<models::sparse::Dtmc<ValueType>> model, storage::ParameterRegion<ValueType> region, uint_fast64_t numberOfSamples);
/*!
* Tries to validate an assumption based on the order and underlying transition matrix.
* Sets the sample values to the given vector and useSamples to true.
*
* @param assumption The assumption to validate.
* @param order The order.
* @return AssumptionStatus::VALID, or AssumptionStatus::UNKNOWN, or AssumptionStatus::INVALID
* @param samples The new value for samples.
*/
AssumptionStatus validateAssumption(std::shared_ptr<expressions::BinaryRelationExpression> assumption, Order* order);
void setSampleValues(std::vector<std::vector<ConstantType>> samples);
/*!
* Tries to validate an assumption based on the order, and SMT solving techniques
* Tries to validate an assumption based on the order and underlying transition matrix.
*
* @param assumption The assumption to validate.
* @param order The order.
* @param region The region of the considered model.
* @return AssumptionStatus::VALID, or AssumptionStatus::UNKNOWN, or AssumptionStatus::INVALID
*/
AssumptionStatus validateAssumptionSMTSolver(std::shared_ptr<expressions::BinaryRelationExpression> assumption, Order* order);
AssumptionStatus validateAssumption(uint_fast64_t val1, uint_fast64_t val2, std::shared_ptr<expressions::BinaryRelationExpression> assumption, std::shared_ptr<Order> order, storage::ParameterRegion<ValueType> region, std::vector<ConstantType> const minValues, std::vector<ConstantType> const maxValue) const;
AssumptionStatus validateAssumption(std::shared_ptr<expressions::BinaryRelationExpression> assumption, std::shared_ptr<Order> order, storage::ParameterRegion<ValueType> region) const;
private:
std::shared_ptr<logic::Formula const> formula;
bool useSamples;
storage::SparseMatrix<ValueType> matrix;
std::vector<std::vector<ConstantType>> samples;
std::vector<std::vector<double>> samples;
void createSamples();
AssumptionStatus validateAssumptionFunction(Order* order,
typename storage::SparseMatrix<ValueType>::iterator state1succ1,
typename storage::SparseMatrix<ValueType>::iterator state1succ2,
typename storage::SparseMatrix<ValueType>::iterator state2succ1,
typename storage::SparseMatrix<ValueType>::iterator state2succ2);
storage::SparseMatrix<ValueType> matrix;
storm::storage::ParameterRegion<ValueType> region;
AssumptionStatus validateAssumptionSMTSolver(uint_fast64_t val1, uint_fast64_t val2, std::shared_ptr<expressions::BinaryRelationExpression> assumption, std::shared_ptr<Order> order, storage::ParameterRegion<ValueType> region, std::vector<ConstantType>const minValues, std::vector<ConstantType>const maxValue) const;
AssumptionStatus checkOnSamples(std::shared_ptr<expressions::BinaryRelationExpression> assumption) const;
};
}
}

116
src/storm-pars/analysis/AssumptionMaker.cpp

@ -2,66 +2,86 @@
namespace storm {
namespace analysis {
typedef std::shared_ptr<expressions::BinaryRelationExpression> AssumptionType;
template<typename ValueType>
AssumptionMaker<ValueType>::AssumptionMaker(AssumptionChecker<ValueType>* assumptionChecker, uint_fast64_t numberOfStates, bool validate) {
this->numberOfStates = numberOfStates;
this->assumptionChecker = assumptionChecker;
this->validate = validate;
this->expressionManager = std::make_shared<expressions::ExpressionManager>(expressions::ExpressionManager());
template<typename ValueType, typename ConstantType>
AssumptionMaker<ValueType, ConstantType>::AssumptionMaker(storage::SparseMatrix<ValueType> matrix) : assumptionChecker(matrix){
numberOfStates = matrix.getColumnCount();
expressionManager = std::make_shared<expressions::ExpressionManager>(expressions::ExpressionManager());
for (uint_fast64_t i = 0; i < this->numberOfStates; ++i) {
expressionManager->declareRationalVariable(std::to_string(i));
}
}
template <typename ValueType, typename ConstantType>
std::map<std::shared_ptr<expressions::BinaryRelationExpression>, AssumptionStatus> AssumptionMaker<ValueType, ConstantType>::createAndCheckAssumptions(uint_fast64_t val1, uint_fast64_t val2, std::shared_ptr<Order> order, storage::ParameterRegion<ValueType> region) const {
auto vec1 = std::vector<ConstantType>();
auto vec2 = std::vector<ConstantType>();
return createAndCheckAssumptions(val1, val2, order, region, vec1, vec2);
}
template <typename ValueType>
std::map<std::shared_ptr<expressions::BinaryRelationExpression>, AssumptionStatus> AssumptionMaker<ValueType>::createAndCheckAssumption(uint_fast64_t val1, uint_fast64_t val2, Order* order) {
template <typename ValueType, typename ConstantType>
std::map<std::shared_ptr<expressions::BinaryRelationExpression>, AssumptionStatus> AssumptionMaker<ValueType, ConstantType>::createAndCheckAssumptions(uint_fast64_t val1, uint_fast64_t val2, std::shared_ptr<Order> order, storage::ParameterRegion<ValueType> region, std::vector<ConstantType> const minValues, std::vector<ConstantType> const maxValues) const {
std::map<std::shared_ptr<expressions::BinaryRelationExpression>, AssumptionStatus> result;
expressions::Variable var1 = expressionManager->getVariable(std::to_string(val1));
expressions::Variable var2 = expressionManager->getVariable(std::to_string(val2));
std::shared_ptr<expressions::BinaryRelationExpression> assumption1
= std::make_shared<expressions::BinaryRelationExpression>(expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
var1.getExpression().getBaseExpressionPointer(), var2.getExpression().getBaseExpressionPointer(),
expressions::BinaryRelationExpression::RelationType::Greater));
AssumptionStatus result1;
AssumptionStatus result2;
AssumptionStatus result3;
if (validate) {
result1 = assumptionChecker->validateAssumption(assumption1, order);
} else {
result1 = AssumptionStatus::UNKNOWN;
STORM_LOG_INFO("Creating assumptions for " << val1 << " and " << val2);
assert (order->compare(val1, val2) == Order::UNKNOWN);
auto assumption = createAndCheckAssumption(val1, val2, expressions::BinaryRelationExpression::RelationType::Greater, order, region, minValues, maxValues);
if (assumption.second != AssumptionStatus::INVALID) {
result.insert(assumption);
if (assumption.second == AssumptionStatus::VALID) {
assert (createAndCheckAssumption(val2, val1, expressions::BinaryRelationExpression::RelationType::Greater, order, region, minValues, maxValues).second != AssumptionStatus::VALID
&& createAndCheckAssumption(val1, val2, expressions::BinaryRelationExpression::RelationType::Equal, order, region, minValues, maxValues).second != AssumptionStatus::VALID);
STORM_LOG_INFO("Assumption " << assumption.first << "is valid" << std::endl);
return result;
}
}
result[assumption1] = result1;
std::shared_ptr<expressions::BinaryRelationExpression> assumption2
= std::make_shared<expressions::BinaryRelationExpression>(expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
var2.getExpression().getBaseExpressionPointer(), var1.getExpression().getBaseExpressionPointer(),
expressions::BinaryRelationExpression::RelationType::Greater));
if (validate) {
result2 = assumptionChecker->validateAssumption(assumption2, order);
} else {
result2 = AssumptionStatus::UNKNOWN;
assert (order->compare(val1, val2) == Order::UNKNOWN);
assumption = createAndCheckAssumption(val2, val1, expressions::BinaryRelationExpression::RelationType::Greater, order, region, minValues, maxValues);
if (assumption.second != AssumptionStatus::INVALID) {
if (assumption.second == AssumptionStatus::VALID) {
result.clear();
result.insert(assumption);
assert (createAndCheckAssumption(val1, val2, expressions::BinaryRelationExpression::RelationType::Equal, order, region, minValues, maxValues).second != AssumptionStatus::VALID);
STORM_LOG_INFO("Assumption " << assumption.first << "is valid" << std::endl);
return result;
}
result.insert(assumption);
}
result[assumption2] = result2;
std::shared_ptr<expressions::BinaryRelationExpression> assumption3
= std::make_shared<expressions::BinaryRelationExpression>(expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
var2.getExpression().getBaseExpressionPointer(), var1.getExpression().getBaseExpressionPointer(),
expressions::BinaryRelationExpression::RelationType::Equal));
if (validate) {
result3 = assumptionChecker->validateAssumption(assumption3, order);
} else {
result3 = AssumptionStatus::UNKNOWN;
assert (order->compare(val1, val2) == Order::UNKNOWN);
assumption = createAndCheckAssumption(val1, val2, expressions::BinaryRelationExpression::RelationType::Equal, order, region, minValues, maxValues);
if (assumption.second != AssumptionStatus::INVALID) {
if (assumption.second == AssumptionStatus::VALID) {
result.clear();
result.insert(assumption);
STORM_LOG_INFO("Assumption " << assumption.first << "is valid" << std::endl);
return result;
}
result.insert(assumption);
}
result[assumption3] = result3;
assert (order->compare(val1, val2) == Order::UNKNOWN);
STORM_LOG_INFO("None of the assumptions is valid, number of possible assumptions: " << result.size() << std::endl);
return result;
}
template class AssumptionMaker<RationalFunction>;
template <typename ValueType, typename ConstantType>
void AssumptionMaker<ValueType, ConstantType>::initializeCheckingOnSamples(std::shared_ptr<logic::Formula const> formula, std::shared_ptr<models::sparse::Dtmc<ValueType>> model, storage::ParameterRegion<ValueType> region, uint_fast64_t numberOfSamples){
assumptionChecker.initializeCheckingOnSamples(formula, model, region, numberOfSamples);
}
template <typename ValueType, typename ConstantType>
void AssumptionMaker<ValueType, ConstantType>::setSampleValues(std::vector<std::vector<ConstantType>>const & samples) {
assumptionChecker.setSampleValues(samples);
}
template <typename ValueType, typename ConstantType>
std::pair<std::shared_ptr<expressions::BinaryRelationExpression>, AssumptionStatus> AssumptionMaker<ValueType, ConstantType>::createAndCheckAssumption(uint_fast64_t val1, uint_fast64_t val2, expressions::BinaryRelationExpression::RelationType relationType, std::shared_ptr<Order> order, storage::ParameterRegion<ValueType> region, std::vector<ConstantType> const minValues, std::vector<ConstantType> const maxValues) const {
assert (val1 != val2);
expressions::Variable var1 = expressionManager->getVariable(std::to_string(val1));
expressions::Variable var2 = expressionManager->getVariable(std::to_string(val2));
auto assumption = std::make_shared<expressions::BinaryRelationExpression>(expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(), var1.getExpression().getBaseExpressionPointer(), var2.getExpression().getBaseExpressionPointer(), relationType));
AssumptionStatus validationResult = assumptionChecker.validateAssumption(val1, val2, assumption, order, region, minValues, maxValues);
return std::pair<std::shared_ptr<expressions::BinaryRelationExpression>, AssumptionStatus>(assumption, validationResult);
}
template class AssumptionMaker<RationalFunction, double>;
template class AssumptionMaker<RationalFunction, RationalNumber>;
}
}

56
src/storm-pars/analysis/AssumptionMaker.h

@ -3,47 +3,65 @@
#include "AssumptionChecker.h"
#include "Order.h"
#include "OrderExtender.h"
#include "storm/storage/expressions/BinaryRelationExpression.h"
#include "storm-pars/utility/ModelInstantiator.h"
#include "storm/storage/expressions/BinaryRelationExpression.h"
#include "storm/storage/expressions/ExpressionManager.h"
#include "storm/storage/SparseMatrix.h"
namespace storm {
namespace analysis {
template<typename ValueType>
template<typename ValueType, typename ConstantType>
class AssumptionMaker {
typedef std::shared_ptr<expressions::BinaryRelationExpression> AssumptionType;
public:
/*!
* Constructs AssumptionMaker based on the order extender, the assumption checker and number of states of the mode
* Constructs AssumptionMaker based on the matrix of the model.
*
* @param matrix The matrix of the model.
*/
AssumptionMaker(storage::SparseMatrix<ValueType> matrix);
/*!
* Creates assumptions, and checks them, only VALID and UNKNOWN assumptions are returned.
* If one assumption is VALID, this assumption will be returned as only assumption.
* Possible results: AssumptionStatus::VALID, AssumptionStatus::UNKNOWN.
*
* @param orderExtender The OrderExtender which needs the assumptions made by the AssumptionMaker.
* @param checker The AssumptionChecker which checks the assumptions at sample points.
* @param numberOfStates The number of states of the model.
* @param val1 First state number.
* @param val2 Second state number.
* @param order The order on which the assumptions are checked.
* @param region The region for the parameters.
* @return Map with at most three assumptions, and the validation.
*/
AssumptionMaker(AssumptionChecker<ValueType>* checker, uint_fast64_t numberOfStates, bool validate);
std::map<std::shared_ptr<expressions::BinaryRelationExpression>, AssumptionStatus> createAndCheckAssumptions(uint_fast64_t val1, uint_fast64_t val2, std::shared_ptr<Order> order, storage::ParameterRegion<ValueType> region) const;
std::map<std::shared_ptr<expressions::BinaryRelationExpression>, AssumptionStatus> createAndCheckAssumptions(uint_fast64_t val1, uint_fast64_t val2, std::shared_ptr<Order> order, storage::ParameterRegion<ValueType> region, std::vector<ConstantType> const minValues, std::vector<ConstantType> const maxValue) const;
/*!
* Creates assumptions, and checks them if validate in constructor is true.
* Possible results: AssumptionStatus::VALID, AssumptionStatus::INVALID, AssumptionStatus::UNKNOWN
* If validate is false result is always AssumptionStatus::UNKNOWN
* Initializes the given number of sample points for a given model, formula and region.
*
* @param val1 First state number
* @param val2 Second state number
* @param order The order on which the assumptions are checked
* @return Map with three assumptions, and the validation
* @param formula The formula to compute the samples for.
* @param model The considered model.
* @param region The region of the model's parameters.
* @param numberOfSamples Number of sample points.
*/
std::map<std::shared_ptr<expressions::BinaryRelationExpression>, AssumptionStatus> createAndCheckAssumption(uint_fast64_t val1, uint_fast64_t val2, Order* order);
void initializeCheckingOnSamples(std::shared_ptr<logic::Formula const> formula, std::shared_ptr<models::sparse::Dtmc<ValueType>> model, storage::ParameterRegion<ValueType> region, uint_fast64_t numberOfSamples);
/*!
* Sets the sample values to the given vector.
*
* @param samples The new value for samples.
*/
void setSampleValues(std::vector<std::vector<ConstantType>>const & samples);
private:
AssumptionChecker<ValueType>* assumptionChecker;
std::pair<std::shared_ptr<expressions::BinaryRelationExpression>, AssumptionStatus> createAndCheckAssumption(uint_fast64_t val1, uint_fast64_t val2, expressions::BinaryRelationExpression::RelationType relationType, std::shared_ptr<Order> order, storage::ParameterRegion<ValueType> region, std::vector<ConstantType> const minValues, std::vector<ConstantType> const maxValue) const;
AssumptionChecker<ValueType, ConstantType> assumptionChecker;
std::shared_ptr<expressions::ExpressionManager> expressionManager;
uint_fast64_t numberOfStates;
bool validate;
};
}
}

175
src/storm-pars/analysis/LocalMonotonicityResult.cpp

@ -0,0 +1,175 @@
#include "LocalMonotonicityResult.h"
namespace storm {
namespace analysis {
template <typename VariableType>
LocalMonotonicityResult<VariableType>::LocalMonotonicityResult(uint_fast64_t numberOfStates) {
stateMonRes = std::vector<std::shared_ptr<MonotonicityResult<VariableType>>>(numberOfStates, nullptr);
globalMonotonicityResult = std::make_shared<MonotonicityResult<VariableType>>();
statesMonotone = storm::storage::BitVector(numberOfStates, false);
dummyPointer = std::make_shared<MonotonicityResult<VariableType>>();
done = false;
}
template <typename VariableType>
typename LocalMonotonicityResult<VariableType>::Monotonicity LocalMonotonicityResult<VariableType>::getMonotonicity(uint_fast64_t state, VariableType var) const {
if (stateMonRes[state] == dummyPointer) {
return Monotonicity::Constant;
} else if (stateMonRes[state] != nullptr) {
auto res = stateMonRes[state]->getMonotonicity(var);
if (res == Monotonicity::Unknown && globalMonotonicityResult->isDoneForVar(var)) {
return globalMonotonicityResult->getMonotonicity(var);
}
return res;
} else {
return globalMonotonicityResult->isDoneForVar(var) ? globalMonotonicityResult->getMonotonicity(var) : Monotonicity::Unknown;
}
}
template <typename VariableType>
std::shared_ptr<MonotonicityResult<VariableType>> LocalMonotonicityResult<VariableType>::getGlobalMonotonicityResult() const {
return globalMonotonicityResult;
}
template <typename VariableType>
void LocalMonotonicityResult<VariableType>::setMonotonicity(uint_fast64_t state, VariableType var, typename LocalMonotonicityResult<VariableType>::Monotonicity mon) {
assert (stateMonRes[state] != dummyPointer);
if (stateMonRes[state] == nullptr) {
stateMonRes[state] = std::make_shared<MonotonicityResult<VariableType>>();
}
stateMonRes[state]->addMonotonicityResult(var, mon);
globalMonotonicityResult->updateMonotonicityResult(var, mon);
if (mon == Monotonicity::Unknown || mon == Monotonicity::Not) {
statesMonotone.set(state, false);
} else {
bool stateMonotone = stateMonRes[state]->isAllMonotonicity();
if (stateMonotone) {
statesMonotone.set(state);
done |= statesMonotone.full();
}
if (isDone()) {
globalMonotonicityResult->setDone();
}
}
}
template <typename VariableType>
std::shared_ptr<LocalMonotonicityResult<VariableType>> LocalMonotonicityResult<VariableType>::copy() {
std::shared_ptr<LocalMonotonicityResult<VariableType>> copy = std::make_shared<LocalMonotonicityResult<VariableType>>(stateMonRes.size());
for (auto state = 0; state < stateMonRes.size(); state++) {
if (stateMonRes[state] != nullptr) {
copy->setMonotonicityResult(state, stateMonRes[state]->copy());
}
}
copy->setGlobalMonotonicityResult(this->getGlobalMonotonicityResult()->copy());
copy->setStatesMonotone(statesMonotone);
return copy;
}
template <typename VariableType>
bool LocalMonotonicityResult<VariableType>::isDone() const {
return done;
}
template <typename VariableType>
void LocalMonotonicityResult<VariableType>::setIndexMinimize(int i) {
assert (indexMinimize == -1);
this->indexMinimize = i;
}
template <typename VariableType>
void LocalMonotonicityResult<VariableType>::setIndexMaximize(int i) {
this->indexMaximize = i;
}
template <typename VariableType>
int LocalMonotonicityResult<VariableType>::getIndexMinimize() const {
return indexMinimize;
}
template <typename VariableType>
int LocalMonotonicityResult<VariableType>::getIndexMaximize() const {
return indexMaximize;
}
template <typename VariableType>
bool LocalMonotonicityResult<VariableType>::isNoMonotonicity() const {
return statesMonotone.empty();
}
template <typename VariableType>
void LocalMonotonicityResult<VariableType>::setMonotonicityResult(uint_fast64_t state, std::shared_ptr<MonotonicityResult<VariableType>> monRes) {
this->stateMonRes[state] = monRes;
}
template <typename VariableType>
void LocalMonotonicityResult<VariableType>::setGlobalMonotonicityResult(std::shared_ptr<MonotonicityResult<VariableType>> monRes) {
this->globalMonotonicityResult = monRes;
}
template <typename VariableType>
void LocalMonotonicityResult<VariableType>::setStatesMonotone(storm::storage::BitVector statesMonotone) {
this->statesMonotone = statesMonotone;
}
template <typename VariableType>
void LocalMonotonicityResult<VariableType>::setConstant(uint_fast64_t state) {
if (stateMonRes[state] == nullptr) {
stateMonRes[state] = dummyPointer;
}
this->statesMonotone.set(state);
}
template<typename VariableType>
void LocalMonotonicityResult<VariableType>::setMonotoneIncreasing(VariableType var) {
globalMonotonicityResult->updateMonotonicityResult(var, Monotonicity::Incr);
globalMonotonicityResult->setDoneForVar(var);
setFixedParameters = true;
}
template<typename VariableType>
void LocalMonotonicityResult<VariableType>::setMonotoneDecreasing(VariableType var) {
globalMonotonicityResult->updateMonotonicityResult(var, Monotonicity::Decr);
globalMonotonicityResult->setDoneForVar(var);
setFixedParameters = true;
}
template <typename VariableType>
std::string LocalMonotonicityResult<VariableType>::toString() const {
std::string result = "Local Monotonicity Result: \n";
for (auto i = 0; i < stateMonRes.size(); ++i) {
result += "state ";
result += std::to_string(i);
if (stateMonRes[i] != nullptr) {
result += stateMonRes[i]->toString();
} else if (statesMonotone[i]) {
result += "constant";
} else {
result += "not analyzed";
}
result += "\n";
}
return result;
}
template<typename VariableType>
bool LocalMonotonicityResult<VariableType>::isFixedParametersSet() const {
return setFixedParameters;
}
template<typename VariableType>
void LocalMonotonicityResult<VariableType>::setDone(bool done) {
this->done = done;
}
template<typename VariableType>
std::shared_ptr<MonotonicityResult<VariableType>>
LocalMonotonicityResult<VariableType>::getMonotonicity(uint_fast64_t state) const {
return stateMonRes[state];
}
template class LocalMonotonicityResult<storm::RationalFunctionVariable>;
}
}

113
src/storm-pars/analysis/LocalMonotonicityResult.h

@ -0,0 +1,113 @@
#ifndef STORM_LOCALMONOTONICITYRESULT_H
#define STORM_LOCALMONOTONICITYRESULT_H
#include <vector>
#include "storm-pars/analysis/MonotonicityResult.h"
#include "storm/adapters/RationalFunctionAdapter.h"
#include "storm/storage/BitVector.h"
namespace storm {
namespace analysis {
template <typename VariableType>
class LocalMonotonicityResult {
public:
typedef typename MonotonicityResult<VariableType>::Monotonicity Monotonicity;
/*!
* Constructs a new LocalMonotonicityResult object.
*
* @param numberOfStates The number of states in the considered model.
* @return The new object.
*/
LocalMonotonicityResult(uint_fast64_t numberOfStates);
/*!
* Returns the local Monotonicity of a parameter at a given state.
*
* @param state The state.
* @param var The parameter.
* @return The local Monotonicity.
*/
Monotonicity getMonotonicity(uint_fast64_t state, VariableType var) const;
std::shared_ptr<MonotonicityResult<VariableType>> getMonotonicity(uint_fast64_t state) const;
/*!
* Sets the local Monotonicity of a parameter at a given state.
*
* @param mon The Monotonicity.
* @param state The state.
* @param var The parameter.
*/
void setMonotonicity(uint_fast64_t state, VariableType var, Monotonicity mon);
/*!
* Returns the Global MonotonicityResult object that corresponds to this object.
*
* @return The pointer to the global MonotonicityResult.
*/
std::shared_ptr<MonotonicityResult<VariableType>> getGlobalMonotonicityResult() const;
/*!
* Constructs a new LocalMonotonicityResult object that is a copy of the current one.
*
* @return Pointer to the copy.
*/
std::shared_ptr<LocalMonotonicityResult<VariableType>> copy();
/*!
* Checks if the LocalMonotonicity is done yet.
*
* @return true if done is set to true, false otherwise.
*/
bool isDone() const;
void setDone(bool done = true);
bool isNoMonotonicity() const;
void setConstant(uint_fast64_t state);
void setIndexMinimize(int index);
void setIndexMaximize(int index);
int getIndexMinimize() const;
int getIndexMaximize() const;
/*!
* Constructs a string output of all variables and their corresponding Monotonicity
* @return Results so far
*/
std::string toString() const;
void setMonotoneIncreasing(VariableType var);
void setMonotoneDecreasing(VariableType var);
bool isFixedParametersSet() const;
private:
std::vector<std::shared_ptr<MonotonicityResult<VariableType>>> stateMonRes;
std::shared_ptr<MonotonicityResult<VariableType>> globalMonotonicityResult;
void setMonotonicityResult(uint_fast64_t state, std::shared_ptr<MonotonicityResult<VariableType>> monRes);
void setGlobalMonotonicityResult(std::shared_ptr<MonotonicityResult<VariableType>> monRes);
void setStatesMonotone(storm::storage::BitVector statesMonotone);
storm::storage::BitVector statesMonotone;
bool done;
int indexMinimize = -1;
int indexMaximize = -1;
std::shared_ptr<MonotonicityResult<VariableType>> dummyPointer;
bool setFixedParameters = false;
};
}
}
#endif //STORM_LOCALMONOTONICITYRESULT_H

757
src/storm-pars/analysis/MonotonicityChecker.cpp

@ -1,690 +1,143 @@
#include "MonotonicityChecker.h"
#include "storm-pars/analysis/AssumptionMaker.h"
#include "storm-pars/analysis/AssumptionChecker.h"
#include "storm-pars/analysis/Order.h"
#include "storm-pars/analysis/OrderExtender.h"
#include "storm/exceptions/NotSupportedException.h"
#include "storm/exceptions/UnexpectedException.h"
#include "storm/exceptions/InvalidOperationException.h"
#include "storm/utility/Stopwatch.h"
#include "storm/models/ModelType.h"
#include "storm/api/verification.h"
#include "storm-pars/api/storm-pars.h"
#include "storm/modelchecker/results/CheckResult.h"
#include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h"
#include "storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.h"
namespace storm {
namespace analysis {
/*** Constructor ***/
template <typename ValueType>
MonotonicityChecker<ValueType>::MonotonicityChecker(std::shared_ptr<storm::models::ModelBase> model, std::vector<std::shared_ptr<storm::logic::Formula const>> formulas, std::vector<storm::storage::ParameterRegion<ValueType>> regions, bool validate, uint_fast64_t numberOfSamples, double const& precision) {
assert (model != nullptr);
this->model = model;
this->formulas = formulas;
this->validate = validate;
this->precision = precision;
std::shared_ptr<storm::models::sparse::Model<ValueType>> sparseModel = model->as<storm::models::sparse::Model<ValueType>>();
if (regions.size() == 1) {
this->region = *(regions.begin());
} else {
assert (regions.size() == 0);
typename storm::storage::ParameterRegion<ValueType>::Valuation lowerBoundaries;
typename storm::storage::ParameterRegion<ValueType>::Valuation upperBoundaries;
std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType> vars;
vars = storm::models::sparse::getProbabilityParameters(*sparseModel);
for (auto var : vars) {
typename storm::storage::ParameterRegion<ValueType>::CoefficientType lb = storm::utility::convertNumber<typename storm::storage::ParameterRegion<ValueType>::CoefficientType>(0 + precision);
typename storm::storage::ParameterRegion<ValueType>::CoefficientType ub = storm::utility::convertNumber<typename storm::storage::ParameterRegion<ValueType>::CoefficientType>(1 - precision);
lowerBoundaries.insert(std::make_pair(var, lb));
upperBoundaries.insert(std::make_pair(var, ub));
}
this->region = storm::storage::ParameterRegion<ValueType>(std::move(lowerBoundaries), std::move(upperBoundaries));
}
if (numberOfSamples > 0) {
// sampling
if (model->isOfType(storm::models::ModelType::Dtmc)) {
this->resultCheckOnSamples = std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>>(
checkOnSamples(model->as<storm::models::sparse::Dtmc<ValueType>>(), numberOfSamples));
} else if (model->isOfType(storm::models::ModelType::Mdp)) {
this->resultCheckOnSamples = std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>>(
checkOnSamples(model->as<storm::models::sparse::Mdp<ValueType>>(), numberOfSamples));
}
checkSamples= true;
} else {
checkSamples= false;
}
this->extender = new storm::analysis::OrderExtender<ValueType>(sparseModel);
MonotonicityChecker<ValueType>::MonotonicityChecker(storage::SparseMatrix<ValueType> matrix) {
this->matrix = matrix;
}
/*** Public methods ***/
template <typename ValueType>
std::map<storm::analysis::Order*, std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>>> MonotonicityChecker<ValueType>::checkMonotonicity(std::ostream& outfile) {
auto map = createOrder();
std::shared_ptr<storm::models::sparse::Model<ValueType>> sparseModel = model->as<storm::models::sparse::Model<ValueType>>();
auto matrix = sparseModel->getTransitionMatrix();
return checkMonotonicity(outfile, map, matrix);
}
template <typename ValueType>
std::map<storm::analysis::Order*, std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>>> MonotonicityChecker<ValueType>::checkMonotonicity(std::ostream& outfile, std::map<storm::analysis::Order*, std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>>> map, storm::storage::SparseMatrix<ValueType> matrix) {
storm::utility::Stopwatch monotonicityCheckWatch(true);
std::map<storm::analysis::Order *, std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>>> result;
if (map.size() == 0) {
// Nothing is known
outfile << " No assumptions -";
STORM_PRINT("No valid assumptions, couldn't build a sufficient order");
if (resultCheckOnSamples.size() != 0) {
STORM_PRINT("\n" << "Based results on samples");
} else {
outfile << " ?";
typename MonotonicityChecker<ValueType>::Monotonicity MonotonicityChecker<ValueType>::checkLocalMonotonicity(std::shared_ptr<Order> const& order, uint_fast64_t state, VariableType const& var, storage::ParameterRegion<ValueType> const& region) {
// Create + fill Vector containing the Monotonicity of the transitions to the succs
auto row = matrix.getRow(state);
// Ignore if all entries are constant
bool ignore = true;
std::vector<uint_fast64_t> succs;
std::vector<Monotonicity> succsMonUnsorted;
std::vector<uint_fast64_t> statesIncr;
std::vector<uint_fast64_t> statesDecr;
bool checkAllow = true;
for (auto entry : row) {
auto succState = entry.getColumn();
auto mon = checkTransitionMonRes(entry.getValue(), var, region);
succsMonUnsorted.push_back(mon);
succs.push_back(succState);
ignore &= entry.getValue().isConstant();
if (mon == Monotonicity::Incr) {
statesIncr.push_back(succState);
} else if (mon == Monotonicity::Decr) {
statesDecr.push_back(succState);
} else if (mon == Monotonicity::Not) {
checkAllow = false;
}
for (auto entry : resultCheckOnSamples) {
if (!entry.second.first && ! entry.second.second) {
outfile << " SX " << entry.first << " ";
} else if (entry.second.first && ! entry.second.second) {
outfile << " SI " << entry.first << " ";
} else if (entry.second.first && entry.second.second) {
outfile << " SC " << entry.first << " ";
} else {
outfile << " SD " << entry.first << " ";
}
}
} else {
size_t i = 0;
for (auto itr = map.begin(); i < map.size() && itr != map.end(); ++itr) {
auto order = itr->first;
auto addedStates = order->getAddedStates()->getNumberOfSetBits();
assert (addedStates == order->getAddedStates()->size());
std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>> varsMonotone = analyseMonotonicity(i, order,
matrix);
auto assumptions = itr->second;
if (assumptions.size() > 0) {
bool first = true;
for (auto itr2 = assumptions.begin(); itr2 != assumptions.end(); ++itr2) {
if (!first) {
outfile << (" ^ ");
} else {
first = false;
}
outfile << (*(*itr2));
}
if (ignore) {
return Monotonicity::Constant;
}
auto succsSorted = order->sortStates(&succs);
uint_fast64_t succSize = succs.size();
if (succsSorted[succSize - 1] == matrix.getColumnCount()) {
// Maybe we can still do something
// If one is decreasing and all others increasing, and this one is above all others or vice versa
if (checkAllow) {
if (statesIncr.size() == 1 && statesDecr.size() > 1) {
auto comp = order->allAboveBelow(statesDecr, statesIncr.back());
if (comp.first) {
// All decreasing states are above the increasing state, therefore decreasing
return Monotonicity::Decr;
} else if (comp.second) {
// All decreasing states are below the increasing state, therefore increasing
return Monotonicity::Incr;
}
outfile << " - ";
} else if (assumptions.size() == 0) {
outfile << "No assumptions - ";
}
if (varsMonotone.size() == 0) {
outfile << "No params";
} else {
auto itr2 = varsMonotone.begin();
while (itr2 != varsMonotone.end()) {
if (checkSamples &&
resultCheckOnSamples.find(itr2->first) != resultCheckOnSamples.end() &&
(!resultCheckOnSamples[itr2->first].first &&
!resultCheckOnSamples[itr2->first].second)) {
outfile << "X " << itr2->first;
} else {
if (itr2->second.first && itr2->second.second) {
outfile << "C " << itr2->first;
} else if (itr2->second.first) {
outfile << "I " << itr2->first;
} else if (itr2->second.second) {
outfile << "D " << itr2->first;
} else {
outfile << "? " << itr2->first;
}
}
++itr2;
if (itr2 != varsMonotone.end()) {
outfile << " ";
}
} else if (statesDecr.size() == 1 && statesIncr.size() > 1) {
auto comp = order->allAboveBelow(statesDecr, statesIncr.back());
if (comp.first) {
// All increasing states are below the decreasing state, therefore increasing
return Monotonicity::Incr;
} else if (comp.second) {
// All increasing states are above the decreasing state, therefore decreasing
return Monotonicity::Decr;
}
result.insert(
std::pair<storm::analysis::Order *, std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>>>(
order, varsMonotone));
}
++i;
outfile << ";";
}
}
outfile << ", ";
monotonicityCheckWatch.stop();
return result;
}
template <typename ValueType>
std::map<storm::analysis::Order*, std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>>> MonotonicityChecker<ValueType>::createOrder() {
// Transform to Orders
storm::utility::Stopwatch orderWatch(true);
// Use parameter lifting modelchecker to get initial min/max values for order creation
storm::modelchecker::SparseDtmcParameterLiftingModelChecker<storm::models::sparse::Dtmc<ValueType>, double> plaModelChecker;
std::unique_ptr<storm::modelchecker::CheckResult> checkResult;
auto env = Environment();
auto formula = formulas[0];
const storm::modelchecker::CheckTask<storm::logic::Formula, ValueType> checkTask
= storm::modelchecker::CheckTask<storm::logic::Formula, ValueType>(*formula);
STORM_LOG_THROW(plaModelChecker.canHandle(model, checkTask), storm::exceptions::NotSupportedException,
"Cannot handle this formula");
plaModelChecker.specify(env, model, checkTask);
std::unique_ptr<storm::modelchecker::CheckResult> minCheck = plaModelChecker.check(env, region,storm::solver::OptimizationDirection::Minimize);
std::unique_ptr<storm::modelchecker::CheckResult> maxCheck = plaModelChecker.check(env, region,storm::solver::OptimizationDirection::Maximize);
auto minRes = minCheck->asExplicitQuantitativeCheckResult<double>();
auto maxRes = maxCheck->asExplicitQuantitativeCheckResult<double>();
std::vector<double> minValues = minRes.getValueVector();
std::vector<double> maxValues = maxRes.getValueVector();
// Create initial order
std::tuple<storm::analysis::Order*, uint_fast64_t, uint_fast64_t> criticalTuple = extender->toOrder(formulas, minValues, maxValues);
// Continue based on not (yet) sorted states
std::map<storm::analysis::Order*, std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>>> result;
auto val1 = std::get<1>(criticalTuple);
auto val2 = std::get<2>(criticalTuple);
auto numberOfStates = model->getNumberOfStates();
std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>> assumptions;
if (val1 == numberOfStates && val2 == numberOfStates) {
result.insert(std::pair<storm::analysis::Order*, std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>>>(std::get<0>(criticalTuple), assumptions));
} else if (val1 != numberOfStates && val2 != numberOfStates) {
storm::analysis::AssumptionChecker<ValueType> *assumptionChecker;
if (model->isOfType(storm::models::ModelType::Dtmc)) {
auto dtmc = model->as<storm::models::sparse::Dtmc<ValueType>>();
assumptionChecker = new storm::analysis::AssumptionChecker<ValueType>(formulas[0], dtmc, region, 3);
} else if (model->isOfType(storm::models::ModelType::Mdp)) {
auto mdp = model->as<storm::models::sparse::Mdp<ValueType>>();
assumptionChecker = new storm::analysis::AssumptionChecker<ValueType>(formulas[0], mdp, 3);
} else {
STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException,
"Unable to perform monotonicity analysis on the provided model type.");
}
auto assumptionMaker = new storm::analysis::AssumptionMaker<ValueType>(assumptionChecker, numberOfStates, validate);
result = extendOrderWithAssumptions(std::get<0>(criticalTuple), assumptionMaker, val1, val2, assumptions);
} else {
assert(false);
return Monotonicity::Unknown;
}
orderWatch.stop();
return result;
}
template <typename ValueType>
std::map<storm::analysis::Order*, std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>>> MonotonicityChecker<ValueType>::extendOrderWithAssumptions(storm::analysis::Order* order, storm::analysis::AssumptionMaker<ValueType>* assumptionMaker, uint_fast64_t val1, uint_fast64_t val2, std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>> assumptions) {
std::map<storm::analysis::Order*, std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>>> result;
auto numberOfStates = model->getNumberOfStates();
if (val1 == numberOfStates || val2 == numberOfStates) {
assert (val1 == val2);
assert (order->getAddedStates()->size() == order->getAddedStates()->getNumberOfSetBits());
result.insert(std::pair<storm::analysis::Order*, std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>>>(order, assumptions));
} else {
// Make the three assumptions
auto assumptionTriple = assumptionMaker->createAndCheckAssumption(val1, val2, order);
assert (assumptionTriple.size() == 3);
auto itr = assumptionTriple.begin();
auto assumption1 = *itr;
++itr;
auto assumption2 = *itr;
++itr;
auto assumption3 = *itr;
if (assumption1.second != AssumptionStatus::INVALID) {
auto orderCopy = new Order(order);
auto assumptionsCopy = std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>>(assumptions);
if (assumption1.second == AssumptionStatus::UNKNOWN) {
// only add assumption to the set of assumptions if it is unknown if it is valid
assumptionsCopy.push_back(assumption1.first);
}
auto criticalTuple = extender->extendOrder(orderCopy, assumption1.first);
if (somewhereMonotonicity(std::get<0>(criticalTuple))) {
auto map = extendOrderWithAssumptions(std::get<0>(criticalTuple), assumptionMaker,
std::get<1>(criticalTuple), std::get<2>(criticalTuple),
assumptionsCopy);
result.insert(map.begin(), map.end());
}
}
if (assumption2.second != AssumptionStatus::INVALID) {
auto orderCopy = new Order(order);
auto assumptionsCopy = std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>>(assumptions);
if (assumption2.second == AssumptionStatus::UNKNOWN) {
assumptionsCopy.push_back(assumption2.first);
}
auto criticalTuple = extender->extendOrder(orderCopy, assumption2.first);
if (somewhereMonotonicity(std::get<0>(criticalTuple))) {
auto map = extendOrderWithAssumptions(std::get<0>(criticalTuple), assumptionMaker,
std::get<1>(criticalTuple), std::get<2>(criticalTuple),
assumptionsCopy);
result.insert(map.begin(), map.end());
}
}
if (assumption3.second != AssumptionStatus::INVALID) {
// Here we can use the original order and assumptions set
if (assumption3.second == AssumptionStatus::UNKNOWN) {
assumptions.push_back(assumption3.first);
}
auto criticalTuple = extender->extendOrder(order, assumption3.first);
if (somewhereMonotonicity(std::get<0>(criticalTuple))) {
auto map = extendOrderWithAssumptions(std::get<0>(criticalTuple), assumptionMaker,
std::get<1>(criticalTuple), std::get<2>(criticalTuple),
assumptions);
result.insert(map.begin(), map.end());
}
}
if (succSize == 2) {
// In this case we can ignore the last entry, as this will have a probability of 1 - the other
succSize = 1;
}
return result;
}
template <typename ValueType>
ValueType MonotonicityChecker<ValueType>::getDerivative(ValueType function, typename utility::parametric::VariableType<ValueType>::type var) {
if (function.isConstant()) {
return storm::utility::zero<ValueType>();
}
if ((derivatives[function]).find(var) == (derivatives[function]).end()) {
(derivatives[function])[var] = function.derivative(var);
}
return (derivatives[function])[var];
}
template <typename ValueType>
std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>> MonotonicityChecker<ValueType>::analyseMonotonicity(uint_fast64_t j, storm::analysis::Order* order, storm::storage::SparseMatrix<ValueType> matrix) {
std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>> varsMonotone;
// go over all rows, check for each row local monotonicity
for (uint_fast64_t i = 0; i < matrix.getColumnCount(); ++i) {
auto row = matrix.getRow(i);
// only enter if you are in a state with at least two successors (so there must be successors,
// and first prob shouldn't be 1)
if (row.begin() != row.end() && !row.begin()->getValue().isOne()) {
std::map<uint_fast64_t, ValueType> transitions;
// Gather all states which are reached with a non constant probability
auto states = new storm::storage::BitVector(matrix.getColumnCount());
std::set<typename utility::parametric::VariableType<ValueType>::type> vars;
for (auto const& entry : row) {
if (!entry.getValue().isConstant()) {
// only analyse take non constant transitions
transitions.insert(std::pair<uint_fast64_t, ValueType>(entry.getColumn(), entry.getValue()));
for (auto const& var:entry.getValue().gatherVariables()) {
vars.insert(var);
states->set(entry.getColumn());
}
}
}
// Copy info from checkOnSamples
if (checkSamples) {
for (auto var : vars) {
assert (resultCheckOnSamples.find(var) != resultCheckOnSamples.end());
if (varsMonotone.find(var) == varsMonotone.end()) {
varsMonotone[var].first = resultCheckOnSamples[var].first;
varsMonotone[var].second = resultCheckOnSamples[var].second;
} else {
varsMonotone[var].first &= resultCheckOnSamples[var].first;
varsMonotone[var].second &= resultCheckOnSamples[var].second;
}
}
} else {
for (auto var : vars) {
if (varsMonotone.find(var) == varsMonotone.end()) {
varsMonotone[var].first = true;
varsMonotone[var].second = true;
}
}
}
// Sort the states based on the order
auto sortedStates = order->sortStates(states);
if (sortedStates[sortedStates.size() - 1] == matrix.getColumnCount()) {
// If the states are not all sorted, we still might obtain some monotonicity
for (auto var: vars) {
// current value of monotonicity
std::pair<bool, bool> *value = &varsMonotone.find(var)->second;
// Go over all transitions to successor states, compare all of them
for (auto itr2 = transitions.begin(); (value->first || value->second)
&& itr2 != transitions.end(); ++itr2) {
for (auto itr3 = transitions.begin(); (value->first || value->second)
&& itr3 != transitions.end(); ++itr3) {
if (itr2->first < itr3->first) {
auto derivative2 = getDerivative(itr2->second, var);
auto derivative3 = getDerivative(itr3->second, var);
auto compare = order->compare(itr2->first, itr3->first);
if (compare == Order::ABOVE) {
// As the first state (itr2) is above the second state (itr3) it
// is sufficient to look at the derivative of itr2.
std::pair<bool, bool> mon2;
mon2 = checkDerivative(derivative2, region);
value->first &= mon2.first;
value->second &= mon2.second;
} else if (compare == Order::BELOW) {
// As the second state (itr3) is above the first state (itr2) it
// is sufficient to look at the derivative of itr3.
std::pair<bool, bool> mon3;
mon3 = checkDerivative(derivative3, region);
value->first &= mon3.first;
value->second &= mon3.second;
} else if (compare == Order::SAME) {
// Behaviour doesn't matter, as the states are at the same level.
} else {
// only if derivatives are the same we can continue
if (derivative2 != derivative3) {
// As the relation between the states is unknown, we can't claim
// anything about the monotonicity.
value->first = false;
value->second = false;
}
}
}
}
}
}
} else {
// The states are all sorted
for (auto var : vars) {
std::pair<bool, bool> *value = &varsMonotone.find(var)->second;
bool change = false;
for (auto const &i : sortedStates) {
auto res = checkDerivative(getDerivative(transitions[i], var), region);
change = change || (!(value->first && value->second) // they do not hold both
&& ((value->first && !res.first)
|| (value->second && !res.second)));
if (change) {
value->first &= res.second;
value->second &= res.first;
} else {
value->first &= res.first;
value->second &= res.second;
}
if (!value->first && !value->second) {
break;
}
}
}
}
// First check as long as it stays constant and either incr or decr
bool allowedToSwap = true;
Monotonicity localMonotonicity = Monotonicity::Constant;
uint_fast64_t index = 0;
while (index < succSize && localMonotonicity == Monotonicity::Constant) {
auto itr = std::find(succs.begin(), succs.end(), succsSorted[index]);
auto newIndex = std::distance(succs.begin(), itr);
auto transitionMon = succsMonUnsorted[newIndex];
localMonotonicity = transitionMon;
if (transitionMon == Monotonicity::Not && succSize != 1) {
localMonotonicity = Monotonicity::Unknown;
}
index++;
}
return varsMonotone;
}
template <typename ValueType>
bool MonotonicityChecker<ValueType>::somewhereMonotonicity(Order* order) {
std::shared_ptr<storm::models::sparse::Model<ValueType>> sparseModel = model->as<storm::models::sparse::Model<ValueType>>();
auto matrix = sparseModel->getTransitionMatrix();
std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>> varsMonotone;
while (index < succSize && localMonotonicity != Monotonicity::Not && localMonotonicity != Monotonicity::Unknown) {
// We get here as soon as we have seen incr/decr once
auto itr = std::find(succs.begin(), succs.end(), succsSorted[index]);
auto newIndex = std::distance(succs.begin(), itr);
auto transitionMon = succsMonUnsorted[newIndex];
for (uint_fast64_t i = 0; i < matrix.getColumnCount(); ++i) {
// go over all rows
auto row = matrix.getRow(i);
auto first = (*row.begin());
if (first.getValue() != ValueType(1)) {
std::map<uint_fast64_t, ValueType> transitions;
for (auto itr = row.begin(); itr != row.end(); ++itr) {
transitions.insert(std::pair<uint_fast64_t, ValueType>((*itr).getColumn(), (*itr).getValue()));
if (transitionMon == Monotonicity::Not || transitionMon == Monotonicity::Unknown) {
return Monotonicity::Unknown;
}
if (allowedToSwap) {
// So far we have only seen constant and either incr or decr, but not both
if (transitionMon != Monotonicity::Constant && transitionMon != localMonotonicity) {
allowedToSwap = false;
}
auto val = first.getValue();
auto vars = val.gatherVariables();
// Copy info from checkOnSamples
if (checkSamples) {
for (auto var : vars) {
assert (resultCheckOnSamples.find(var) != resultCheckOnSamples.end());
if (varsMonotone.find(var) == varsMonotone.end()) {
varsMonotone[var].first = resultCheckOnSamples[var].first;
varsMonotone[var].second = resultCheckOnSamples[var].second;
} else {
varsMonotone[var].first &= resultCheckOnSamples[var].first;
varsMonotone[var].second &= resultCheckOnSamples[var].second;
}
}
} else {
for (auto var : vars) {
if (varsMonotone.find(var) == varsMonotone.end()) {
varsMonotone[var].first = true;
varsMonotone[var].second = true;
}
}
}
for (auto var: vars) {
// current value of monotonicity
std::pair<bool, bool> *value = &varsMonotone.find(var)->second;
// Go over all transitions to successor states, compare all of them
for (auto itr2 = transitions.begin(); (value->first || value->second)
&& itr2 != transitions.end(); ++itr2) {
for (auto itr3 = transitions.begin(); (value->first || value->second)
&& itr3 != transitions.end(); ++itr3) {
if (itr2->first < itr3->first) {
auto derivative2 = getDerivative(itr2->second, var);
auto derivative3 = getDerivative(itr3->second, var);
auto compare = order->compare(itr2->first, itr3->first);
if (compare == Order::ABOVE) {
// As the first state (itr2) is above the second state (itr3) it
// is sufficient to look at the derivative of itr2.
std::pair<bool, bool> mon2;
mon2 = checkDerivative(derivative2, region);
value->first &= mon2.first;
value->second &= mon2.second;
} else if (compare == Order::BELOW) {
// As the second state (itr3) is above the first state (itr2) it
// is sufficient to look at the derivative of itr3.
std::pair<bool, bool> mon3;
mon3 = checkDerivative(derivative3, region);
value->first &= mon3.first;
value->second &= mon3.second;
} else if (compare == Order::SAME) {
// Behaviour doesn't matter, as the states are at the same level.
} else {
// As the relation between the states is unknown, we don't do anything
}
}
}
}
} else if (!allowedToSwap) {
// So we have been at the point where we changed from incr to decr (or decr to incr)
if (transitionMon == localMonotonicity || transitionMon == Monotonicity::Not || transitionMon == Monotonicity::Unknown) {
localMonotonicity = Monotonicity::Unknown;
}
}
index++;
}
bool result = false;
for (auto itr = varsMonotone.begin(); !result && itr != varsMonotone.end(); ++itr) {
result = itr->second.first || itr->second.second;
}
return result;
return localMonotonicity;
}
/*** Private methods ***/
template <typename ValueType>
std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>> MonotonicityChecker<ValueType>::checkOnSamples(std::shared_ptr<storm::models::sparse::Dtmc<ValueType>> model, uint_fast64_t numberOfSamples) {
storm::utility::Stopwatch samplesWatch(true);
std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>> result;
auto instantiator = storm::utility::ModelInstantiator<storm::models::sparse::Dtmc<ValueType>, storm::models::sparse::Dtmc<double>>(*model);
auto matrix = model->getTransitionMatrix();
std::set<typename utility::parametric::VariableType<ValueType>::type> variables = storm::models::sparse::getProbabilityParameters(*model);
// For each of the variables create a model in which we only change the value for this specific variable
for (auto itr = variables.begin(); itr != variables.end(); ++itr) {
double previous = -1;
bool monDecr = true;
bool monIncr = true;
// Check monotonicity in variable (*itr) by instantiating the model
// all other variables fixed on lb, only increasing (*itr)
for (uint_fast64_t i = 0; (monDecr || monIncr) && i < numberOfSamples; ++i) {
// Create valuation
auto valuation = storm::utility::parametric::Valuation<ValueType>();
for (auto itr2 = variables.begin(); itr2 != variables.end(); ++itr2) {
// Only change value for current variable
if ((*itr) == (*itr2)) {
auto lb = region.getLowerBoundary(itr->name());
auto ub = region.getUpperBoundary(itr->name());
// Creates samples between lb and ub, that is: lb, lb + (ub-lb)/(#samples -1), lb + 2* (ub-lb)/(#samples -1), ..., ub
valuation[*itr2] = utility::convertNumber<typename utility::parametric::CoefficientType<ValueType>::type>(lb + i*(ub-lb)/(numberOfSamples-1));
} else {
auto lb = region.getLowerBoundary(itr->name());
valuation[*itr2] = utility::convertNumber<typename utility::parametric::CoefficientType<ValueType>::type>(lb);
}
}
// Instantiate model and get result
storm::models::sparse::Dtmc<double> sampleModel = instantiator.instantiate(valuation);
auto checker = storm::modelchecker::SparseDtmcPrctlModelChecker<storm::models::sparse::Dtmc<double>>(sampleModel);
std::unique_ptr<storm::modelchecker::CheckResult> checkResult;
auto formula = formulas[0];
if (formula->isProbabilityOperatorFormula() &&
formula->asProbabilityOperatorFormula().getSubformula().isUntilFormula()) {
const storm::modelchecker::CheckTask<storm::logic::UntilFormula, double> checkTask = storm::modelchecker::CheckTask<storm::logic::UntilFormula, double>(
(*formula).asProbabilityOperatorFormula().getSubformula().asUntilFormula());
checkResult = checker.computeUntilProbabilities(Environment(), checkTask);
} else if (formula->isProbabilityOperatorFormula() &&
formula->asProbabilityOperatorFormula().getSubformula().isEventuallyFormula()) {
const storm::modelchecker::CheckTask<storm::logic::EventuallyFormula, double> checkTask = storm::modelchecker::CheckTask<storm::logic::EventuallyFormula, double>(
(*formula).asProbabilityOperatorFormula().getSubformula().asEventuallyFormula());
checkResult = checker.computeReachabilityProbabilities(Environment(), checkTask);
} else {
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException,
"Expecting until or eventually formula");
}
auto quantitativeResult = checkResult->asExplicitQuantitativeCheckResult<double>();
std::vector<double> values = quantitativeResult.getValueVector();
auto initialStates = model->getInitialStates();
double initial = 0;
// Get total probability from initial states
for (auto j = initialStates.getNextSetIndex(0); j < model->getNumberOfStates(); j = initialStates.getNextSetIndex(j+1)) {
initial += values[j];
}
// Calculate difference with result for previous valuation
assert (initial >= 0-precision && initial <= 1+precision);
double diff = previous - initial;
assert (previous == -1 || diff >= -1-precision && diff <= 1 + precision);
if (previous != -1 && (diff > precision || diff < -precision)) {
monDecr &= diff > precision; // then previous value is larger than the current value from the initial states
monIncr &= diff < -precision;
}
previous = initial;
}
result.insert(std::pair<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>>(*itr, std::pair<bool,bool>(monIncr, monDecr)));
typename MonotonicityChecker<ValueType>::Monotonicity MonotonicityChecker<ValueType>::checkTransitionMonRes(ValueType function, typename MonotonicityChecker<ValueType>::VariableType param, typename MonotonicityChecker<ValueType>::Region region) {
std::pair<bool, bool> res = MonotonicityChecker<ValueType>::checkDerivative(getDerivative(function, param), region);
if (res.first && !res.second) {
return Monotonicity::Incr;
} else if (!res.first && res.second) {
return Monotonicity::Decr;
} else if (res.first && res.second) {
return Monotonicity::Constant;
} else {
return Monotonicity::Not;
}
samplesWatch.stop();
resultCheckOnSamples = result;
return result;
}
template <typename ValueType>
std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>> MonotonicityChecker<ValueType>::checkOnSamples(std::shared_ptr<storm::models::sparse::Mdp<ValueType>> model, uint_fast64_t numberOfSamples) {
storm::utility::Stopwatch samplesWatch(true);
std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>> result;
auto instantiator = storm::utility::ModelInstantiator<storm::models::sparse::Mdp<ValueType>, storm::models::sparse::Mdp<double>>(*model);
auto matrix = model->getTransitionMatrix();
std::set<typename utility::parametric::VariableType<ValueType>::type> variables = storm::models::sparse::getProbabilityParameters(*model);
for (auto itr = variables.begin(); itr != variables.end(); ++itr) {
double previous = -1;
bool monDecr = true;
bool monIncr = true;
for (uint_fast64_t i = 0; i < numberOfSamples; ++i) {
auto valuation = storm::utility::parametric::Valuation<ValueType>();
for (auto itr2 = variables.begin(); itr2 != variables.end(); ++itr2) {
// Only change value for current variable
if ((*itr) == (*itr2)) {
auto val = std::pair<typename utility::parametric::VariableType<ValueType>::type, typename utility::parametric::CoefficientType<ValueType>::type>(
(*itr2), storm::utility::convertNumber<typename utility::parametric::CoefficientType<ValueType>::type>(
boost::lexical_cast<std::string>((i + 1) / (double(numberOfSamples + 1)))));
valuation.insert(val);
} else {
auto val = std::pair<typename utility::parametric::VariableType<ValueType>::type, typename utility::parametric::CoefficientType<ValueType>::type>(
(*itr2), storm::utility::convertNumber<typename utility::parametric::CoefficientType<ValueType>::type>(
boost::lexical_cast<std::string>((1) / (double(numberOfSamples + 1)))));
valuation.insert(val);
}
}
storm::models::sparse::Mdp<double> sampleModel = instantiator.instantiate(valuation);
auto checker = storm::modelchecker::SparseMdpPrctlModelChecker<storm::models::sparse::Mdp<double>>(sampleModel);
std::unique_ptr<storm::modelchecker::CheckResult> checkResult;
auto formula = formulas[0];
if (formula->isProbabilityOperatorFormula() &&
formula->asProbabilityOperatorFormula().getSubformula().isUntilFormula()) {
const storm::modelchecker::CheckTask<storm::logic::UntilFormula, double> checkTask = storm::modelchecker::CheckTask<storm::logic::UntilFormula, double>(
(*formula).asProbabilityOperatorFormula().getSubformula().asUntilFormula());
checkResult = checker.computeUntilProbabilities(Environment(), checkTask);
} else if (formula->isProbabilityOperatorFormula() &&
formula->asProbabilityOperatorFormula().getSubformula().isEventuallyFormula()) {
const storm::modelchecker::CheckTask<storm::logic::EventuallyFormula, double> checkTask = storm::modelchecker::CheckTask<storm::logic::EventuallyFormula, double>(
(*formula).asProbabilityOperatorFormula().getSubformula().asEventuallyFormula());
checkResult = checker.computeReachabilityProbabilities(Environment(), checkTask);
} else {
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException,
"Expecting until or eventually formula");
}
auto quantitativeResult = checkResult->asExplicitQuantitativeCheckResult<double>();
std::vector<double> values = quantitativeResult.getValueVector();
auto initialStates = model->getInitialStates();
double initial = 0;
for (auto i = initialStates.getNextSetIndex(0); i < model->getNumberOfStates(); i = initialStates.getNextSetIndex(i+1)) {
initial += values[i];
}
assert (initial >= precision && initial <= 1+precision);
double diff = previous - initial;
assert (previous == -1 || diff >= -1-precision && diff <= 1 + precision);
if (previous != -1 && (diff > precision || diff < -precision)) {
monDecr &= diff > precision; // then previous value is larger than the current value from the initial states
monIncr &= diff < -precision;
}
previous = initial;
}
result.insert(std::pair<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>>(*itr, std::pair<bool,bool>(monIncr, monDecr)));
ValueType& MonotonicityChecker<ValueType>::getDerivative(ValueType function, typename MonotonicityChecker<ValueType>::VariableType var) {
auto& derivativeMap = derivatives[function];
if (derivativeMap.find(var) == derivativeMap.end()) {
derivativeMap[var] = function.derivative(var);
}
samplesWatch.stop();
resultCheckOnSamples = result;
return result;
return derivativeMap[var];
}
template class MonotonicityChecker<storm::RationalFunction>;
template class MonotonicityChecker<RationalFunction>;
}
}

148
src/storm-pars/analysis/MonotonicityChecker.h

@ -2,22 +2,22 @@
#define STORM_MONOTONICITYCHECKER_H
#include <map>
#include <boost/container/flat_map.hpp>
#include "Order.h"
#include "OrderExtender.h"
#include "AssumptionMaker.h"
#include "LocalMonotonicityResult.h"
#include "MonotonicityResult.h"
#include "storm-pars/storage/ParameterRegion.h"
#include "storm/solver/Z3SmtSolver.h"
#include "storm/storage/SparseMatrix.h"
#include "storm/storage/expressions/BinaryRelationExpression.h"
#include "storm/storage/expressions/ExpressionManager.h"
#include "storm/storage/expressions/RationalFunctionToExpression.h"
#include "storm/utility/constants.h"
#include "storm/models/ModelBase.h"
#include "storm/models/sparse/Dtmc.h"
#include "storm/models/sparse/Mdp.h"
#include "storm/logic/Formula.h"
#include "storm/storage/SparseMatrix.h"
#include "storm-pars/api/region.h"
#include "storm/solver/Z3SmtSolver.h"
#include "storm/utility/constants.h"
#include "storm/utility/solver.h"
namespace storm {
namespace analysis {
@ -26,34 +26,26 @@ namespace storm {
class MonotonicityChecker {
public:
/*!
* Constructor of MonotonicityChecker
* @param model the model considered
* @param formula the formula considered
* @param regions the regions to consider
* @param validate whether or not assumptions are to be validated
* @param numberOfSamples number of samples taken for monotonicity checking, default 0,
* if 0 then no check on samples is executed
* @param precision precision on which the samples are compared
*/
MonotonicityChecker(std::shared_ptr<storm::models::ModelBase> model, std::vector<std::shared_ptr<storm::logic::Formula const>> formulas, std::vector<storm::storage::ParameterRegion<ValueType>> regions, bool validate, uint_fast64_t numberOfSamples=0, double const& precision=0.000001);
/*!
* Checks for model and formula as provided in constructor for monotonicity
*/
std::map<storm::analysis::Order*, std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>>> checkMonotonicity(std::ostream& outfile);
typedef typename utility::parametric::VariableType<ValueType>::type VariableType;
typedef typename utility::parametric::CoefficientType<ValueType>::type CoefficientType;
typedef typename MonotonicityResult<VariableType>::Monotonicity Monotonicity;
typedef typename storage::ParameterRegion<ValueType> Region;
/*!
* Checks if monotonicity can be found in this order. Unordered states are not checked
* Constructs a new MonotonicityChecker object.
*
* @param matrix The Matrix of the model.
*/
bool somewhereMonotonicity(storm::analysis::Order* order) ;
MonotonicityChecker(storage::SparseMatrix<ValueType> matrix);
/*!
* Checks if a derivative >=0 or/and <=0
* @param derivative The derivative you want to check
* @return pair of bools, >= 0 and <= 0
* Checks if a derivative >=0 or/and <=0.
*
* @param derivative The derivative you want to check.
* @param reg The region of the parameters.
* @return Pair of bools, >= 0 and <= 0.
*/
static std::pair<bool, bool> checkDerivative(ValueType derivative, storm::storage::ParameterRegion<ValueType> reg) {
static std::pair<bool, bool> checkDerivative(ValueType derivative, storage::ParameterRegion<ValueType> reg) {
bool monIncr = false;
bool monDecr = false;
@ -64,86 +56,58 @@ namespace storm {
monIncr = derivative.constantPart() >= 0;
monDecr = derivative.constantPart() <= 0;
} else {
std::shared_ptr<utility::solver::SmtSolverFactory> smtSolverFactory = std::make_shared<utility::solver::MathsatSmtSolverFactory>();
std::shared_ptr<expressions::ExpressionManager> manager(new expressions::ExpressionManager());
solver::Z3SmtSolver s(*manager);
std::set<VariableType> variables = derivative.gatherVariables();
std::shared_ptr<storm::utility::solver::SmtSolverFactory> smtSolverFactory = std::make_shared<storm::utility::solver::MathsatSmtSolverFactory>();
std::shared_ptr<storm::expressions::ExpressionManager> manager(
new storm::expressions::ExpressionManager());
storm::solver::Z3SmtSolver s(*manager);
std::set<typename utility::parametric::VariableType<ValueType>::type> variables = derivative.gatherVariables();
expressions::Expression exprBounds = manager->boolean(true);
for (auto variable : variables) {
manager->declareRationalVariable(variable.name());
}
storm::expressions::Expression exprBounds = manager->boolean(true);
auto managervars = manager->getVariables();
for (auto var : managervars) {
auto lb = storm::utility::convertNumber<storm::RationalNumber>(reg.getLowerBoundary(var.getName()));
auto ub = storm::utility::convertNumber<storm::RationalNumber>(reg.getUpperBoundary(var.getName()));
exprBounds = exprBounds && manager->rational(lb) < var && var < manager->rational(ub);
auto managerVariable = manager->declareRationalVariable(variable.name());
auto lb = utility::convertNumber<RationalNumber>(reg.getLowerBoundary(variable));
auto ub = utility::convertNumber<RationalNumber>(reg.getUpperBoundary(variable));
exprBounds = exprBounds && manager->rational(lb) < managerVariable && managerVariable < manager->rational(ub);
}
assert (s.check() == storm::solver::SmtSolver::CheckResult::Sat);
auto converter = storm::expressions::RationalFunctionToExpression<ValueType>(manager);
auto converter = expressions::RationalFunctionToExpression<ValueType>(manager);
// < 0 so not monotone increasing
storm::expressions::Expression exprToCheck =
converter.toExpression(derivative) < manager->rational(0);
// < 0, so not monotone increasing. If this is unsat, then it should be monotone increasing.
expressions::Expression exprToCheck = converter.toExpression(derivative) < manager->rational(0);
s.add(exprBounds);
s.add(exprToCheck);
// If it is unsatisfiable then it should be monotone increasing
monIncr = s.check() == storm::solver::SmtSolver::CheckResult::Unsat;
// > 0 so not monotone decreasing
exprToCheck =
converter.toExpression(derivative) > manager->rational(0);
monIncr = s.check() == solver::SmtSolver::CheckResult::Unsat;
// > 0, so not monotone decreasing. If this is unsat it should be monotone decreasing.
exprToCheck = converter.toExpression(derivative) > manager->rational(0);
s.reset();
s.add(exprBounds);
assert (s.check() == storm::solver::SmtSolver::CheckResult::Sat);
s.add(exprToCheck);
monDecr = s.check() == storm::solver::SmtSolver::CheckResult::Unsat;
monDecr = s.check() == solver::SmtSolver::CheckResult::Unsat;
}
assert (!(monIncr && monDecr) || derivative.isZero());
return std::pair<bool, bool>(monIncr, monDecr);
}
private:
std::map<storm::analysis::Order*, std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>>> checkMonotonicity(std::ostream& outfile, std::map<storm::analysis::Order*, std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>>> map, storm::storage::SparseMatrix<ValueType> matrix);
std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>> analyseMonotonicity(uint_fast64_t i, Order* order, storm::storage::SparseMatrix<ValueType> matrix) ;
std::map<Order*, std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>>> createOrder();
std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>> checkOnSamples(std::shared_ptr<storm::models::sparse::Dtmc<ValueType>> model, uint_fast64_t numberOfSamples);
std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>> checkOnSamples(std::shared_ptr<storm::models::sparse::Mdp<ValueType>> model, uint_fast64_t numberOfSamples);
std::unordered_map<ValueType, std::unordered_map<typename utility::parametric::VariableType<ValueType>::type, ValueType>> derivatives;
ValueType getDerivative(ValueType function, typename utility::parametric::VariableType<ValueType>::type var);
std::map<Order*, std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>>> extendOrderWithAssumptions(Order* order, AssumptionMaker<ValueType>* assumptionMaker, uint_fast64_t val1, uint_fast64_t val2, std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>> assumptions);
std::shared_ptr<storm::models::ModelBase> model;
std::vector<std::shared_ptr<storm::logic::Formula const>> formulas;
bool validate;
bool checkSamples;
/*!
* Checks for local monotonicity at the given state.
*
* @param order The order on which the monotonicity should be checked.
* @param state The considerd state.
* @param var The variable in which we check for monotonicity.
* @param region The region on which we check the monotonicity.
* @return Incr, Decr, Constant, Unknown or Not
*/
Monotonicity checkLocalMonotonicity(std::shared_ptr<Order> const & order, uint_fast64_t state, VariableType const& var, storage::ParameterRegion<ValueType> const& region);
std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>> resultCheckOnSamples;
private:
Monotonicity checkTransitionMonRes(ValueType function, VariableType param, Region region);
OrderExtender<ValueType> *extender;
ValueType& getDerivative(ValueType function, VariableType var);
double precision;
storage::SparseMatrix<ValueType> matrix;
storm::storage::ParameterRegion<ValueType> region;
boost::container::flat_map<ValueType, boost::container::flat_map<VariableType, ValueType>> derivatives;
};
}
}

325
src/storm-pars/analysis/MonotonicityHelper.cpp

@ -0,0 +1,325 @@
#include "MonotonicityHelper.h"
#include "storm/exceptions/NotSupportedException.h"
#include "storm/exceptions/InvalidOperationException.h"
#include "storm/models/ModelType.h"
#include "storm/modelchecker/results/CheckResult.h"
#include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h"
#include "storm-pars/analysis/AssumptionChecker.h"
namespace storm {
namespace analysis {
/*** Constructor ***/
template <typename ValueType, typename ConstantType>
MonotonicityHelper<ValueType, ConstantType>::MonotonicityHelper(std::shared_ptr<models::sparse::Model<ValueType>> model, std::vector<std::shared_ptr<logic::Formula const>> formulas, std::vector<storage::ParameterRegion<ValueType>> regions, uint_fast64_t numberOfSamples, double const& precision, bool dotOutput) : assumptionMaker(model->getTransitionMatrix()){
assert (model != nullptr);
STORM_LOG_THROW(regions.size() <= 1, exceptions::NotSupportedException, "Monotonicity checking is not (yet) supported for multiple regions");
STORM_LOG_THROW(formulas.size() <= 1, exceptions::NotSupportedException, "Monotonicity checking is not (yet) supported for multiple formulas");
this->model = model;
this->formulas = formulas;
this->precision = utility::convertNumber<ConstantType>(precision);
this->matrix = model->getTransitionMatrix();
this->dotOutput = dotOutput;
if (regions.size() == 1) {
this->region = *(regions.begin());
} else {
typename storage::ParameterRegion<ValueType>::Valuation lowerBoundaries;
typename storage::ParameterRegion<ValueType>::Valuation upperBoundaries;
std::set<VariableType> vars;
vars = models::sparse::getProbabilityParameters(*model);
for (auto var : vars) {
typename storage::ParameterRegion<ValueType>::CoefficientType lb = utility::convertNumber<CoefficientType>(0 + precision) ;
typename storage::ParameterRegion<ValueType>::CoefficientType ub = utility::convertNumber<CoefficientType>(1 - precision) ;
lowerBoundaries.insert(std::make_pair(var, lb));
upperBoundaries.insert(std::make_pair(var, ub));
}
this->region = storage::ParameterRegion<ValueType>(std::move(lowerBoundaries), std::move(upperBoundaries));
}
if (numberOfSamples > 2) {
// sampling
if (model->isOfType(models::ModelType::Dtmc)) {
checkMonotonicityOnSamples(model->template as<models::sparse::Dtmc<ValueType>>(), numberOfSamples);
} else if (model->isOfType(models::ModelType::Mdp)) {
checkMonotonicityOnSamples(model->template as<models::sparse::Mdp<ValueType>>(), numberOfSamples);
}
checkSamples = true;
} else {
if (numberOfSamples > 0) {
STORM_LOG_WARN("At least 3 sample points are needed to check for monotonicity on samples, not using samples for now");
}
checkSamples = false;
}
this->extender = new analysis::OrderExtender<ValueType, ConstantType>(model, formulas[0]);
for (auto i = 0; i < matrix.getRowCount(); ++i) {
std::set<VariableType> occurringVariables;
for (auto &entry : matrix.getRow(i)) {
storm::utility::parametric::gatherOccurringVariables(entry.getValue(), occurringVariables);
}
for (auto& var : occurringVariables) {
occuringStatesAtVariable[var].push_back(i);
}
}
}
/*** Public methods ***/
template <typename ValueType, typename ConstantType>
std::map<std::shared_ptr<Order>, std::pair<std::shared_ptr<MonotonicityResult<typename MonotonicityHelper<ValueType, ConstantType>::VariableType>>, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>>> MonotonicityHelper<ValueType, ConstantType>::checkMonotonicityInBuild(std::ostream& outfile, bool usePLA, std::string dotOutfileName) {
if (usePLA) {
storm::utility::Stopwatch plaWatch(true);
this->extender->initializeMinMaxValues(region);
plaWatch.stop();
STORM_PRINT(std::endl << "Total time for pla checking: " << plaWatch << "." << std::endl << std::endl);
}
createOrder();
//output of results
for (auto itr : monResults) {
if (itr.first != nullptr) {
std::cout << "Number of done states: " << itr.first->getNumberOfDoneStates() << std::endl;
}
if (checkSamples) {
for (auto & entry : resultCheckOnSamples.getMonotonicityResult()) {
if (entry.second == Monotonicity::Not) {
itr.second.first->updateMonotonicityResult(entry.first, entry.second, true);
}
}
}
std::string temp = itr.second.first->toString();
bool first = true;
for (auto& assumption : itr.second.second) {
if (!first) {
outfile << " & ";
} else {
outfile << "Assumptions: " << std::endl << " ";
first = false;
}
outfile << *assumption;
}
if (!first) {
outfile << std::endl;
} else {
outfile << "No Assumptions" << std::endl;
}
outfile << "Monotonicity Result: " << std::endl << " " << temp << std::endl << std::endl;
}
if (monResults.size() == 0) {
outfile << "No monotonicity found, as the order is insufficient" << std::endl;
if (checkSamples) {
outfile << "Monotonicity Result on samples: " << resultCheckOnSamples.toString() << std::endl;
}
}
//dotoutput
if (dotOutput) {
STORM_LOG_WARN_COND(monResults.size() <= 10, "Too many Reachability Orders. Dot Output will only be created for 10.");
int i = 0;
auto orderItr = monResults.begin();
while (i < 10 && orderItr != monResults.end()) {
std::ofstream dotOutfile;
std::string name = dotOutfileName + std::to_string(i);
utility::openFile(name, dotOutfile);
dotOutfile << "Assumptions:" << std::endl;
auto assumptionItr = orderItr->second.second.begin();
while (assumptionItr != orderItr->second.second.end()) {
dotOutfile << *assumptionItr << std::endl;
dotOutfile << std::endl;
assumptionItr++;
}
dotOutfile << std::endl;
orderItr->first->dotOutputToFile(dotOutfile);
utility::closeFile(dotOutfile);
i++;
orderItr++;
}
}
return monResults;
}
/*** Private methods ***/
template <typename ValueType, typename ConstantType>
void MonotonicityHelper<ValueType, ConstantType>::createOrder() {
// Transform to Orders
std::tuple<std::shared_ptr<Order>, uint_fast64_t, uint_fast64_t> criticalTuple;
// Create initial order
auto monRes = std::make_shared<MonotonicityResult<VariableType>>(MonotonicityResult<VariableType>());
criticalTuple = extender->toOrder(region, monRes);
// Continue based on not (yet) sorted states
std::map<std::shared_ptr<Order>, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>> result;
auto val1 = std::get<1>(criticalTuple);
auto val2 = std::get<2>(criticalTuple);
auto numberOfStates = model->getNumberOfStates();
std::vector<std::shared_ptr<expressions::BinaryRelationExpression>> assumptions;
if (val1 == numberOfStates && val2 == numberOfStates) {
auto resAssumptionPair = std::pair<std::shared_ptr<MonotonicityResult<VariableType>>, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>>(monRes, assumptions);
monResults.insert(std::pair<std::shared_ptr<Order>, std::pair<std::shared_ptr<MonotonicityResult<VariableType>>, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>>>(std::get<0>(criticalTuple), resAssumptionPair));
} else if (val1 != numberOfStates && val2 != numberOfStates) {
extendOrderWithAssumptions(std::get<0>(criticalTuple), val1, val2, assumptions, monRes);
} else {
assert (false);
}
}
template <typename ValueType, typename ConstantType>
void MonotonicityHelper<ValueType, ConstantType>::extendOrderWithAssumptions(std::shared_ptr<Order> order, uint_fast64_t val1, uint_fast64_t val2, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>> assumptions, std::shared_ptr<MonotonicityResult<VariableType>> monRes) {
std::map<std::shared_ptr<Order>, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>> result;
if (order->isInvalid()) {
// We don't add anything as the order we created with assumptions turns out to be invalid
STORM_LOG_INFO(" The order was invalid, so we stop here");
return;
}
auto numberOfStates = model->getNumberOfStates();
if (val1 == numberOfStates || val2 == numberOfStates) {
assert (val1 == val2);
assert (order->getNumberOfAddedStates() == order->getNumberOfStates());
auto resAssumptionPair = std::pair<std::shared_ptr<MonotonicityResult<VariableType>>, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>>(monRes, assumptions);
monResults.insert(std::pair<std::shared_ptr<Order>, std::pair<std::shared_ptr<MonotonicityResult<VariableType>>, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>>>(std::move(order), std::move(resAssumptionPair)));
} else {
// Make the three assumptions
STORM_LOG_INFO("Creating assumptions for " << val1 << " and " << val2 << ". ");
auto newAssumptions = assumptionMaker.createAndCheckAssumptions(val1, val2, order, region);
assert (newAssumptions.size() <= 3);
auto itr = newAssumptions.begin();
if (newAssumptions.size() == 0) {
monRes = std::make_shared<MonotonicityResult<VariableType>>(MonotonicityResult<VariableType>());
for (auto& entry : occuringStatesAtVariable) {
for (auto & state : entry.second) {
extender->checkParOnStateMonRes(state, order, entry.first, monRes);
if (monRes->getMonotonicity(entry.first) == Monotonicity::Unknown) {
break;
}
}
monRes->setDoneForVar(entry.first);
}
monResults.insert({order, {monRes, assumptions}});
STORM_LOG_INFO(" None of the assumptions were valid, we stop exploring the current order");
} else {
STORM_LOG_INFO(" Created " << newAssumptions.size() << " assumptions, we continue extending the current order");
}
while (itr != newAssumptions.end()) {
auto assumption = *itr;
++itr;
if (assumption.second != AssumptionStatus::INVALID) {
if (itr != newAssumptions.end()) {
// We make a copy of the order and the assumptions
auto orderCopy = order->copy();
auto assumptionsCopy = std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>(assumptions);
auto monResCopy = monRes->copy();
if (assumption.second == AssumptionStatus::UNKNOWN) {
// only add assumption to the set of assumptions if it is unknown whether it holds or not
assumptionsCopy.push_back(std::move(assumption.first));
}
auto criticalTuple = extender->extendOrder(orderCopy, region, monResCopy, assumption.first);
extendOrderWithAssumptions(std::get<0>(criticalTuple), std::get<1>(criticalTuple), std::get<2>(criticalTuple), assumptionsCopy, monResCopy);
} else {
// It is the last one, so we don't need to create a copy.
if (assumption.second == AssumptionStatus::UNKNOWN) {
// only add assumption to the set of assumptions if it is unknown whether it holds or not
assumptions.push_back(std::move(assumption.first));
}
auto criticalTuple = extender->extendOrder(order, region, monRes, assumption.first);
extendOrderWithAssumptions(std::get<0>(criticalTuple), std::get<1>(criticalTuple), std::get<2>(criticalTuple), assumptions, monRes);
}
}
}
}
}
template <typename ValueType, typename ConstantType>
void MonotonicityHelper<ValueType, ConstantType>::checkMonotonicityOnSamples(std::shared_ptr<models::sparse::Dtmc<ValueType>> model, uint_fast64_t numberOfSamples) {
assert (numberOfSamples > 2);
auto instantiator = utility::ModelInstantiator<models::sparse::Dtmc<ValueType>, models::sparse::Dtmc<ConstantType>>(*model);
std::set<VariableType> variables = models::sparse::getProbabilityParameters(*model);
std::vector<std::vector<ConstantType>> samples;
// For each of the variables create a model in which we only change the value for this specific variable
for (auto itr = variables.begin(); itr != variables.end(); ++itr) {
ConstantType previous = -1;
bool monDecr = true;
bool monIncr = true;
// Check monotonicity in variable (*itr) by instantiating the model
// all other variables fixed on lb, only increasing (*itr)
for (uint_fast64_t i = 0; (monDecr || monIncr) && i < numberOfSamples; ++i) {
// Create valuation
auto valuation = utility::parametric::Valuation<ValueType>();
for (auto itr2 = variables.begin(); itr2 != variables.end(); ++itr2) {
// Only change value for current variable
if ((*itr) == (*itr2)) {
auto lb = region.getLowerBoundary(itr->name());
auto ub = region.getUpperBoundary(itr->name());
// Creates samples between lb and ub, that is: lb, lb + (ub-lb)/(#samples -1), lb + 2* (ub-lb)/(#samples -1), ..., ub
valuation[*itr2] = utility::convertNumber<typename utility::parametric::CoefficientType<ValueType>::type>(lb + i*(ub-lb)/(numberOfSamples-1));
} else {
auto lb = region.getLowerBoundary(itr2->name());
valuation[*itr2] = utility::convertNumber<typename utility::parametric::CoefficientType<ValueType>::type>(lb);
}
}
// Instantiate model and get result
models::sparse::Dtmc<ConstantType> sampleModel = instantiator.instantiate(valuation);
auto checker = modelchecker::SparseDtmcPrctlModelChecker<models::sparse::Dtmc<ConstantType>>(sampleModel);
std::unique_ptr<modelchecker::CheckResult> checkResult;
auto formula = formulas[0];
if (formula->isProbabilityOperatorFormula() && formula->asProbabilityOperatorFormula().getSubformula().isUntilFormula()) {
const modelchecker::CheckTask<logic::UntilFormula, ConstantType> checkTask = modelchecker::CheckTask<logic::UntilFormula, ConstantType>((*formula).asProbabilityOperatorFormula().getSubformula().asUntilFormula());
checkResult = checker.computeUntilProbabilities(Environment(), checkTask);
} else if (formula->isProbabilityOperatorFormula() && formula->asProbabilityOperatorFormula().getSubformula().isEventuallyFormula()) {
const modelchecker::CheckTask<logic::EventuallyFormula, ConstantType> checkTask = modelchecker::CheckTask<logic::EventuallyFormula, ConstantType>((*formula).asProbabilityOperatorFormula().getSubformula().asEventuallyFormula());
checkResult = checker.computeReachabilityProbabilities(Environment(), checkTask);
} else {
STORM_LOG_THROW(false, exceptions::NotSupportedException, "Expecting until or eventually formula");
}
auto quantitativeResult = checkResult->asExplicitQuantitativeCheckResult<ConstantType>();
std::vector<ConstantType> values = quantitativeResult.getValueVector();
auto initialStates = model->getInitialStates();
ConstantType initial = 0;
// Get total probability from initial states
for (auto j = initialStates.getNextSetIndex(0); j < model->getNumberOfStates(); j = initialStates.getNextSetIndex(j + 1)) {
initial += values[j];
}
// Calculate difference with result for previous valuation
assert (initial >= 0 - precision && initial <= 1 + precision);
ConstantType diff = previous - initial;
assert (previous == -1 || diff >= -1 - precision && diff <= 1 + precision);
if (previous != -1 && (diff > precision || diff < -precision)) {
monDecr &= diff > precision; // then previous value is larger than the current value from the initial states
monIncr &= diff < -precision;
}
previous = initial;
samples.push_back(std::move(values));
}
auto res = (!monIncr && !monDecr) ? MonotonicityResult<VariableType>::Monotonicity::Not : MonotonicityResult<VariableType>::Monotonicity::Unknown;
resultCheckOnSamples.addMonotonicityResult(*itr, res);
}
assumptionMaker.setSampleValues(std::move(samples));
}
template <typename ValueType, typename ConstantType>
void MonotonicityHelper<ValueType, ConstantType>::checkMonotonicityOnSamples(std::shared_ptr<models::sparse::Mdp<ValueType>> model, uint_fast64_t numberOfSamples) {
STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Checking monotonicity on samples not implemented for mdps");
}
template class MonotonicityHelper<RationalFunction, double>;
template class MonotonicityHelper<RationalFunction, RationalNumber>;
}
}

171
src/storm-pars/analysis/MonotonicityHelper.h

@ -0,0 +1,171 @@
#ifndef STORM_MONOTONICITYHELPER_H
#define STORM_MONOTONICITYHELPER_H
#include <map>
#include "Order.h"
#include "LocalMonotonicityResult.h"
#include "OrderExtender.h"
#include "AssumptionMaker.h"
#include "MonotonicityResult.h"
#include "storm/logic/Formula.h"
#include "storm/models/ModelBase.h"
#include "storm/models/sparse/Dtmc.h"
#include "storm/models/sparse/Mdp.h"
#include "storm/solver/Z3SmtSolver.h"
#include "storm/storage/SparseMatrix.h"
#include "storm/storage/expressions/BinaryRelationExpression.h"
#include "storm/storage/expressions/ExpressionManager.h"
#include "storm/storage/expressions/RationalFunctionToExpression.h"
#include "storm/utility/constants.h"
#include "storm-pars/api/region.h"
namespace storm {
namespace analysis {
template <typename ValueType, typename ConstantType>
class MonotonicityHelper {
public:
typedef typename utility::parametric::VariableType<ValueType>::type VariableType;
typedef typename utility::parametric::CoefficientType<ValueType>::type CoefficientType;
typedef typename MonotonicityResult<VariableType>::Monotonicity Monotonicity;
typedef typename storage::ParameterRegion<ValueType> Region;
/*!
* Constructor of MonotonicityHelper.
*
* @param model The model considered.
* @param formulas The formulas considered.
* @param regions The regions to consider.
* @param numberOfSamples Number of samples taken for monotonicity checking, default 0,
* if 0 then no check on samples is executed.
* @param precision Precision on which the samples are compared
* @param dotOutput Whether or not dot output should be generated for the ROs.
*/
MonotonicityHelper(std::shared_ptr<models::sparse::Model<ValueType>> model, std::vector<std::shared_ptr<logic::Formula const>> formulas, std::vector<storage::ParameterRegion<ValueType>> regions, uint_fast64_t numberOfSamples=0, double const& precision=0.000001, bool dotOutput = false);
/*!
* Checks if a derivative >=0 or/and <=0
*
* @param derivative The derivative you want to check
* @return pair of bools, >= 0 and <= 0
*/
static std::pair<bool, bool> checkDerivative(ValueType derivative, storage::ParameterRegion<ValueType> reg) {
bool monIncr = false;
bool monDecr = false;
if (derivative.isZero()) {
monIncr = true;
monDecr = true;
} else if (derivative.isConstant()) {
monIncr = derivative.constantPart() >= 0;
monDecr = derivative.constantPart() <= 0;
} else {
std::shared_ptr<utility::solver::SmtSolverFactory> smtSolverFactory = std::make_shared<utility::solver::MathsatSmtSolverFactory>();
std::shared_ptr<expressions::ExpressionManager> manager(new expressions::ExpressionManager());
solver::Z3SmtSolver s(*manager);
std::set<VariableType> variables = derivative.gatherVariables();
expressions::Expression exprBounds = manager->boolean(true);
for (auto variable : variables) {
auto managerVariable = manager->declareRationalVariable(variable.name());
auto lb = utility::convertNumber<RationalNumber>(reg.getLowerBoundary(variable));
auto ub = utility::convertNumber<RationalNumber>(reg.getUpperBoundary(variable));
exprBounds = exprBounds && manager->rational(lb) < managerVariable && managerVariable < manager->rational(ub);
}
auto converter = expressions::RationalFunctionToExpression<ValueType>(manager);
// < 0, so not monotone increasing. If this is unsat, then it should be monotone increasing.
expressions::Expression exprToCheck = converter.toExpression(derivative) < manager->rational(0);
s.add(exprBounds);
s.add(exprToCheck);
monIncr = s.check() == solver::SmtSolver::CheckResult::Unsat;
// > 0, so not monotone decreasing. If this is unsat it should be monotone decreasing.
exprToCheck = converter.toExpression(derivative) > manager->rational(0);
s.reset();
s.add(exprBounds);
s.add(exprToCheck);
monDecr = s.check() == solver::SmtSolver::CheckResult::Unsat;
}
assert (!(monIncr && monDecr) || derivative.isZero());
return std::pair<bool, bool>(monIncr, monDecr);
}
/*!
* Builds Reachability Orders for the given model and simultaneously uses them to check for Monotonicity.
*
* @param outfile Outfile to which results are written.
* @param dotOutfileName Name for the files of the dot outputs should they be generated
* @return Map which maps each order to its Reachability Order and used assumptions.
*/
std::map<std::shared_ptr<Order>, std::pair<std::shared_ptr<MonotonicityResult<VariableType>>, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>>> checkMonotonicityInBuild(std::ostream& outfile, bool usePLA = false, std::string dotOutfileName = "dotOutput");
/*!
* Checks for local monotonicity at the given state.
*
* @param order the order on which the monotonicity should be checked
* @param state the considerd state
* @param var the variable in which we check for monotonicity
* @param region the region on which we check the monotonicity
* @return Incr, Decr, Constant, Unknown or Not
*/
Monotonicity checkLocalMonotonicity(std::shared_ptr<Order> order, uint_fast64_t state, VariableType var, storage::ParameterRegion<ValueType> region);
private:
void createOrder();
void checkMonotonicityOnSamples(std::shared_ptr<models::sparse::Dtmc<ValueType>> model, uint_fast64_t numberOfSamples);
void checkMonotonicityOnSamples(std::shared_ptr<models::sparse::Mdp<ValueType>> model, uint_fast64_t numberOfSamples);
void extendOrderWithAssumptions(std::shared_ptr<Order> order, uint_fast64_t val1, uint_fast64_t val2, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>> assumptions, std::shared_ptr<MonotonicityResult<VariableType>> monRes);
Monotonicity checkTransitionMonRes(ValueType function, VariableType param, Region region);
ValueType getDerivative(ValueType function, VariableType var);
std::shared_ptr<models::ModelBase> model;
std::vector<std::shared_ptr<logic::Formula const>> formulas;
bool dotOutput;
bool checkSamples;
bool onlyCheckOnOrder;
MonotonicityResult<VariableType> resultCheckOnSamples;
std::map<VariableType, std::vector<uint_fast64_t>> occuringStatesAtVariable;
std::map<std::shared_ptr<Order>, std::pair<std::shared_ptr<MonotonicityResult<VariableType>>, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>>> monResults;
OrderExtender<ValueType, ConstantType> *extender;
ConstantType precision;
Region region;
storage::SparseMatrix<ValueType> matrix;
std::unordered_map<ValueType, std::unordered_map<VariableType, ValueType>> derivatives;
AssumptionMaker<ValueType, ConstantType> assumptionMaker;
};
}
}
#endif //STORM_MONOTONICITYHELPER_H

231
src/storm-pars/analysis/MonotonicityResult.cpp

@ -0,0 +1,231 @@
#include "MonotonicityResult.h"
#include "storm/utility/macros.h"
#include "storm/exceptions/NotImplementedException.h"
#include "storm/models/sparse/Dtmc.h"
#include "storm/models/sparse/Mdp.h"
namespace storm {
namespace analysis {
template <typename VariableType>
MonotonicityResult<VariableType>::MonotonicityResult() {
this->done = false;
this->somewhereMonotonicity = true;
this->allMonotonicity = true;
}
template <typename VariableType>
void MonotonicityResult<VariableType>::addMonotonicityResult(VariableType var, MonotonicityResult<VariableType>::Monotonicity mon) {
monotonicityResult.insert(std::pair<VariableType, MonotonicityResult<VariableType>::Monotonicity>(std::move(var), std::move(mon)));
}
template <typename VariableType>
void MonotonicityResult<VariableType>::updateMonotonicityResult(VariableType var, MonotonicityResult<VariableType>::Monotonicity mon, bool force) {
assert (!isDoneForVar(var));
if (force) {
assert (mon == MonotonicityResult<VariableType>::Monotonicity::Not);
if (monotonicityResult.find(var) == monotonicityResult.end()) {
addMonotonicityResult(std::move(var), std::move(mon));
} else {
monotonicityResult[var] = mon;
}
} else {
if (mon == MonotonicityResult<VariableType>::Monotonicity::Not) {
mon = MonotonicityResult<VariableType>::Monotonicity::Unknown;
}
if (monotonicityResult.find(var) == monotonicityResult.end()) {
addMonotonicityResult(std::move(var), std::move(mon));
} else {
auto monRes = monotonicityResult[var];
if (monRes == MonotonicityResult<VariableType>::Monotonicity::Unknown || monRes == mon ||
mon == MonotonicityResult<VariableType>::Monotonicity::Constant) {
return;
} else if (mon == MonotonicityResult<VariableType>::Monotonicity::Unknown ||
monRes == MonotonicityResult<VariableType>::Monotonicity::Constant) {
monotonicityResult[var] = mon;
} else {
monotonicityResult[var] = MonotonicityResult<VariableType>::Monotonicity::Unknown;
}
}
if (monotonicityResult[var] == MonotonicityResult<VariableType>::Monotonicity::Unknown) {
setAllMonotonicity(false);
setSomewhereMonotonicity(false);
} else {
setSomewhereMonotonicity(true);
}
}
}
template <typename VariableType>
typename MonotonicityResult<VariableType>::Monotonicity MonotonicityResult<VariableType>::getMonotonicity(VariableType var) const {
auto itr = monotonicityResult.find(var);
if (itr != monotonicityResult.end()) {
return itr->second;
}
return Monotonicity::Unknown;
}
template <typename VariableType>
std::map<VariableType, typename MonotonicityResult<VariableType>::Monotonicity> MonotonicityResult<VariableType>::getMonotonicityResult() const {
return monotonicityResult;
}
template <typename VariableType>
std::pair<std::set<VariableType>, std::set<VariableType>> MonotonicityResult<VariableType>::splitVariables(std::set<VariableType> const& consideredVariables) const {
std::set<VariableType> nonMonotoneVariables;
std::set<VariableType> monotoneVariables;
for (auto var : consideredVariables) {
if (isDoneForVar(var)) {
auto res = getMonotonicity(var);
if (res == Monotonicity::Not || res == Monotonicity::Unknown) {
nonMonotoneVariables.insert(var);
} else {
monotoneVariables.insert(var);
}
} else {
nonMonotoneVariables.insert(var);
}
}
return std::make_pair(std::move(monotoneVariables), std::move(nonMonotoneVariables));
}
template <typename VariableType>
std::string MonotonicityResult<VariableType>::toString() const {
std::string result;
auto countIncr = 0;
auto countDecr = 0;
for (auto res : getMonotonicityResult()) {
result += res.first.name();
switch (res.second) {
case MonotonicityResult<VariableType>::Monotonicity::Incr:
countIncr++;
result += " MonIncr; ";
break;
case MonotonicityResult<VariableType>::Monotonicity::Decr:
countDecr++;
result += " MonDecr; ";
break;
case MonotonicityResult<VariableType>::Monotonicity::Constant:
result += " Constant; ";
break;
case MonotonicityResult<VariableType>::Monotonicity::Not:
result += " NotMon; ";
break;
case MonotonicityResult<VariableType>::Monotonicity::Unknown:
result += " Unknown; ";
break;
default:
STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Could not get a string from the region monotonicity check result. The case has not been implemented");
}
}
result = "#Incr: " + std::to_string(countIncr) + " #Decr: " + std::to_string(countDecr) + "\n" + result;
return result;
}
template <typename VariableType>
void MonotonicityResult<VariableType>::setDone(bool done) {
this->done = done;
}
template <typename VariableType>
void MonotonicityResult<VariableType>::setDoneForVar(VariableType variable) {
doneVariables.insert(variable);
}
template <typename VariableType>
bool MonotonicityResult<VariableType>::isDone() const {
return done;
}
template <typename VariableType>
bool MonotonicityResult<VariableType>::isDoneForVar(VariableType var) const {
return doneVariables.find(var) != doneVariables.end();
}
template <typename VariableType>
void MonotonicityResult<VariableType>::setSomewhereMonotonicity(bool somewhereMonotonicity) {
this->somewhereMonotonicity = somewhereMonotonicity;
}
template <typename VariableType>
bool MonotonicityResult<VariableType>::existsMonotonicity() {
if (!somewhereMonotonicity) {
for (auto itr : monotonicityResult) {
if (isDoneForVar(itr.first) && itr.second != MonotonicityResult<VariableType>::Monotonicity::Unknown && itr.second != MonotonicityResult<VariableType>::Monotonicity::Not) {
setSomewhereMonotonicity(true);
break;
}
}
}
return monotonicityResult.size() > 0 && somewhereMonotonicity;
}
template <typename VariableType>
void MonotonicityResult<VariableType>::setAllMonotonicity(bool allMonotonicity) {
this->allMonotonicity = allMonotonicity;
}
template <typename VariableType>
bool MonotonicityResult<VariableType>::isAllMonotonicity() const {
return allMonotonicity;
}
template <typename VariableType>
std::shared_ptr<MonotonicityResult<VariableType>> MonotonicityResult<VariableType>::copy() const {
std::shared_ptr<MonotonicityResult<VariableType>> copy = std::make_shared<MonotonicityResult<VariableType>>();
copy->monotonicityResult = std::map<VariableType, Monotonicity>(monotonicityResult);
copy->setAllMonotonicity(allMonotonicity);
copy->setSomewhereMonotonicity(somewhereMonotonicity);
copy->setDone(done);
copy->setDoneVariables(doneVariables);
return copy;
}
template<typename VariableType>
void MonotonicityResult<VariableType>::setDoneVariables(std::set<VariableType> doneVariables) {
this->doneVariables = doneVariables;
}
template<typename VariableType>
void
MonotonicityResult<VariableType>::splitBasedOnMonotonicity(const std::set<VariableType> &consideredVariables,
std::set<VariableType> & monotoneIncr,
std::set<VariableType> & monotoneDecr,
std::set<VariableType> & notMonotone) const {
for (auto& var : consideredVariables) {
if (!isDoneForVar(var)) {
notMonotone.insert(var);
} else {
auto mon = getMonotonicity(var);
if (mon == Monotonicity::Unknown || mon == Monotonicity::Not) {
notMonotone.insert(var);
} else if (mon == Monotonicity::Incr) {
monotoneIncr.insert(var);
} else {
monotoneDecr.insert(var);
}
}
}
}
template<typename VariableType>
bool MonotonicityResult<VariableType>::isMonotone(VariableType var) const {
if (monotonicityResult.find(var) == monotonicityResult.end()) {
return false;
} else {
auto monRes = monotonicityResult.at(var);
return isDoneForVar(var) && (monRes == Monotonicity::Incr
|| monRes == Monotonicity::Decr
|| monRes == Monotonicity::Constant);
}
}
template class MonotonicityResult<storm::RationalFunctionVariable>;
}
}

130
src/storm-pars/analysis/MonotonicityResult.h

@ -0,0 +1,130 @@
#pragma once
#include <ostream>
#include <map>
#include <set>
#include <memory>
#include <storm/storage/BitVector.h>
namespace storm {
namespace analysis {
template <typename VariableType>
class MonotonicityResult {
public:
/*!
* The results of monotonicity checking for a single Parameter Region
*/
enum class Monotonicity {
Incr, /*!< the result is monotonically increasing */
Decr, /*!< the result is monotonically decreasing */
Constant, /*!< the result is constant */
Not, /*!< the result is not monotonic */
Unknown /*!< the monotonicity result is unknown */
};
/*!
* Constructs a new MonotonicityResult object.
*/
MonotonicityResult();
/*!
* Adds a new variable with a given Monotonicity to the map.
*
* @param var The variable that is to be added.
* @param mon The Monotonicity of the variable.
*/
void addMonotonicityResult(VariableType var, Monotonicity mon);
/*!
* Updates the Monotonicity of a variable based on its value so far and a new value.
*
* @param var The variable.
* @param mon The new Monotonicity to be considered.
*/
void updateMonotonicityResult(VariableType var, Monotonicity mon, bool force = false);
/*!
* Returns the current monotonicity of a given parameter.
*
* @param var The parameter.
* @return Incr, Decr, Constant, Not or Unknown.
*/
Monotonicity getMonotonicity(VariableType var) const;
bool isMonotone(VariableType var) const;
/*!
* Returns the results so far.
*
* @return The parameter / Monotonicity map
*/
std::map<VariableType, Monotonicity> getMonotonicityResult() const;
void splitBasedOnMonotonicity(std::set<VariableType> const& consideredVariables, std::set<VariableType>& monotoneIncr, std::set<VariableType>& monotoneDecr, std::set<VariableType> & notMontone) const;
std::pair<std::set<VariableType>, std::set<VariableType>> splitVariables(std::set<VariableType> const& consideredVariables) const;
/*!
* Constructs a string output of all variables and their corresponding Monotonicity
*
* @return Results so far
*/
std::string toString() const;
/*!
* Checks if the result is complete
*/
bool isDone() const;
bool isDoneForVar(VariableType) const;
/*!
* Checks if there is any variable that is monotone
*/
bool existsMonotonicity();
/*!
* Returns if all Variables are monotone
*/
bool isAllMonotonicity() const;
/*!
* Sets the done bool to the given truth value
*/
void setDone(bool done = true);
/*!
* Sets the somewhereMonotonicity bool to the given truth value
*/
void setSomewhereMonotonicity(bool done = true);
/*!
* Sets the allMonotonicity bool to the given truth value
*/
void setAllMonotonicity(bool done = true);
void setDoneForVar(VariableType);
void setDoneVariables(std::set<VariableType> doneVariables);
/*!
* Constructs a new MonotonicityResult object that is a copy of the current one
*
* @return Pointer to the copy
*/
std::shared_ptr<MonotonicityResult<VariableType>> copy() const;
private:
std::map<VariableType, Monotonicity> monotonicityResult;
std::set<VariableType> doneVariables;
bool done;
bool somewhereMonotonicity;
bool allMonotonicity;
};
}
}

912
src/storm-pars/analysis/Order.cpp
File diff suppressed because it is too large
View File

324
src/storm-pars/analysis/Order.h

@ -1,22 +1,22 @@
#ifndef ORDER_ORDER_H
#define ORDER_ORDER_H
#include <boost/container/flat_set.hpp>
#include <iostream>
#include <set>
#include <vector>
#include <unordered_map>
#include <boost/container/flat_set.hpp>
#include <memory>
#include "storm/storage/BitVector.h"
#include "storm/storage/StronglyConnectedComponent.h"
namespace storm {
namespace analysis {
class Order {
public:
/*!
* Constants for comparison of nodes/states
* Constants for comparison of nodes/states.
*/
enum NodeComparison {
UNKNOWN,
@ -24,127 +24,177 @@ namespace storm {
ABOVE,
SAME,
};
/*!
* Nodes of the Reachability Order. Contains all states with the same reachability.
*/
struct Node {
boost::container::flat_set<uint_fast64_t> states;
storm::storage::BitVector statesAbove;
};
/*!
* Constructs an order with the given top node and bottom node.
* Constructs an order with the given top and bottom states.
*
* @param topNode The top node of the resulting order.
* @param bottomNode The bottom node of the resulting order.
* @param topStates A Bitvector with the top states of the resulting order.
* @param bottomStates A Bitvector with the bottom states of the resulting order.
* @param numberOfStates Maximum number of states in order.
* @param statesSorted Pointer to a vector which contains the states which still need to added to the order.
*/
Order(storm::storage::BitVector* topStates,
storm::storage::BitVector* bottomStates,
storm::storage::BitVector* initialMiddleStates,
uint_fast64_t numberOfStates,
std::vector<uint_fast64_t>* statesSorted);
Order(storm::storage::BitVector* topStates, storm::storage::BitVector* bottomStates, uint_fast64_t numberOfStates, storage::Decomposition<storage::StronglyConnectedComponent> sccsSorted, std::vector<uint_fast64_t> statesSorted);
/*!
* Constructs an order with the given top state and bottom state.
*
* @param top The top state of the resulting order.
* @param bottom The bottom state of the resulting order.
* @param numberOfStates Max number of states in order.
* @param numberOfStates Maximum number of states in order.
* @param statesSorted Pointer to a vector which contains the states which still need to added to the order.
*/
Order(uint_fast64_t top,
uint_fast64_t bottom,
uint_fast64_t numberOfStates,
std::vector<uint_fast64_t>* statesSorted);
Order(uint_fast64_t top, uint_fast64_t bottom, uint_fast64_t numberOfStates, storage::Decomposition<storage::StronglyConnectedComponent> sccsSorted, std::vector<uint_fast64_t> statesSorted);
/*!
* Constructs a copy of the given order.
* Constructs a new Order.
*/
Order();
/*!
* Adds state between the top and bottom node of the order.
*
* @param order The original order.
* @param state The given state.
*/
Order(Order* order);
void add(uint_fast64_t state);
/*!
* Adds a node with the given state above the given node.
*
* @param state The state which is added.
* @param node The pointer to the node above which the state is added, should not be nullptr.
*/
void addAbove(uint_fast64_t state, Node *node);
/*!
* Adds a node with the given state below the given node.
*
* @param state The state which is added.
* @param node The pointer to the node below which the state is added, should not be nullptr.
*/
void addBelow(uint_fast64_t state, Node *node);
/*!
* Adds a node with the given state below node1 and above node2.
*
* @param state The given state.
* @param node1 The pointer to the node below which a new node (with state) is added
* @param node2 The pointer to the node above which a new node (with state) is added
* @param node1 The pointer to the node below which a new node (with state) is added.
* @param node2 The pointer to the node above which a new node (with state) is added.
*/
void addBetween(uint_fast64_t state, Node *node1, Node *node2);
/*!
* Adds a node with the given state between the nodes of below and above.
* Result: below -> state -> above
*
* @param state The given state.
* @param above The state number of the state below which a new node (with state) is added
* @param below The state number of the state above which a new node (with state) is added
* @param above The state number of the state below which a new node (with state) is added.
* @param below The state number of the state above which a new node (with state) is added.
*/
void addBetween(uint_fast64_t state, uint_fast64_t above, uint_fast64_t below);
/*!
* Adds a new relation between two nodes to the order.
*
* @param above The node closest to the top Node of the Order.
* @param below The node closest to the bottom Node of the Order.
*/
void addRelationNodes(storm::analysis::Order::Node *above, storm::analysis::Order::Node * below, bool allowMerge = false);
/*!
* Adds a new relation between two states to the order.
*
* @param above The state closest to the top Node of the Order.
* @param below The state closest to the bottom Node of the Order.
*/
void addRelation(uint_fast64_t above, uint_fast64_t below, bool allowMerge = false);
/*!
* Adds state to the states of the given node.
*
* @param state The state which is added.
* @param node The pointer to the node to which state is added, should not be nullptr.
*/
void addToNode(uint_fast64_t state, Node *node);
/*!
* Adds state between the top and bottom node of the order
* @param state The given state
* Merges node2 into node1.
* @return false when merging leads to invalid order
*/
void add(uint_fast64_t state);
bool mergeNodes(Node* node1, Node* node2);
/*!
* Adds a new relation between two nodes to the order
* @param above The node closest to the top Node of the Order.
* @param below The node closest to the bottom Node of the Order.
* Merges node of var2 into node of var1.
* @return false when merging leads to invalid order
*/
void addRelationNodes(storm::analysis::Order::Node *above, storm::analysis::Order::Node * below);
bool merge(uint_fast64_t var1, uint_fast64_t var2);
/*!
* Adds a new relation between two states to the order
* @param above The state closest to the top Node of the Order.
* @param below The state closest to the bottom Node of the Order.
*/
void addRelation(uint_fast64_t above, uint_fast64_t below);
* Compares the level of the nodes of the states.
* Behaviour unknown when one or more of the states does not occur at any Node in the Order.
*
* @param State1 the first state.
* @param State2 the second state.
* @return SAME if the nodes are on the same level;
* ABOVE if the node of the first state is closer to top than the node of the second state;
* BELOW if the node of the second state is closer to top than the node of the first state;
* UNKNOWN if it is unclear from the structure of the order how the nodes relate.
*/
Order::NodeComparison compare(uint_fast64_t state1, uint_fast64_t state2, NodeComparison hypothesis = UNKNOWN);
Order::NodeComparison compareFast(uint_fast64_t state1, uint_fast64_t state2, NodeComparison hypothesis = UNKNOWN) const;
/*!
* Compares the level of the nodes of the states.
* Behaviour unknown when one or more of the states doesnot occur at any Node in the Order.
* @param state1 The first state.
* @param state2 The second state.
* Compares the level of the two nodes.
*
* @param node1 The first node.
* @param node2 The second node.
* @return SAME if the nodes are on the same level;
* ABOVE if the node of the first state is closer to top then the node of the second state;
* BELOW if the node of the second state is closer to top then the node of the first state;
* UNKNOWN if it is unclear from the structure of the order how the nodes relate.
* ABOVE if node1 is closer to top than node2;
* BELOW if node2 is closer to top than node1;
* UNKNOWN if it is unclear from the structure of the order how the nodes relate.
*/
Order::NodeComparison compare(uint_fast64_t state1, uint_fast64_t state2);
NodeComparison compare(Node* node1, Node* node2, NodeComparison hypothesis = UNKNOWN);
NodeComparison compareFast(Node* node1, Node* node2, NodeComparison hypothesis = UNKNOWN) const;
/*!
* Check if state is already in order
* @param state
* @return
* Check if state is already contained in order.
*/
bool contains(uint_fast64_t state);
bool contains(uint_fast64_t state) const;
/*!
* Retrieves the pointer to a Node at which the state occurs.
*
* @param state The number of the state.
* Retrieves the bottom node of the order.
*
* @return The pointer to the node of the state, nullptr if the node does not exist.
* @return The bottom node.
*/
Node *getNode(uint_fast64_t state);
Node* getBottom() const;
/*!
* Retrieves the top node of the order.
*
* @return The top node.
* Returns true if done building the order.
*/
Node* getTop();
bool getDoneBuilding() const;
/*!
* Retrieves the bottom node of the order.
* Returns the next done state of the order, returns the number of state if end of done states is reached.
*/
uint_fast64_t getNextDoneState(uint_fast64_t state) const;
uint_fast64_t getNumberOfDoneStates() const;
/*!
* Retrieves the pointer to a Node at which the state occurs.
*
* @return The bottom node.
* @param state The number of the state.
* @return The pointer to the node of the state, nullptr if the node does not exist.
*/
Node* getBottom();
Node *getNode(uint_fast64_t state) const;
/*!
* Returns the vector with the nodes of the order.
@ -153,82 +203,151 @@ namespace storm {
*
* @return The vector with nodes of the order.
*/
std::vector<Node*> getNodes();
std::vector<Node*> getNodes() const;
std::vector<uint_fast64_t>& getStatesSorted();
/*!
* Returns a BitVector in which all added states are set.
* Retrieves the top node of the order.
*
* @return The BitVector with all added states.
* @return The top node.
*/
storm::storage::BitVector* getAddedStates();
Node* getTop() const;
/*!
* Returns true if done building the order.
* @return
* Returns the number of added states.
*/
bool getDoneBuilding();
uint_fast64_t getNumberOfAddedStates() const;
/*!
* Compares two nodes in the order
* @param node1
* @param node2
* @return BELOW, ABOVE, SAME or UNKNOWN
* Returns the number of possible states in the order.
*/
NodeComparison compare(Node* node1, Node* node2);
uint_fast64_t getNumberOfStates() const;
/*!
* Sorts the given stats if possible.
* Checks if the given state is a bottom state.
*/
bool isBottomState(uint_fast64_t) const;
/*!
* Checks if the given state is a top state.
*/
bool isTopState(uint_fast64_t) const;
/*!
* Returns if the order only consists of bottom and top states (so no in-between nodes).
*/
bool isOnlyBottomTopOrder() const;
/*!
* Sorts the given states if possible.
*
* @param states Bitvector of the states to sort
* @param states Vector of the states to be sorted.
* @return Vector with states sorted, length equals number of states to sort.
* If states cannot be sorted, last state of the vector will always equal the length of the BitVector
* If states cannot be sorted, last state of the vector will always equal the length of the BitVector.
*/
std::vector<uint_fast64_t> sortStates(storm::storage::BitVector* states);
std::vector<uint_fast64_t> sortStates(std::vector<uint_fast64_t>* states);
std::pair<bool, bool> allAboveBelow(std::vector<uint_fast64_t>const states, uint_fast64_t state);
/*!
* If the order is fully build, this can be set to true.
* Sorts the given states if possible.
*
* @param states Vector of the states to be sorted.
* @return s1, s2, vector
* if s1 == numberOfSTates, all states could be sorted including current
* if s1 < numberOfStates && s2 == numberOfStates, all states excluding s1 could be sorted, forward reasonging can be continued
* else assumption is needed
*/
void setDoneBuilding(bool done);
std::pair<std::pair<uint_fast64_t,uint_fast64_t>, std::vector<uint_fast64_t>> sortStatesForForward(uint_fast64_t currentState, std::vector<uint_fast64_t> const& successors);
/*!
* Prints a string representation of the order to the output stream.
* Sorts the given states if possible.
*
* @param out The stream to output to.
* @param states Vector of the states to be sorted.
* @return pair of unsortabe states, vector with states sorted (so far).
* If all states could be sorted, both values of the pair are numberOfStates and the vectors length will equal the number of states to sort.
*/
void toString(std::ostream &out);
std::pair<std::pair<uint_fast64_t ,uint_fast64_t>,std::vector<uint_fast64_t>> sortStatesUnorderedPair(const std::vector<uint_fast64_t>* states);
/*!
* Merges node2 into node1
* @param node1
* @param node2
* Sorts the given states if possible.
*
* @param states Bitvector of the states to be sorted.
* @return vector with states sorted, length equals number of states to sort.
* If states cannot be sorted, last state of the vector will always equal the length of the BitVector.
*/
void mergeNodes(Node* node1, Node* node2);
std::vector<uint_fast64_t> sortStates(storm::storage::BitVector* states);
bool isTrivial(uint_fast64_t state);
std::pair<uint_fast64_t, bool> getNextStateNumber();
// bool existsNextSCC();
bool existsNextState();
bool existsStateToHandle();
std::pair<uint_fast64_t, bool> getStateToHandle();
void addStateToHandle(uint_fast64_t state);
void addStateSorted(uint_fast64_t state);
/*!
* Merges node of var2 into node of var1
* @param var1
* @param var2
* If the order is fully built, this can be set to true.
*/
void merge(uint_fast64_t var1, uint_fast64_t var2);
storm::storage::BitVector* statesToHandle;
void setDoneBuilding(bool done = true);
uint_fast64_t getNextSortedState();
/*!
* Prints the dot output to normal STORM_PRINT.
*/
void toDotOutput() const;
bool containsStatesSorted(uint_fast64_t state);
/*!
* Writes dotoutput to the given file.
*
* @param dotOutfile
*/
void dotOutputToFile(std::ofstream& dotOutfile) const;
void removeFirstStatesSorted();
/*!
* Creates a copy of the calling Order.
*
* @return Pointer to the copy.
*/
std::shared_ptr<Order> copy() const;
// void setAddedSCC(uint_fast64_t sccNumber);
void setDoneState(uint_fast64_t sccNumber);
void removeStatesSorted(uint_fast64_t state);
bool isInvalid() const;
protected:
std::vector<uint_fast64_t> getStatesSorted();
storage::Decomposition<storage::StronglyConnectedComponent> getDecomposition() const;
private:
std::vector<Node*> nodes;
bool above(Node * node1, Node * node2);
std::vector<uint_fast64_t> statesSorted;
bool above(Node * node1, Node * node2, storm::analysis::Order::Node *nodePrev, storm::storage::BitVector *statesSeen);
bool aboveFast(Node * node1, Node * node2) const;
void init(uint_fast64_t numberOfStates, storage::Decomposition<storage::StronglyConnectedComponent>, bool doneBuilding = false);
storm::storage::BitVector* addedStates;
std::string nodeName(Node n) const;
std::string nodeLabel(Node n) const;
bool invalid;
void nodeOutput();
bool doneBuilding;
bool onlyBottomTopOrder;
storm::storage::BitVector doneStates;
storm::storage::BitVector trivialStates;
std::vector<Node*> nodes;
std::vector<uint_fast64_t> statesToHandle;
Node* top;
@ -236,11 +355,10 @@ namespace storm {
uint_fast64_t numberOfStates;
bool above(Node * node1, Node * node2);
uint_fast64_t numberOfAddedStates;
bool above(Node * node1, Node * node2, storm::analysis::Order::Node *nodePrev, storm::storage::BitVector *statesSeen);
std::vector<uint_fast64_t> statesSorted;
bool doneBuilding;
};
}
}

1104
src/storm-pars/analysis/OrderExtender.cpp
File diff suppressed because it is too large
View File

129
src/storm-pars/analysis/OrderExtender.h

@ -1,81 +1,132 @@
#ifndef STORM_LATTICEEXTENDER_H
#define STORM_LATTICEEXTENDER_H
#ifndef STORM_ORDEREXTENDER_H
#define STORM_ORDEREXTENDER_H
#include <storm/logic/Formula.h>
#include "storm/models/sparse/Dtmc.h"
#include "storm-pars/analysis/Order.h"
#include <boost/container/flat_set.hpp>
#include "storm/api/storm.h"
#include "storm-pars/storage/ParameterRegion.h"
#include "storm/storage/StronglyConnectedComponentDecomposition.h"
#include "storm/storage/StronglyConnectedComponent.h"
#include "storm/logic/Formula.h"
#include "storm/models/sparse/Model.h"
#include "storm/storage/expressions/BinaryRelationExpression.h"
#include "storm/storage/expressions/VariableExpression.h"
#include "storm-pars/analysis/Order.h"
#include "storm-pars/analysis/MonotonicityResult.h"
#include "storm-pars/analysis/MonotonicityChecker.h"
#include "storm-pars/storage/ParameterRegion.h"
#include "AssumptionMaker.h"
namespace storm {
namespace analysis {
template<typename ValueType>
template<typename ValueType, typename ConstantType>
class OrderExtender {
public:
typedef typename utility::parametric::CoefficientType<ValueType>::type CoefficientType;
typedef typename utility::parametric::VariableType<ValueType>::type VariableType;
typedef typename MonotonicityResult<VariableType>::Monotonicity Monotonicity;
/*!
* Constructs a new OrderExtender.
*
* @param model The model for which the order should be extended.
* @param formula The considered formula.
* @param region The Region of the model's parameters.
*/
OrderExtender(std::shared_ptr<models::sparse::Model<ValueType>> model, std::shared_ptr<logic::Formula const> formula);
/*!
* Constructs OrderExtender which can extend an order
* Constructs a new OrderExtender.
*
* @param model The model for which the order should be extended.
* @param topStates The top states of the order.
* @param bottomStates The bottom states of the order.
* @param matrix The matrix of the considered model.
*/
OrderExtender(std::shared_ptr<storm::models::sparse::Model<ValueType>> model);
OrderExtender(storm::storage::BitVector* topStates, storm::storage::BitVector* bottomStates, storm::storage::SparseMatrix<ValueType> matrix);
/*!
* Creates an order based on the given formula.
*
* @param formulas The formulas based on which the order is created, only the first is used.
* @param monRes The monotonicity result so far.
* @return A triple with a pointer to the order and two states of which the current place in the order
* is unknown but needed. When the states have as number the number of states, no states are
* unplaced but needed.
*/
std::tuple<storm::analysis::Order*, uint_fast64_t, uint_fast64_t> toOrder(std::vector<std::shared_ptr<storm::logic::Formula const>> formulas);
std::tuple<std::shared_ptr<Order>, uint_fast64_t, uint_fast64_t> toOrder(storage::ParameterRegion<ValueType> region, std::shared_ptr<MonotonicityResult<VariableType>> monRes = nullptr);
/*!
* Creates an order based on the given extremal values.
* Extends the order for the given region.
*
* @return A triple with a pointer to the order and two states of which the current place in the order
* @param order pointer to the order.
* @param region The region on which the order needs to be extended.
* @return Two states of which the current place in the order
* is unknown but needed. When the states have as number the number of states, no states are
* unplaced but needed.
* unplaced or needed.
*/
std::tuple<storm::analysis::Order*, uint_fast64_t, uint_fast64_t> toOrder(std::vector<std::shared_ptr<storm::logic::Formula const>> formulas, std::vector<double> minValues, std::vector<double> maxValues);
std::tuple<std::shared_ptr<Order>, uint_fast64_t, uint_fast64_t> extendOrder(std::shared_ptr<Order> order, storm::storage::ParameterRegion<ValueType> region, std::shared_ptr<MonotonicityResult<VariableType>> monRes = nullptr, std::shared_ptr<expressions::BinaryRelationExpression> assumption = nullptr);
void setMinMaxValues(std::shared_ptr<Order> order, std::vector<ConstantType> &minValues, std::vector<ConstantType> &maxValues);
void setMinValues(std::shared_ptr<Order> order, std::vector<ConstantType> &minValues);
void setMaxValues(std::shared_ptr<Order> order,std::vector<ConstantType> &maxValues);
void setMinValuesInit(std::vector<ConstantType> &minValues);
void setMaxValuesInit(std::vector<ConstantType> &minValues);
/*!
* Extends the order based on the given assumption.
*
* @param order The order.
* @param assumption The assumption on states.
* @return A triple with a pointer to the order and two states of which the current place in the order
* is unknown but needed. When the states have as number the number of states, no states are
* unplaced but needed.
*/
std::tuple<storm::analysis::Order*, uint_fast64_t, uint_fast64_t> extendOrder(storm::analysis::Order* order, std::shared_ptr<storm::expressions::BinaryRelationExpression> assumption = nullptr);
void setUnknownStates(std::shared_ptr<Order> order, uint_fast64_t state1, uint_fast64_t state2);
std::pair<uint_fast64_t, uint_fast64_t> getUnknownStates(std::shared_ptr<Order> order) const;
void setUnknownStates(std::shared_ptr<Order> orderOriginal, std::shared_ptr<Order> orderCopy);
void copyMinMax(std::shared_ptr<Order> orderOriginal, std::shared_ptr<Order> orderCopy);
void initializeMinMaxValues(storage::ParameterRegion<ValueType> region);
void checkParOnStateMonRes(uint_fast64_t s, std::shared_ptr<Order> order, typename OrderExtender<ValueType, ConstantType>::VariableType param, std::shared_ptr<MonotonicityResult<VariableType>> monResult);
bool isHope(std::shared_ptr<Order> order, storage::ParameterRegion<ValueType>);
private:
std::shared_ptr<storm::models::sparse::Model<ValueType>> model;
std::map<uint_fast64_t, storm::storage::BitVector*> stateMap;
Order::NodeComparison addStatesBasedOnMinMax(std::shared_ptr<Order> order, uint_fast64_t state1, uint_fast64_t state2) const;
std::tuple<std::shared_ptr<Order>, uint_fast64_t, uint_fast64_t> extendOrder(std::shared_ptr<Order> order, std::shared_ptr<MonotonicityResult<VariableType>> monRes, std::shared_ptr<expressions::BinaryRelationExpression> assumption = nullptr);
std::pair<uint_fast64_t, uint_fast64_t> extendNormal(std::shared_ptr<Order> order, uint_fast64_t currentState, std::vector<uint_fast64_t> const& successors, bool allowMerge);
std::pair<uint_fast64_t, uint_fast64_t> extendByBackwardReasoning(std::shared_ptr<Order> order, uint_fast64_t currentState, std::vector<uint_fast64_t> const& successors, bool allowMerge);
std::pair<uint_fast64_t, uint_fast64_t> extendByForwardReasoning(std::shared_ptr<Order> order, uint_fast64_t currentState, std::vector<uint_fast64_t> const& successors, bool allowMerge);
bool extendByAssumption(std::shared_ptr<Order> order, uint_fast64_t currentState, uint_fast64_t succState2, uint_fast64_t succState1);
void handleOneSuccessor(std::shared_ptr<Order> order, uint_fast64_t currentState, uint_fast64_t successor);
void handleAssumption(std::shared_ptr<Order> order, std::shared_ptr<expressions::BinaryRelationExpression> assumption) const;
std::pair<uint_fast64_t, bool> getNextState(std::shared_ptr<Order> order, uint_fast64_t stateNumber, bool done);
std::shared_ptr<Order> getBottomTopOrder();
std::shared_ptr<Order> bottomTopOrder = nullptr;
std::map<std::shared_ptr<Order>, std::vector<ConstantType>> minValues;
boost::optional<std::vector<ConstantType>> minValuesInit;
boost::optional<std::vector<ConstantType>> maxValuesInit;
std::map<std::shared_ptr<Order>, std::vector<ConstantType>> maxValues;
storage::SparseMatrix<ValueType> matrix;
std::shared_ptr<models::sparse::Model<ValueType>> model;
std::map<uint_fast64_t, std::vector<uint_fast64_t>> stateMap;
std::map<std::shared_ptr<Order>, std::pair<uint_fast64_t, uint_fast64_t>> unknownStatesMap;
std::map<std::shared_ptr<Order>, bool> usePLA;
std::map<std::shared_ptr<Order>, bool> continueExtending;
bool cyclic;
std::shared_ptr<logic::Formula const> formula;
bool acyclic;
storage::ParameterRegion<ValueType> region;
bool assumptionSeen;
uint_fast64_t numberOfStates;
storm::storage::StronglyConnectedComponentDecomposition<ValueType> sccs;
analysis::AssumptionMaker<ValueType, ConstantType>* assumptionMaker;
storm::storage::SparseMatrix<ValueType> matrix;
void handleAssumption(Order* order, std::shared_ptr<storm::expressions::BinaryRelationExpression> assumption);
boost::container::flat_set<uint_fast64_t> nonParametricStates;
std::tuple<Order*, uint_fast64_t, uint_fast64_t> extendAllSuccAdded(Order* order, uint_fast64_t const & stateNumber, storm::storage::BitVector* successors);
std::map<VariableType, std::vector<uint_fast64_t>> occuringStatesAtVariable;
std::vector<std::set<VariableType>> occuringVariablesAtState;
MonotonicityChecker<ValueType> monotonicityChecker;
std::tuple<bool, uint_fast64_t, uint_fast64_t> allSuccAdded(Order* order, uint_fast64_t stateNumber);
};
}
}

10
src/storm-pars/api/analysis.h

@ -0,0 +1,10 @@
#pragma once
#include "storm-pars/analysis/Order.h"
#include "storm-pars/analysis/OrderExtender.h"
#include "storm-pars/analysis/AssumptionChecker.h"
#include "storm-pars/analysis/AssumptionMaker.h"
#include "storm-pars/analysis/MonotonicityResult.h"
#include "storm-pars/analysis/MonotonicityHelper.h"
#include "storm-pars/analysis/LocalMonotonicityResult.h"

113
src/storm-pars/api/region.h

@ -15,6 +15,7 @@
#include "storm-pars/modelchecker/region/ValidatingSparseDtmcParameterLiftingModelChecker.h"
#include "storm-pars/modelchecker/region/RegionResultHypothesis.h"
#include "storm-pars/parser/ParameterRegionParser.h"
#include "storm-pars/parser/MonotonicityParser.h"
#include "storm-pars/storage/ParameterRegion.h"
#include "storm-pars/utility/parameterlifting.h"
@ -30,20 +31,31 @@
namespace storm {
namespace api {
struct MonotonicitySetting {
MonotonicitySetting(bool a = false, bool b = false, bool c = false) { useMonotonicity = a; useOnlyGlobalMonotonicity = b; useBoundsFromPLA = c;}
bool useMonotonicity;
bool useOnlyGlobalMonotonicity;
bool useBoundsFromPLA;
};
template <typename ValueType>
std::vector<storm::storage::ParameterRegion<ValueType>> parseRegions(std::string const& inputString, std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType> const& consideredVariables) {
std::vector<storm::storage::ParameterRegion<ValueType>> parseRegions(std::string const& inputString, std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType> const& consideredVariables, boost::optional<int> splittingThreshold = boost::none) {
// If the given input string 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 region string.
if (inputString.find(".") != std::string::npos && std::ifstream(inputString).good()) {
return storm::parser::ParameterRegionParser<ValueType>().parseMultipleRegionsFromFile(inputString, consideredVariables);
return storm::parser::ParameterRegionParser<ValueType>().parseMultipleRegionsFromFile(inputString, consideredVariables, splittingThreshold);
} else {
return storm::parser::ParameterRegionParser<ValueType>().parseMultipleRegions(inputString, consideredVariables);
return storm::parser::ParameterRegionParser<ValueType>().parseMultipleRegions(inputString, consideredVariables, splittingThreshold);
}
}
template <typename ValueType>
std::vector<storm::storage::ParameterRegion<ValueType>> parseRegions(std::string const& inputString, storm::models::ModelBase const& model) {
storm::storage::ParameterRegion<ValueType> createRegion(std::string const& inputString, std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType> const& consideredVariables, boost::optional<int> splittingThreshold= boost::none) {
return storm::parser::ParameterRegionParser<ValueType>().createRegion(inputString, consideredVariables, splittingThreshold);
}
template <typename ValueType>
std::vector<storm::storage::ParameterRegion<ValueType>> parseRegions(std::string const& inputString, storm::models::ModelBase const& model, boost::optional<int> splittingThreshold= boost::none) {
std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType> modelParameters;
if (model.isSparseModel()) {
auto const& sparseModel = dynamic_cast<storm::models::sparse::Model<ValueType> const&>(model);
@ -53,23 +65,37 @@ namespace storm {
} else {
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Retrieving model parameters is not supported for the given model type.");
}
return parseRegions<ValueType>(inputString, modelParameters);
return parseRegions<ValueType>(inputString, modelParameters, splittingThreshold);
}
template <typename ValueType>
std::vector<storm::storage::ParameterRegion<ValueType>> createRegion(std::string const& inputString, storm::models::ModelBase const& model, boost::optional<int> splittingThreshold= boost::none) {
std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType> modelParameters;
if (model.isSparseModel()) {
auto const& sparseModel = dynamic_cast<storm::models::sparse::Model<ValueType> const&>(model);
modelParameters = storm::models::sparse::getProbabilityParameters(sparseModel);
auto rewParameters = storm::models::sparse::getRewardParameters(sparseModel);
modelParameters.insert(rewParameters.begin(), rewParameters.end());
} else {
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Retrieving model parameters is not supported for the given model type.");
}
return std::vector<storm::storage::ParameterRegion<ValueType>>({createRegion<ValueType>(inputString, modelParameters, splittingThreshold)});
}
template <typename ValueType>
storm::storage::ParameterRegion<ValueType> parseRegion(std::string const& inputString, std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType> const& consideredVariables) {
storm::storage::ParameterRegion<ValueType> parseRegion(std::string const& inputString, std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType> const& consideredVariables, boost::optional<int> splittingThreshold= boost::none) {
// Handle the "empty region" case
if (inputString == "" && consideredVariables.empty()) {
return storm::storage::ParameterRegion<ValueType>();
}
auto res = parseRegions<ValueType>(inputString, consideredVariables);
auto res = parseRegions<ValueType>(inputString, consideredVariables, splittingThreshold);
STORM_LOG_THROW(res.size() == 1, storm::exceptions::InvalidOperationException, "Parsed " << res.size() << " regions but exactly one was expected.");
return res.front();
}
template <typename ValueType>
storm::storage::ParameterRegion<ValueType> parseRegion(std::string const& inputString, storm::models::ModelBase const& model) {
storm::storage::ParameterRegion<ValueType> parseRegion(std::string const& inputString, storm::models::ModelBase const& model, boost::optional<int> splittingThreshold= boost::none) {
// Handle the "empty region" case
if (inputString == "" && !model.hasParameters()) {
return storm::storage::ParameterRegion<ValueType>();
@ -80,15 +106,27 @@ namespace storm {
return res.front();
}
template <typename ValueType>
std::pair<std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>, std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>> parseMonotoneParameters(std::string const& fileName, std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model) {
std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType> modelParameters;
modelParameters = storm::models::sparse::getProbabilityParameters(*model);
auto rewParameters = storm::models::sparse::getRewardParameters(*model);
modelParameters.insert(rewParameters.begin(), rewParameters.end());
return std::move(storm::parser::MonotonicityParser<typename storm::storage::ParameterRegion<ValueType>::VariableType>().parseMonotoneVariablesFromFile(fileName, modelParameters));
}
template <typename ParametricType, typename ConstantType>
std::shared_ptr<storm::modelchecker::RegionModelChecker<ParametricType>> initializeParameterLiftingRegionModelChecker(Environment const& env, std::shared_ptr<storm::models::sparse::Model<ParametricType>> const& model, storm::modelchecker::CheckTask<storm::logic::Formula, ParametricType> const& task, bool generateSplitEstimates = false, bool allowModelSimplification = true) {
std::shared_ptr<storm::modelchecker::RegionModelChecker<ParametricType>> initializeParameterLiftingRegionModelChecker(Environment const& env, std::shared_ptr<storm::models::sparse::Model<ParametricType>> const& model, storm::modelchecker::CheckTask<storm::logic::Formula, ParametricType> const& task, bool generateSplitEstimates = false, bool allowModelSimplification = true, MonotonicitySetting monotonicitySetting = MonotonicitySetting(), boost::optional<std::pair<std::set<typename storm::storage::ParameterRegion<ParametricType>::VariableType>, std::set<typename storm::storage::ParameterRegion<ParametricType>::VariableType>>> monotoneParameters = boost::none) {
STORM_LOG_WARN_COND(storm::utility::parameterlifting::validateParameterLiftingSound(*model, task.getFormula()), "Could not validate whether parameter lifting is applicable. Please validate manually...");
STORM_LOG_WARN_COND(!(allowModelSimplification && monotonicitySetting.useMonotonicity), "Allowing model simplification when using monotonicity is not useful, as for monotonicity checking model simplification is done as preprocessing");
STORM_LOG_WARN_COND(!(monotoneParameters && !monotonicitySetting.useMonotonicity), "Setting monotone parameters without setting monotonicity usage doesn't work");
std::shared_ptr<storm::models::sparse::Model<ParametricType>> consideredModel = model;
// Treat continuous time models
if (consideredModel->isOfType(storm::models::ModelType::Ctmc) || consideredModel->isOfType(storm::models::ModelType::MarkovAutomaton)) {
STORM_LOG_WARN_COND(!monotonicitySetting.useMonotonicity, "Usage of monotonicity not supported for this type of model, continuing without montonicity checking");
STORM_LOG_WARN("Parameter lifting not supported for continuous time models. Transforming continuous model to discrete model...");
std::vector<std::shared_ptr<storm::logic::Formula const>> taskFormulaAsVector { task.getFormula().asSharedPointer() };
consideredModel = storm::api::transformContinuousToDiscreteTimeSparseModel(consideredModel, taskFormulaAsVector).first;
@ -99,7 +137,14 @@ namespace storm {
std::shared_ptr<storm::modelchecker::RegionModelChecker<ParametricType>> checker;
if (consideredModel->isOfType(storm::models::ModelType::Dtmc)) {
checker = std::make_shared<storm::modelchecker::SparseDtmcParameterLiftingModelChecker<storm::models::sparse::Dtmc<ParametricType>, ConstantType>>();
checker->setUseMonotonicity(monotonicitySetting.useMonotonicity);
checker->setUseOnlyGlobal(monotonicitySetting.useOnlyGlobalMonotonicity);
checker->setUseBounds(monotonicitySetting.useBoundsFromPLA);
if (monotonicitySetting.useMonotonicity && monotoneParameters) {
checker->setMonotoneParameters(monotoneParameters.get());
}
} else if (consideredModel->isOfType(storm::models::ModelType::Mdp)) {
STORM_LOG_WARN_COND(!monotonicitySetting.useMonotonicity, "Usage of monotonicity not supported for this type of model, continuing without montonicity checking");
checker = std::make_shared<storm::modelchecker::SparseMdpParameterLiftingModelChecker<storm::models::sparse::Mdp<ParametricType>, ConstantType>>();
} else {
STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Unable to perform parameterLifting on the provided model type.");
@ -134,21 +179,21 @@ namespace storm {
} else {
STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Unable to perform parameterLifting on the provided model type.");
}
checker->specify(env, consideredModel, task, generateSplitEstimates, allowModelSimplification);
return checker;
}
template <typename ValueType>
std::shared_ptr<storm::modelchecker::RegionModelChecker<ValueType>> initializeRegionModelChecker(Environment const& env, std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, storm::modelchecker::CheckTask<storm::logic::Formula, ValueType> const& task, storm::modelchecker::RegionCheckEngine engine) {
std::shared_ptr<storm::modelchecker::RegionModelChecker<ValueType>> initializeRegionModelChecker(Environment const& env, std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, storm::modelchecker::CheckTask<storm::logic::Formula, ValueType> const& task, storm::modelchecker::RegionCheckEngine engine, bool generateSplitEstimates = false, bool allowModelSimplification = true, MonotonicitySetting monotonicitySetting = MonotonicitySetting(), boost::optional<std::pair<std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>, std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>>> monotoneParameters = boost::none) {
switch (engine) {
// TODO: now we always use regionsplitestimates
case storm::modelchecker::RegionCheckEngine::ParameterLifting:
return initializeParameterLiftingRegionModelChecker<ValueType, double>(env, model, task);
return initializeParameterLiftingRegionModelChecker<ValueType, double>(env, model, task, generateSplitEstimates, allowModelSimplification, monotonicitySetting, monotoneParameters);
case storm::modelchecker::RegionCheckEngine::ExactParameterLifting:
return initializeParameterLiftingRegionModelChecker<ValueType, storm::RationalNumber>(env, model, task);
return initializeParameterLiftingRegionModelChecker<ValueType, storm::RationalNumber>(env, model, task, generateSplitEstimates, allowModelSimplification, monotonicitySetting, monotoneParameters);
case storm::modelchecker::RegionCheckEngine::ValidatingParameterLifting:
return initializeValidatingRegionModelChecker<ValueType, double, storm::RationalNumber>(env, model, task);
return initializeValidatingRegionModelChecker<ValueType, double, storm::RationalNumber>(env, model, task, generateSplitEstimates, allowModelSimplification);
default:
STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected region model checker type.");
}
@ -180,14 +225,18 @@ namespace storm {
* @param coverageThreshold if given, the refinement stops as soon as the fraction of the area of the subregions with inconclusive result is less then this threshold
* @param refinementDepthThreshold if given, the refinement stops at the given depth. depth=0 means no refinement.
* @param hypothesis if not 'unknown', it is only checked whether the hypothesis holds (and NOT the complementary result).
* @param allowModelSimplification
* @param useMonotonicity
* @param monThresh if given, determines at which depth to start using monotonicity
*/
template <typename ValueType>
std::unique_ptr<storm::modelchecker::RegionRefinementCheckResult<ValueType>> checkAndRefineRegionWithSparseEngine(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, storm::modelchecker::CheckTask<storm::logic::Formula, ValueType> const& task, storm::storage::ParameterRegion<ValueType> const& region, storm::modelchecker::RegionCheckEngine engine, boost::optional<ValueType> const& coverageThreshold, boost::optional<uint64_t> const& refinementDepthThreshold = boost::none, storm::modelchecker::RegionResultHypothesis hypothesis = storm::modelchecker::RegionResultHypothesis::Unknown) {
std::unique_ptr<storm::modelchecker::RegionRefinementCheckResult<ValueType>> checkAndRefineRegionWithSparseEngine(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, storm::modelchecker::CheckTask<storm::logic::Formula, ValueType> const& task, storm::storage::ParameterRegion<ValueType> const& region, storm::modelchecker::RegionCheckEngine engine, boost::optional<ValueType> const& coverageThreshold, boost::optional<uint64_t> const& refinementDepthThreshold = boost::none, storm::modelchecker::RegionResultHypothesis hypothesis = storm::modelchecker::RegionResultHypothesis::Unknown, bool allowModelSimplification = true, MonotonicitySetting monotonicitySetting = MonotonicitySetting(), uint64_t monThresh = 0) {
Environment env;
auto regionChecker = initializeRegionModelChecker(env, model, task, engine);
return regionChecker->performRegionRefinement(env, region, coverageThreshold, refinementDepthThreshold, hypothesis);
auto regionChecker = initializeRegionModelChecker(env, model, task, engine, true, allowModelSimplification, monotonicitySetting);
return regionChecker->performRegionRefinement(env, region, coverageThreshold, refinementDepthThreshold, hypothesis, monThresh);
}
// TODO: update documentation
/*!
* Finds the extremal value in the given region
* @param engine The considered region checking engine
@ -196,11 +245,28 @@ namespace storm {
* @param hypothesis if not 'unknown', it is only checked whether the hypothesis holds (and NOT the complementary result).
*/
template <typename ValueType>
std::pair<ValueType, typename storm::storage::ParameterRegion<ValueType>::Valuation> computeExtremalValue(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, storm::modelchecker::CheckTask<storm::logic::Formula, ValueType> const& task, storm::storage::ParameterRegion<ValueType> const& region, storm::modelchecker::RegionCheckEngine engine, storm::solver::OptimizationDirection const& dir, boost::optional<ValueType> const& precision) {
std::pair<ValueType, typename storm::storage::ParameterRegion<ValueType>::Valuation> computeExtremalValue(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, storm::modelchecker::CheckTask<storm::logic::Formula, ValueType> const& task, storm::storage::ParameterRegion<ValueType> const& region, storm::modelchecker::RegionCheckEngine engine, storm::solver::OptimizationDirection const& dir, boost::optional<ValueType> const& precision, MonotonicitySetting monotonicitySetting, bool generateSplitEstimates = false, boost::optional<std::pair<std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>, std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>>>& monotoneParameters = boost::none) {
Environment env;
auto regionChecker = initializeRegionModelChecker(env, model, task, engine);
bool allowModelSimplification = !monotonicitySetting.useMonotonicity;
auto regionChecker = initializeRegionModelChecker(env, model, task, engine, generateSplitEstimates, allowModelSimplification, monotonicitySetting, monotoneParameters);
return regionChecker->computeExtremalValue(env, region, dir, precision.is_initialized() ? precision.get() : storm::utility::zero<ValueType>());
}
// TODO: update documentation
/*!
* Checks if a given extremal value is indeed the extremal value in the given region
* @param engine The considered region checking engine
* @param coverageThreshold if given, the refinement stops as soon as the fraction of the area of the subregions with inconclusive result is less then this threshold
* @param refinementDepthThreshold if given, the refinement stops at the given depth. depth=0 means no refinement.
* @param hypothesis if not 'unknown', it is only checked whether the hypothesis holds (and NOT the complementary result).
*/
template <typename ValueType>
bool checkExtremalValue(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, storm::modelchecker::CheckTask<storm::logic::Formula, ValueType> const& task, storm::storage::ParameterRegion<ValueType> const& region, storm::modelchecker::RegionCheckEngine engine, storm::solver::OptimizationDirection const& dir, boost::optional<ValueType> const& precision, ValueType const& suggestion, MonotonicitySetting monotonicitySetting, bool generateSplitEstimates = false, boost::optional<std::pair<std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>, std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>>>& monotoneParameters = boost::none) {
Environment env;
bool allowModelSimplification = !monotonicitySetting.useMonotonicity;
auto regionChecker = initializeRegionModelChecker(env, model, task, engine, generateSplitEstimates, allowModelSimplification, monotonicitySetting, monotoneParameters);
return regionChecker->checkExtremalValue(env, region, dir, precision.is_initialized() ? precision.get() : storm::utility::zero<ValueType>(), suggestion);
}
template <typename ValueType>
void exportRegionCheckResultToFile(std::unique_ptr<storm::modelchecker::CheckResult> const& checkResult, std::string const& filename, bool onlyConclusiveResults = false) {
@ -211,6 +277,7 @@ namespace storm {
std::ofstream filestream;
storm::utility::openFile(filename, filestream);
for (auto const& res : regionCheckResult->getRegionResults()) {
if (!onlyConclusiveResults || res.second == storm::modelchecker::RegionResult::AllViolated || res.second == storm::modelchecker::RegionResult::AllSat) {
filestream << res.second << ": " << res.first << std::endl;
}

3
src/storm-pars/api/storm-pars.h

@ -1,4 +1,5 @@
#pragma once
#include "storm-pars/api/region.h"
#include "storm-pars/api/export.h"
#include "storm-pars/api/export.h"
#include "storm-pars/api/analysis.h"

263
src/storm-pars/modelchecker/region/RegionModelChecker.cpp

@ -1,21 +1,19 @@
#include <sstream>
#include <queue>
#include "storm-pars/analysis/OrderExtender.cpp"
#include "storm-pars/modelchecker/region/RegionModelChecker.h"
#include "storm/adapters/RationalFunctionAdapter.h"
#include "storm/utility/vector.h"
#include "storm/models/sparse/StandardRewardModel.h"
#include "storm/models/sparse/Dtmc.h"
#include "storm/models/sparse/Mdp.h"
#include "storm/settings/SettingsManager.h"
#include "storm/settings/modules/CoreSettings.h"
#include "storm/exceptions/NotImplementedException.h"
#include "storm/exceptions/NotSupportedException.h"
#include "storm/exceptions/InvalidStateException.h"
#include "storm/exceptions/InvalidArgumentException.h"
@ -50,7 +48,7 @@ namespace storm {
}
template <typename ParametricType>
std::unique_ptr<storm::modelchecker::RegionRefinementCheckResult<ParametricType>> RegionModelChecker<ParametricType>::performRegionRefinement(Environment const& env, storm::storage::ParameterRegion<ParametricType> const& region, boost::optional<ParametricType> const& coverageThreshold, boost::optional<uint64_t> depthThreshold, RegionResultHypothesis const& hypothesis) {
std::unique_ptr<storm::modelchecker::RegionRefinementCheckResult<ParametricType>> RegionModelChecker<ParametricType>::performRegionRefinement(Environment const& env, storm::storage::ParameterRegion<ParametricType> const& region, boost::optional<ParametricType> const& coverageThreshold, boost::optional<uint64_t> depthThreshold, RegionResultHypothesis const& hypothesis, uint64_t monThresh) {
STORM_LOG_INFO("Applying refinement on region: " << region.toString(true) << " .");
auto thresholdAsCoefficient = coverageThreshold ? storm::utility::convertNumber<CoefficientType>(coverageThreshold.get()) : storm::utility::zero<CoefficientType>();
@ -58,16 +56,18 @@ namespace storm {
auto fractionOfUndiscoveredArea = storm::utility::one<CoefficientType>();
auto fractionOfAllSatArea = storm::utility::zero<CoefficientType>();
auto fractionOfAllViolatedArea = storm::utility::zero<CoefficientType>();
numberOfRegionsKnownThroughMonotonicity = 0;
// The resulting (sub-)regions
std::vector<std::pair<storm::storage::ParameterRegion<ParametricType>, RegionResult>> result;
// FIFO queues storing the data for the regions that we still need to process.
std::queue<std::pair<storm::storage::ParameterRegion<ParametricType>, RegionResult>> unprocessedRegions;
std::queue<uint64_t> refinementDepths;
unprocessedRegions.emplace(region, RegionResult::Unknown);
refinementDepths.push(0);
uint_fast64_t numOfAnalyzedRegions = 0;
CoefficientType displayedProgress = storm::utility::zero<CoefficientType>();
if (storm::settings::getModule<storm::settings::modules::CoreSettings>().isShowStatisticsSet()) {
@ -84,13 +84,17 @@ namespace storm {
displayedProgress = storm::utility::zero<CoefficientType>();
}
while (fractionOfUndiscoveredArea > thresholdAsCoefficient && !unprocessedRegions.empty()) {
// NORMAL WHILE LOOP
uint64_t currentDepth = refinementDepths.front();
while ((!useMonotonicity || currentDepth < monThresh) && fractionOfUndiscoveredArea > thresholdAsCoefficient && !unprocessedRegions.empty()) {
assert(unprocessedRegions.size() == refinementDepths.size());
uint64_t currentDepth = refinementDepths.front();
STORM_LOG_INFO("Analyzing region #" << numOfAnalyzedRegions << " (Refinement depth " << currentDepth << "; " << storm::utility::convertNumber<double>(fractionOfUndiscoveredArea) * 100 << "% still unknown)");
auto& currentRegion = unprocessedRegions.front().first;
auto& res = unprocessedRegions.front().second;
std::shared_ptr<storm::analysis::Order> order;
std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonotonicityResult;
res = analyzeRegion(env, currentRegion, hypothesis, res, false);
switch (res) {
case RegionResult::AllSat:
fractionOfUndiscoveredArea -= currentRegion.area() / areaOfParameterSpace;
@ -106,11 +110,167 @@ namespace storm {
// Split the region as long as the desired refinement depth is not reached.
if (!depthThreshold || currentDepth < depthThreshold.get()) {
std::vector<storm::storage::ParameterRegion<ParametricType>> newRegions;
RegionResult initResForNewRegions = (res == RegionResult::CenterSat) ? RegionResult::ExistsSat :
((res == RegionResult::CenterViolated) ? RegionResult::ExistsViolated :
RegionResult::Unknown);
currentRegion.split(currentRegion.getCenterPoint(), newRegions);
bool first = true;
for (auto& newRegion : newRegions) {
unprocessedRegions.emplace(std::move(newRegion), initResForNewRegions);
refinementDepths.push(currentDepth + 1);
}
} else {
// If the region is not further refined, it is still added to the result
result.push_back(std::move(unprocessedRegions.front()));
}
break;
}
++numOfAnalyzedRegions;
unprocessedRegions.pop();
refinementDepths.pop();
if (storm::settings::getModule<storm::settings::modules::CoreSettings>().isShowStatisticsSet()) {
while (displayedProgress < storm::utility::one<CoefficientType>() - fractionOfUndiscoveredArea) {
STORM_PRINT_AND_LOG("#");
displayedProgress += storm::utility::convertNumber<CoefficientType>(0.01);
}
}
currentDepth = refinementDepths.front();
}
// FIFO queues for the order and local monotonicity results
std::queue<std::shared_ptr<storm::analysis::Order>> orders;
std::queue<std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>>> localMonotonicityResults;
std::shared_ptr<storm::analysis::Order> order;
std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonotonicityResult;
if (useMonotonicity && fractionOfUndiscoveredArea > thresholdAsCoefficient && !unprocessedRegions.empty()) {
storm::utility::Stopwatch monWatch(true);
orders.emplace(extendOrder(env, nullptr, region));
assert (orders.front() != nullptr);
auto monRes = std::shared_ptr< storm::analysis::LocalMonotonicityResult<VariableType>>(new storm::analysis::LocalMonotonicityResult<VariableType>(orders.front()->getNumberOfStates()));
extendLocalMonotonicityResult(region, orders.front(), monRes);
localMonotonicityResults.emplace(monRes);
order = orders.front();
localMonotonicityResult = localMonotonicityResults.front();
if (!order->getDoneBuilding()) {
// we need to use copies for both order and local mon res
while(unprocessedRegions.size() > orders.size()){
orders.emplace(order->copy());
localMonotonicityResults.emplace(localMonotonicityResult->copy());
}
} else if (!localMonotonicityResult->isDone()) {
// the order will not change anymore
while(unprocessedRegions.size() > orders.size()) {
orders.emplace(order);
localMonotonicityResults.emplace(localMonotonicityResult->copy());
}
} else {
// both will not change anymore
while (unprocessedRegions.size() > orders.size()) {
orders.emplace(order);
localMonotonicityResults.emplace(localMonotonicityResult);
}
}
monWatch.stop();
STORM_PRINT(std::endl << "Time for orderBuilding and monRes initialization: " << monWatch << "." << std::endl << std::endl);
}
bool useSameOrder = useMonotonicity && order->getDoneBuilding();
bool useSameLocalMonotonicityResult = useSameOrder && localMonotonicityResult->isDone();
// USEMON WHILE LOOP
while (useMonotonicity && fractionOfUndiscoveredArea > thresholdAsCoefficient && !unprocessedRegions.empty()) {
assert ((useSameLocalMonotonicityResult && localMonotonicityResults.size() == 1)|| unprocessedRegions.size() == localMonotonicityResults.size());
assert ((useSameOrder && orders.size() == 1) || unprocessedRegions.size() == orders.size());
assert(unprocessedRegions.size() == refinementDepths.size());
currentDepth = refinementDepths.front();
STORM_LOG_INFO("Analyzing region #" << numOfAnalyzedRegions << " (Refinement depth " << currentDepth << "; " << storm::utility::convertNumber<double>(fractionOfUndiscoveredArea) * 100 << "% still unknown)");
auto& currentRegion = unprocessedRegions.front().first;
auto& res = unprocessedRegions.front().second;
assert(!orders.empty());
if (!useSameOrder) {
order = orders.front();
if (!order->getDoneBuilding()) {
extendOrder(env, order, currentRegion);
}
}
if (!useSameLocalMonotonicityResult) {
localMonotonicityResult = localMonotonicityResults.front();
if (!localMonotonicityResult->isDone()) {
extendLocalMonotonicityResult(currentRegion, order, localMonotonicityResult);
}
}
res = analyzeRegion(env, currentRegion, hypothesis, res, false, order, localMonotonicityResult);
switch (res) {
case RegionResult::AllSat:
fractionOfUndiscoveredArea -= currentRegion.area() / areaOfParameterSpace;
fractionOfAllSatArea += currentRegion.area() / areaOfParameterSpace;
STORM_LOG_INFO("Region " << unprocessedRegions.front() << " is AllSat");
result.push_back(std::move(unprocessedRegions.front()));
break;
case RegionResult::AllViolated:
fractionOfUndiscoveredArea -= currentRegion.area() / areaOfParameterSpace;
fractionOfAllViolatedArea += currentRegion.area() / areaOfParameterSpace;
STORM_LOG_INFO("Region " << unprocessedRegions.front() << " is AllViolated");
result.push_back(std::move(unprocessedRegions.front()));
break;
default:
// Split the region as long as the desired refinement depth is not reached.
if (!depthThreshold || currentDepth < depthThreshold.get()) {
std::vector<storm::storage::ParameterRegion<ParametricType>> newRegions;
RegionResult initResForNewRegions = (res == RegionResult::CenterSat) ? RegionResult::ExistsSat :
((res == RegionResult::CenterViolated) ? RegionResult::ExistsViolated :
RegionResult::Unknown);
((res == RegionResult::CenterViolated) ? RegionResult::ExistsViolated :
RegionResult::Unknown);
std::vector<storm::storage::ParameterRegion<ParametricType>> newKnownRegions;
// Only split in (non)monotone vars
splitSmart(currentRegion, newRegions, order, *(localMonotonicityResult->getGlobalMonotonicityResult()), false);
assert (newRegions.size() != 0);
initResForNewRegions = (res == RegionResult::CenterSat) ? RegionResult::ExistsSat :
((res == RegionResult::CenterViolated) ? RegionResult::ExistsViolated :
RegionResult::Unknown);
bool first = true;
for (auto& newRegion : newRegions) {
if (!useSameOrder) {
if (first) {
orders.emplace(order);
localMonotonicityResults.emplace(localMonotonicityResult);
first = false;
} else {
if (!order->getDoneBuilding()) {
// we need to use copies for both order and local mon res
orders.emplace(order->copy());
localMonotonicityResults.emplace(localMonotonicityResult->copy());
} else if (!localMonotonicityResult->isDone()) {
// the order will not change anymore
orders.emplace(order);
localMonotonicityResults.emplace(localMonotonicityResult->copy());
} else {
// both will not change anymore
orders.emplace(order);
localMonotonicityResults.emplace(localMonotonicityResult);
}
}
} else if (!useSameLocalMonotonicityResult) {
if (first) {
localMonotonicityResults.emplace(localMonotonicityResult);
first = false;
} else {
if (!localMonotonicityResult->isDone()) {
localMonotonicityResults.emplace(localMonotonicityResult->copy());
} else {
localMonotonicityResults.emplace(localMonotonicityResult);
}
}
}
unprocessedRegions.emplace(std::move(newRegion), initResForNewRegions);
refinementDepths.push(currentDepth + 1);
}
@ -120,9 +280,17 @@ namespace storm {
}
break;
}
++numOfAnalyzedRegions;
unprocessedRegions.pop();
refinementDepths.pop();
if (!useSameOrder) {
orders.pop();
}
if (!useSameLocalMonotonicityResult) {
localMonotonicityResults.pop();
}
if (storm::settings::getModule<storm::settings::modules::CoreSettings>().isShowStatisticsSet()) {
while (displayedProgress < storm::utility::one<CoefficientType>() - fractionOfUndiscoveredArea) {
STORM_PRINT_AND_LOG("#");
@ -146,18 +314,35 @@ namespace storm {
STORM_PRINT_AND_LOG("Region Refinement Statistics:" << std::endl);
STORM_PRINT_AND_LOG(" Analyzed a total of " << numOfAnalyzedRegions << " regions." << std::endl);
if (useMonotonicity) {
STORM_PRINT_AND_LOG(" " << numberOfRegionsKnownThroughMonotonicity << " regions where discovered with help of monotonicity." << std::endl);
}
}
auto regionCopyForResult = region;
return std::make_unique<storm::modelchecker::RegionRefinementCheckResult<ParametricType>>(std::move(result), std::move(regionCopyForResult));
}
template <typename ParametricType>
void RegionModelChecker<ParametricType>::extendLocalMonotonicityResult(storm::storage::ParameterRegion<ParametricType> const& region, std::shared_ptr<storm::analysis::Order> order, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonotonicityResult){
STORM_LOG_WARN("Initializing local Monotonicity Results not implemented for RegionModelChecker.");
}
template <typename ParametricType>
std::pair<ParametricType, typename storm::storage::ParameterRegion<ParametricType>::Valuation> RegionModelChecker<ParametricType>::computeExtremalValue(Environment const& env, storm::storage::ParameterRegion<ParametricType> const& region, storm::solver::OptimizationDirection const& dir, ParametricType const& precision) {
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Computing extremal values is not supported for this region model checker.");
return std::pair<ParametricType, typename storm::storage::ParameterRegion<ParametricType>::Valuation>();
}
template <typename ParametricType>
bool RegionModelChecker<ParametricType>::checkExtremalValue(Environment const& env, storm::storage::ParameterRegion<ParametricType> const& region, storm::solver::OptimizationDirection const& dir, ParametricType const& precision, ParametricType const& valueToCheck) {
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Checking extremal values is not supported for this region model checker.");
return false;
}
template <typename ParametricType>
bool RegionModelChecker<ParametricType>::isRegionSplitEstimateSupported() const {
@ -170,7 +355,63 @@ namespace storm {
return std::map<typename RegionModelChecker<ParametricType>::VariableType, double>();
}
template <typename ParametricType>
std::shared_ptr<storm::analysis::Order> RegionModelChecker<ParametricType>::extendOrder(Environment const& env, std::shared_ptr<storm::analysis::Order> order, storm::storage::ParameterRegion<ParametricType> region) {
STORM_LOG_WARN("Extending order for RegionModelChecker not implemented");
// Does nothing
return order;
}
template <typename ParametricType>
void RegionModelChecker<ParametricType>::setConstantEntries(std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonotonicityResult) {
STORM_LOG_WARN("Setting constant entries fo local monotonicity result not implemented");
// Does nothing
}
template <typename ParametricType>
bool RegionModelChecker<ParametricType>::isUseMonotonicitySet() const{
return useMonotonicity;
}
template <typename ParametricType>
bool RegionModelChecker<ParametricType>::isUseBoundsSet() {
return useBounds;
}
template <typename ParametricType>
bool RegionModelChecker<ParametricType>::isOnlyGlobalSet() {
return useOnlyGlobal;
}
template <typename ParametricType>
void RegionModelChecker<ParametricType>::setUseMonotonicity(bool monotonicity) {
this->useMonotonicity = monotonicity;
}
template <typename ParametricType>
void RegionModelChecker<ParametricType>::setUseBounds(bool bounds) {
assert (!bounds || useMonotonicity);
this->useBounds = bounds;
}
template <typename ParametricType>
void RegionModelChecker<ParametricType>::setUseOnlyGlobal(bool global) {
assert (!global || useMonotonicity);
this->useOnlyGlobal = global;
}
template <typename ParametricType>
void RegionModelChecker<ParametricType>::splitSmart(storm::storage::ParameterRegion<ParametricType> & currentRegion, std::vector<storm::storage::ParameterRegion<ParametricType>> &regionVector, std::shared_ptr<storm::analysis::Order> order, storm::analysis::MonotonicityResult<VariableType> & monRes, bool splitForExtremum) const {
STORM_LOG_WARN("Smart splitting for this model checker not implemented");
currentRegion.split(currentRegion.getCenterPoint(), regionVector);
}
template<typename ParametricType>
void RegionModelChecker<ParametricType>::setMonotoneParameters(std::pair<std::set<typename storm::storage::ParameterRegion<ParametricType>::VariableType>, std::set<typename storm::storage::ParameterRegion<ParametricType>::VariableType>> monotoneParameters) {
monotoneIncrParameters = std::move(monotoneParameters.first);
monotoneDecrParameters = std::move(monotoneParameters.second);
}
#ifdef STORM_HAVE_CARL
template class RegionModelChecker<storm::RationalFunction>;
#endif

46
src/storm-pars/modelchecker/region/RegionModelChecker.h

@ -2,6 +2,9 @@
#include <memory>
#include "storm-pars/analysis/Order.h"
#include "storm-pars/analysis/OrderExtender.h"
#include "storm-pars/analysis/LocalMonotonicityResult.h"
#include "storm-pars/modelchecker/results/RegionCheckResult.h"
#include "storm-pars/modelchecker/results/RegionRefinementCheckResult.h"
#include "storm-pars/modelchecker/region/RegionResult.h"
@ -37,7 +40,7 @@ namespace storm {
* @param initialResult encodes what is already known about this region
* @param sampleVerticesOfRegion enables sampling of the vertices of the region in cases where AllSat/AllViolated could not be shown.
*/
virtual RegionResult analyzeRegion(Environment const& env, storm::storage::ParameterRegion<ParametricType> const& region, RegionResultHypothesis const& hypothesis = RegionResultHypothesis::Unknown, RegionResult const& initialResult = RegionResult::Unknown, bool sampleVerticesOfRegion = false) = 0;
virtual RegionResult analyzeRegion(Environment const& env, storm::storage::ParameterRegion<ParametricType> const& region, RegionResultHypothesis const& hypothesis = RegionResultHypothesis::Unknown, RegionResult const& initialResult = RegionResult::Unknown, bool sampleVerticesOfRegion = false, std::shared_ptr<storm::analysis::Order> reachabilityOrder = nullptr, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonotonicityResult = nullptr) = 0;
/*!
* Analyzes the given regions.
@ -55,17 +58,19 @@ namespace storm {
* @param coverageThreshold if given, the refinement stops as soon as the fraction of the area of the subregions with inconclusive result is less then this threshold
* @param depthThreshold if given, the refinement stops at the given depth. depth=0 means no refinement.
* @param hypothesis if not 'unknown', it is only checked whether the hypothesis holds within the given region.
*
* @param monThresh if given, determines at which depth to start using monotonicity
*/
std::unique_ptr<storm::modelchecker::RegionRefinementCheckResult<ParametricType>> performRegionRefinement(Environment const& env, storm::storage::ParameterRegion<ParametricType> const& region, boost::optional<ParametricType> const& coverageThreshold, boost::optional<uint64_t> depthThreshold = boost::none, RegionResultHypothesis const& hypothesis = RegionResultHypothesis::Unknown);
std::unique_ptr<storm::modelchecker::RegionRefinementCheckResult<ParametricType>> performRegionRefinement(Environment const& env, storm::storage::ParameterRegion<ParametricType> const& region, boost::optional<ParametricType> const& coverageThreshold, boost::optional<uint64_t> depthThreshold = boost::none, RegionResultHypothesis const& hypothesis = RegionResultHypothesis::Unknown, uint64_t monThresh = 0);
// TODO: documentation
/*!
* Finds the extremal value within the given region and with the given precision.
* The returned value v corresponds to the value at the returned valuation.
* The actual maximum (minimum) lies in the interval [v, v+precision] ([v-precision, v])
*/
virtual std::pair<ParametricType, typename storm::storage::ParameterRegion<ParametricType>::Valuation> computeExtremalValue(Environment const& env, storm::storage::ParameterRegion<ParametricType> const& region, storm::solver::OptimizationDirection const& dir, ParametricType const& precision);
virtual bool checkExtremalValue(Environment const& env, storm::storage::ParameterRegion<ParametricType> const& region, storm::solver::OptimizationDirection const& dir, ParametricType const& precision, ParametricType const& valueToCheck);
/*!
* Returns true if region split estimation (a) was enabled when model and check task have been specified and (b) is supported by this region model checker.
*/
@ -76,7 +81,36 @@ namespace storm {
* If a parameter is assigned a high value, we should prefer splitting with respect to this parameter.
*/
virtual std::map<VariableType, double> getRegionSplitEstimate() const;
virtual std::shared_ptr<storm::analysis::Order> extendOrder(Environment const& env, std::shared_ptr<storm::analysis::Order> order, storm::storage::ParameterRegion<ParametricType> region);
virtual void setConstantEntries(std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonotonicityResult);
bool isUseMonotonicitySet() const;
bool isUseBoundsSet();
bool isOnlyGlobalSet();
void setUseMonotonicity(bool monotonicity = true);
void setUseBounds(bool bounds = true);
void setUseOnlyGlobal(bool global = true);
void setMonotoneParameters(std::pair<std::set<typename storm::storage::ParameterRegion<ParametricType>::VariableType>, std::set<typename storm::storage::ParameterRegion<ParametricType>::VariableType>> monotoneParameters);
private:
bool useMonotonicity = false;
bool useOnlyGlobal = false;
bool useBounds = false;
protected:
uint_fast64_t numberOfRegionsKnownThroughMonotonicity;
boost::optional<std::set<typename storm::storage::ParameterRegion<ParametricType>::VariableType>> monotoneIncrParameters;
boost::optional<std::set<typename storm::storage::ParameterRegion<ParametricType>::VariableType>> monotoneDecrParameters;
virtual void extendLocalMonotonicityResult(storm::storage::ParameterRegion<ParametricType> const& region, std::shared_ptr<storm::analysis::Order> order, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonotonicityResult);
virtual void splitSmart(storm::storage::ParameterRegion<ParametricType> & region, std::vector<storm::storage::ParameterRegion<ParametricType>> &regionVector, std::shared_ptr<storm::analysis::Order> order, storm::analysis::MonotonicityResult<VariableType> & monRes, bool splitForExtremum) const;
};
} //namespace modelchecker

347
src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.cpp

@ -38,7 +38,7 @@ namespace storm {
}
template <typename SparseModelType, typename ConstantType>
bool SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::canHandle(std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask) const {
bool SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::canHandle(std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, ValueType> const& checkTask) const {
bool result = parametricModel->isOfType(storm::models::ModelType::Dtmc);
result &= parametricModel->isSparseModel();
result &= parametricModel->supportsParameters();
@ -47,21 +47,25 @@ namespace storm {
result &= checkTask.getFormula().isInFragment(storm::logic::reachability().setRewardOperatorsAllowed(true).setReachabilityRewardFormulasAllowed(true).setBoundedUntilFormulasAllowed(true).setCumulativeRewardFormulasAllowed(true).setStepBoundedCumulativeRewardFormulasAllowed(true).setTimeBoundedCumulativeRewardFormulasAllowed(true).setTimeBoundedUntilFormulasAllowed(true).setStepBoundedUntilFormulasAllowed(true).setTimeBoundedUntilFormulasAllowed(true));
return result;
}
template <typename SparseModelType, typename ConstantType>
void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::specify(Environment const& env, std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask, bool generateRegionSplitEstimates, bool allowModelSimplification) {
void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::specify(Environment const& env, std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, ValueType> const& checkTask, bool generateRegionSplitEstimates, bool allowModelSimplification) {
auto dtmc = parametricModel->template as<SparseModelType>();
monotonicityChecker = std::make_unique<storm::analysis::MonotonicityChecker<ValueType>>(dtmc->getTransitionMatrix());
specify_internal(env, dtmc, checkTask, generateRegionSplitEstimates, !allowModelSimplification);
if (checkTask.isBoundSet()) {
thresholdTask = storm::utility::convertNumber<ConstantType>(checkTask.getBoundThreshold());
}
}
template <typename SparseModelType, typename ConstantType>
void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::specify_internal(Environment const& env, std::shared_ptr<SparseModelType> parametricModel, CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask, bool generateRegionSplitEstimates, bool skipModelSimplification) {
void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::specify_internal(Environment const& env, std::shared_ptr<SparseModelType> parametricModel, CheckTask<storm::logic::Formula, ValueType> const& checkTask, bool generateRegionSplitEstimates, bool skipModelSimplification) {
STORM_LOG_ASSERT(this->canHandle(parametricModel, checkTask), "specified model and formula can not be handled by this.");
reset();
regionSplitEstimationsEnabled = generateRegionSplitEstimates;
if (skipModelSimplification) {
this->parametricModel = parametricModel;
this->specifyFormula(env, checkTask);
@ -74,11 +78,11 @@ namespace storm {
this->specifyFormula(env, checkTask.substituteFormula(*simplifier.getSimplifiedFormula()));
}
}
template <typename SparseModelType, typename ConstantType>
void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::specifyBoundedUntilFormula(Environment const& env, CheckTask<storm::logic::BoundedUntilFormula, ConstantType> const& checkTask) {
// get the step bound
STORM_LOG_THROW(!checkTask.getFormula().hasLowerBound(), storm::exceptions::NotSupportedException, "Lower step bounds are not supported.");
STORM_LOG_THROW(checkTask.getFormula().hasUpperBound(), storm::exceptions::NotSupportedException, "Expected a bounded until formula with an upper bound.");
@ -108,9 +112,8 @@ namespace storm {
// if there are maybestates, create the parameterLifter
if (!maybeStates.empty()) {
// Create the vector of one-step probabilities to go to target states.
std::vector<typename SparseModelType::ValueType> b = this->parametricModel->getTransitionMatrix().getConstrainedRowSumVector(storm::storage::BitVector(this->parametricModel->getTransitionMatrix().getRowCount(), true), psiStates);
parameterLifter = std::make_unique<storm::transformer::ParameterLifter<typename SparseModelType::ValueType, ConstantType>>(this->parametricModel->getTransitionMatrix(), b, maybeStates, maybeStates);
std::vector<ValueType> b = this->parametricModel->getTransitionMatrix().getConstrainedRowSumVector(storm::storage::BitVector(this->parametricModel->getTransitionMatrix().getRowCount(), true), psiStates);
parameterLifter = std::make_unique<storm::transformer::ParameterLifter<ValueType, ConstantType>>(this->parametricModel->getTransitionMatrix(), b, maybeStates, maybeStates, false, RegionModelChecker<ValueType>::isUseMonotonicitySet());
}
// We know some bounds for the results so set them
@ -118,11 +121,14 @@ namespace storm {
upperResultBound = storm::utility::one<ConstantType>();
// No requirements for bounded formulas
solverFactory->setRequirementsChecked(true);
// For monotonicity checking
std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(this->parametricModel->getBackwardTransitions(), phiStates, psiStates);
this->orderExtender = storm::analysis::OrderExtender<ValueType,ConstantType>(&statesWithProbability01.second, &statesWithProbability01.first, this->parametricModel->getTransitionMatrix());
}
template <typename SparseModelType, typename ConstantType>
void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::specifyUntilFormula(Environment const& env, CheckTask<storm::logic::UntilFormula, ConstantType> const& checkTask) {
// get the results for the subformulas
storm::modelchecker::SparsePropositionalModelChecker<SparseModelType> propositionalChecker(*this->parametricModel);
STORM_LOG_THROW(propositionalChecker.canHandle(checkTask.getFormula().getLeftSubformula()) && propositionalChecker.canHandle(checkTask.getFormula().getRightSubformula()), storm::exceptions::NotSupportedException, "Parameter lifting with non-propositional subformulas is not supported");
@ -140,9 +146,8 @@ namespace storm {
// if there are maybestates, create the parameterLifter
if (!maybeStates.empty()) {
// Create the vector of one-step probabilities to go to target states.
std::vector<typename SparseModelType::ValueType> b = this->parametricModel->getTransitionMatrix().getConstrainedRowSumVector(storm::storage::BitVector(this->parametricModel->getTransitionMatrix().getRowCount(), true), statesWithProbability01.second);
parameterLifter = std::make_unique<storm::transformer::ParameterLifter<typename SparseModelType::ValueType, ConstantType>>(this->parametricModel->getTransitionMatrix(), b, maybeStates, maybeStates, regionSplitEstimationsEnabled);
std::vector<ValueType> b = this->parametricModel->getTransitionMatrix().getConstrainedRowSumVector(storm::storage::BitVector(this->parametricModel->getTransitionMatrix().getRowCount(), true), statesWithProbability01.second);
parameterLifter = std::make_unique<storm::transformer::ParameterLifter<ValueType, ConstantType>>(this->parametricModel->getTransitionMatrix(), b, maybeStates, maybeStates, regionSplitEstimationsEnabled, RegionModelChecker<ValueType>::isUseMonotonicitySet());
}
// We know some bounds for the results so set them
@ -154,16 +159,16 @@ namespace storm {
req.clearBounds();
STORM_LOG_THROW(!req.hasEnabledCriticalRequirement(), storm::exceptions::UncheckedRequirementException, "Solver requirements " + req.getEnabledRequirementsAsString() + " not checked.");
solverFactory->setRequirementsChecked(true);
this->orderExtender = storm::analysis::OrderExtender<ValueType,ConstantType>(&statesWithProbability01.second, &statesWithProbability01.first, this->parametricModel->getTransitionMatrix());
}
template <typename SparseModelType, typename ConstantType>
void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::specifyReachabilityRewardFormula(Environment const& env, CheckTask<storm::logic::EventuallyFormula, ConstantType> const& checkTask) {
// get the results for the subformula
storm::modelchecker::SparsePropositionalModelChecker<SparseModelType> propositionalChecker(*this->parametricModel);
STORM_LOG_THROW(propositionalChecker.canHandle(checkTask.getFormula().getSubformula()), storm::exceptions::NotSupportedException, "Parameter lifting with non-propositional subformulas is not supported");
storm::storage::BitVector targetStates = std::move(propositionalChecker.check(checkTask.getFormula().getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector());
// get the maybeStates
storm::storage::BitVector infinityStates = storm::utility::graph::performProb1(this->parametricModel->getBackwardTransitions(), storm::storage::BitVector(this->parametricModel->getNumberOfStates(), true), targetStates);
infinityStates.complement();
@ -180,9 +185,9 @@ namespace storm {
typename SparseModelType::RewardModelType const& rewardModel = checkTask.isRewardModelSet() ? this->parametricModel->getRewardModel(checkTask.getRewardModel()) : this->parametricModel->getUniqueRewardModel();
std::vector<typename SparseModelType::ValueType> b = rewardModel.getTotalRewardVector(this->parametricModel->getTransitionMatrix());
std::vector<ValueType> b = rewardModel.getTotalRewardVector(this->parametricModel->getTransitionMatrix());
parameterLifter = std::make_unique<storm::transformer::ParameterLifter<typename SparseModelType::ValueType, ConstantType>>(this->parametricModel->getTransitionMatrix(), b, maybeStates, maybeStates, regionSplitEstimationsEnabled);
parameterLifter = std::make_unique<storm::transformer::ParameterLifter<ValueType, ConstantType>>(this->parametricModel->getTransitionMatrix(), b, maybeStates, maybeStates, regionSplitEstimationsEnabled);
}
// We only know a lower bound for the result
@ -203,7 +208,6 @@ namespace storm {
template <typename SparseModelType, typename ConstantType>
void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::specifyCumulativeRewardFormula(Environment const& env, CheckTask<storm::logic::CumulativeRewardFormula, ConstantType> const& checkTask) {
// Obtain the stepBound
stepBound = checkTask.getFormula().getBound().evaluateAsInt();
if (checkTask.getFormula().isBoundStrict()) {
@ -219,10 +223,9 @@ namespace storm {
// Create the reward vector
STORM_LOG_THROW((checkTask.isRewardModelSet() && this->parametricModel->hasRewardModel(checkTask.getRewardModel())) || (!checkTask.isRewardModelSet() && this->parametricModel->hasUniqueRewardModel()), storm::exceptions::InvalidPropertyException, "The reward model specified by the CheckTask is not available in the given model.");
typename SparseModelType::RewardModelType const& rewardModel = checkTask.isRewardModelSet() ? this->parametricModel->getRewardModel(checkTask.getRewardModel()) : this->parametricModel->getUniqueRewardModel();
std::vector<typename SparseModelType::ValueType> b = rewardModel.getTotalRewardVector(this->parametricModel->getTransitionMatrix());
parameterLifter = std::make_unique<storm::transformer::ParameterLifter<typename SparseModelType::ValueType, ConstantType>>(this->parametricModel->getTransitionMatrix(), b, maybeStates, maybeStates);
std::vector<ValueType> b = rewardModel.getTotalRewardVector(this->parametricModel->getTransitionMatrix());
parameterLifter = std::make_unique<storm::transformer::ParameterLifter<ValueType, ConstantType>>(this->parametricModel->getTransitionMatrix(), b, maybeStates, maybeStates);
// We only know a lower bound for the result
lowerResultBound = storm::utility::zero<ConstantType>();
@ -231,25 +234,44 @@ namespace storm {
solverFactory->setRequirementsChecked(true);
}
template <typename SparseModelType, typename ConstantType>
storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::getInstantiationCheckerSAT() {
if (!instantiationCheckerSAT) {
instantiationCheckerSAT = std::make_unique<storm::modelchecker::SparseDtmcInstantiationModelChecker<SparseModelType, ConstantType>>(*this->parametricModel);
instantiationCheckerSAT->specifyFormula(this->currentCheckTask->template convertValueType<ValueType>());
instantiationCheckerSAT->setInstantiationsAreGraphPreserving(true);
}
return *instantiationCheckerSAT;
}
template <typename SparseModelType, typename ConstantType>
storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::getInstantiationCheckerVIO() {
if (!instantiationCheckerVIO) {
instantiationCheckerVIO = std::make_unique<storm::modelchecker::SparseDtmcInstantiationModelChecker<SparseModelType, ConstantType>>(*this->parametricModel);
instantiationCheckerVIO->specifyFormula(this->currentCheckTask->template convertValueType<ValueType>());
instantiationCheckerVIO->setInstantiationsAreGraphPreserving(true);
}
return *instantiationCheckerVIO;
}
template <typename SparseModelType, typename ConstantType>
storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::getInstantiationChecker() {
if (!instantiationChecker) {
instantiationChecker = std::make_unique<storm::modelchecker::SparseDtmcInstantiationModelChecker<SparseModelType, ConstantType>>(*this->parametricModel);
instantiationChecker->specifyFormula(this->currentCheckTask->template convertValueType<typename SparseModelType::ValueType>());
instantiationChecker->specifyFormula(this->currentCheckTask->template convertValueType<ValueType>());
instantiationChecker->setInstantiationsAreGraphPreserving(true);
}
return *instantiationChecker;
}
template <typename SparseModelType, typename ConstantType>
std::unique_ptr<CheckResult> SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::computeQuantitativeValues(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters) {
std::unique_ptr<CheckResult> SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::computeQuantitativeValues(Environment const& env, storm::storage::ParameterRegion<ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonotonicityResult) {
if (maybeStates.empty()) {
return std::make_unique<storm::modelchecker::ExplicitQuantitativeCheckResult<ConstantType>>(resultsForNonMaybeStates);
}
parameterLifter->specifyRegion(region, dirForParameters);
if (stepBound) {
assert(*stepBound > 0);
x = std::vector<ConstantType>(maybeStates.getNumberOfSetBits(), storm::utility::zero<ConstantType>());
@ -267,37 +289,96 @@ namespace storm {
std::vector<ConstantType> oneStepProbs;
oneStepProbs.reserve(parameterLifter->getMatrix().getRowCount());
for (uint64_t row = 0; row < parameterLifter->getMatrix().getRowCount(); ++row) {
oneStepProbs.push_back(storm::utility::one<ConstantType>() - parameterLifter->getMatrix().getRowSum(row));
oneStepProbs.push_back(
storm::utility::one<ConstantType>() - parameterLifter->getMatrix().getRowSum(row));
}
if (dirForParameters == storm::OptimizationDirection::Minimize) {
storm::modelchecker::helper::DsMpiMdpUpperRewardBoundsComputer<ConstantType> dsmpi(parameterLifter->getMatrix(), parameterLifter->getVector(), oneStepProbs);
storm::modelchecker::helper::DsMpiMdpUpperRewardBoundsComputer<ConstantType> dsmpi(
parameterLifter->getMatrix(), parameterLifter->getVector(), oneStepProbs);
solver->setUpperBounds(dsmpi.computeUpperBounds());
} else {
storm::modelchecker::helper::BaierUpperRewardBoundsComputer<ConstantType> baier(parameterLifter->getMatrix(), parameterLifter->getVector(), oneStepProbs);
storm::modelchecker::helper::BaierUpperRewardBoundsComputer<ConstantType> baier(
parameterLifter->getMatrix(), parameterLifter->getVector(), oneStepProbs);
solver->setUpperBound(baier.computeUpperBound());
}
}
solver->setTrackScheduler(true);
if (storm::solver::minimize(dirForParameters) && minSchedChoices) solver->setInitialScheduler(std::move(minSchedChoices.get()));
if (storm::solver::maximize(dirForParameters) && maxSchedChoices) solver->setInitialScheduler(std::move(maxSchedChoices.get()));
if (localMonotonicityResult != nullptr && !this->isOnlyGlobalSet()) {
storm::storage::BitVector fixedStates(parameterLifter->getRowGroupCount(), false);
bool useMinimize = storm::solver::minimize(dirForParameters);
if (useMinimize && !minSchedChoices) {
minSchedChoices = std::vector<uint_fast64_t>(parameterLifter->getRowGroupCount(), 0);
}
if (!useMinimize && !maxSchedChoices) {
maxSchedChoices = std::vector<uint_fast64_t>(parameterLifter->getRowGroupCount(), 0);
}
// TODO: this only works since we decided to keep all columns
auto const & occuringVariables = parameterLifter->getOccurringVariablesAtState();
for (auto state = 0; state < parameterLifter->getRowGroupCount(); ++state) {
auto oldStateNumber = parameterLifter->getOriginalStateNumber(state);
auto& variables = occuringVariables.at(oldStateNumber);
// point at which we start with rows for this state
STORM_LOG_THROW(variables.size() <= 1, storm::exceptions::NotImplementedException, "Using localMonRes not yet implemented for states with 2 or more variables, please run without --use-monotonicity");
bool allMonotone = true;
for (auto var : variables) {
auto monotonicity = localMonotonicityResult->getMonotonicity(oldStateNumber, var);
bool ignoreUpperBound = monotonicity == Monotonicity::Constant || (useMinimize && monotonicity == Monotonicity::Incr) || (!useMinimize && monotonicity == Monotonicity::Decr);
bool ignoreLowerBound = !ignoreUpperBound && ((useMinimize && monotonicity == Monotonicity::Decr) || (!useMinimize && monotonicity == Monotonicity::Incr));
allMonotone &= (ignoreUpperBound || ignoreLowerBound);
if (ignoreLowerBound) {
if (useMinimize) {
minSchedChoices.get()[state] = 1;
} else {
maxSchedChoices.get()[state] = 1;
}
} else if (ignoreUpperBound) {
if (useMinimize) {
minSchedChoices.get()[state] = 0;
} else {
maxSchedChoices.get()[state] = 0;
}
}
}
if (allMonotone) {
fixedStates.set(state);
}
}
solver->setFixedStates(std::move(fixedStates));
}
if (storm::solver::minimize(dirForParameters) && minSchedChoices)
solver->setInitialScheduler(std::move(minSchedChoices.get()));
if (storm::solver::maximize(dirForParameters) && maxSchedChoices)
solver->setInitialScheduler(std::move(maxSchedChoices.get()));
if (this->currentCheckTask->isBoundSet() && solver->hasInitialScheduler()) {
// If we reach this point, we know that after applying the hint, the x-values can only become larger (if we maximize) or smaller (if we minimize).
std::unique_ptr<storm::solver::TerminationCondition<ConstantType>> termCond;
storm::storage::BitVector relevantStatesInSubsystem = this->currentCheckTask->isOnlyInitialStatesRelevantSet() ? this->parametricModel->getInitialStates() % maybeStates : storm::storage::BitVector(maybeStates.getNumberOfSetBits(), true);
storm::storage::BitVector relevantStatesInSubsystem = this->currentCheckTask->isOnlyInitialStatesRelevantSet()
? this->parametricModel->getInitialStates() %
maybeStates : storm::storage::BitVector(
maybeStates.getNumberOfSetBits(), true);
if (storm::solver::minimize(dirForParameters)) {
// Terminate if the value for ALL relevant states is already below the threshold
termCond = std::make_unique<storm::solver::TerminateIfFilteredExtremumBelowThreshold<ConstantType>> (relevantStatesInSubsystem, true, this->currentCheckTask->getBoundThreshold(), false);
termCond = std::make_unique<storm::solver::TerminateIfFilteredExtremumBelowThreshold<ConstantType>>(
relevantStatesInSubsystem, true, this->currentCheckTask->getBoundThreshold(), false);
} else {
// Terminate if the value for ALL relevant states is already above the threshold
termCond = std::make_unique<storm::solver::TerminateIfFilteredExtremumExceedsThreshold<ConstantType>> (relevantStatesInSubsystem, true, this->currentCheckTask->getBoundThreshold(), true);
termCond = std::make_unique<storm::solver::TerminateIfFilteredExtremumExceedsThreshold<ConstantType>>(
relevantStatesInSubsystem, true, this->currentCheckTask->getBoundThreshold(), true);
}
solver->setTerminationCondition(std::move(termCond));
}
// Invoke the solver
x.resize(maybeStates.getNumberOfSetBits(), storm::utility::zero<ConstantType>());
solver->solveEquations(env, dirForParameters, x, parameterLifter->getVector());
if(storm::solver::minimize(dirForParameters)) {
if (storm::solver::minimize(dirForParameters)) {
minSchedChoices = solver->getSchedulerChoices();
} else {
maxSchedChoices = solver->getSchedulerChoices();
@ -306,6 +387,7 @@ namespace storm {
computeRegionSplitEstimates(x, solver->getSchedulerChoices(), region, dirForParameters);
}
}
// Get the result for the complete model (including maybestates)
std::vector<ConstantType> result = resultsForNonMaybeStates;
@ -318,8 +400,8 @@ namespace storm {
}
template <typename SparseModelType, typename ConstantType>
void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::computeRegionSplitEstimates(std::vector<ConstantType> const& quantitativeResult, std::vector<uint_fast64_t> const& schedulerChoices, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters) {
std::map<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType, double> deltaLower, deltaUpper;
void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::computeRegionSplitEstimates(std::vector<ConstantType> const& quantitativeResult, std::vector<uint_fast64_t> const& schedulerChoices, storm::storage::ParameterRegion<ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters) {
std::map<VariableType, double> deltaLower, deltaUpper;
for (auto const& p : region.getVariables()) {
deltaLower.insert(std::make_pair(p, 0.0));
deltaUpper.insert(std::make_pair(p, 0.0));
@ -327,7 +409,11 @@ namespace storm {
auto const& choiceValuations = parameterLifter->getRowLabels();
auto const& matrix = parameterLifter->getMatrix();
auto const& vector = parameterLifter->getVector();
auto i = 0;
for (auto & x : vector) {
++i;
}
std::vector<ConstantType> stateResults;
for (uint64_t state = 0; state < schedulerChoices.size(); ++state) {
uint64_t rowOffset = matrix.getRowGroupIndices()[state];
@ -338,12 +424,13 @@ namespace storm {
for (uint64_t row = rowOffset; row < matrix.getRowGroupIndices()[state + 1]; ++row) {
stateResults.push_back(matrix.multiplyRowWithVector(row, quantitativeResult) + vector[row]);
}
// Do this twice, once for upperbound once for lowerbound
bool checkUpperParameters = false;
do {
auto const& consideredParameters = checkUpperParameters ? optimalChoiceVal.getUpperParameters() : optimalChoiceVal.getLowerParameters();
for (auto const& p : consideredParameters) {
// Find the 'best' choice that assigns the parameter to the other bound
ConstantType bestValue;
ConstantType bestValue = 0;
bool foundBestValue = false;
for (uint64_t choice = 0; choice < stateResults.size(); ++choice) {
if (choice != optimalChoice) {
@ -357,26 +444,36 @@ namespace storm {
}
}
}
if (checkUpperParameters) {
deltaLower[p] += storm::utility::convertNumber<double>(bestValue);
} else {
deltaUpper[p] += storm::utility::convertNumber<double>(bestValue);
auto optimal = storm::utility::convertNumber<double>(stateResults[optimalChoice]);
auto diff = optimal - storm::utility::convertNumber<double>(bestValue);
if (foundBestValue) {
if (checkUpperParameters) {
deltaLower[p] += std::abs(diff);
} else {
deltaUpper[p] += std::abs(diff);
}
}
}
checkUpperParameters = !checkUpperParameters;
} while (checkUpperParameters);
}
regionSplitEstimates.clear();
useRegionSplitEstimates = false;
for (auto const& p : region.getVariables()) {
if (deltaLower[p] > deltaUpper[p]) {
regionSplitEstimates.insert(std::make_pair(p, deltaUpper[p]));
} else {
regionSplitEstimates.insert(std::make_pair(p, deltaLower[p]));
if (this->possibleMonotoneParameters.find(p) != this->possibleMonotoneParameters.end()) {
if (deltaLower[p] > deltaUpper[p] && deltaUpper[p] >= 0.0001) {
regionSplitEstimates.insert(std::make_pair(p, deltaUpper[p]));
useRegionSplitEstimates = true;
} else if (deltaLower[p] <= deltaUpper[p] && deltaLower[p] >= 0.0001) {
{
regionSplitEstimates.insert(std::make_pair(p, deltaLower[p]));
useRegionSplitEstimates = true;
}
}
}
}
// large regionsplitestimate implies that parameter p occurs as p and 1-p at least once
}
template <typename SparseModelType, typename ConstantType>
@ -437,9 +534,145 @@ namespace storm {
return regionSplitEstimates;
}
template<typename SparseModelType, typename ConstantType>
std::shared_ptr<storm::analysis::Order> SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::extendOrder(Environment const& env, std::shared_ptr<storm::analysis::Order> order, storm::storage::ParameterRegion<ValueType> region) {
if (this->orderExtender) {
auto res = this->orderExtender->extendOrder(order, region);
order = std::get<0>(res);
if (std::get<1>(res) != order->getNumberOfStates()) {
this->orderExtender.get().setUnknownStates(order, std::get<1>(res), std::get<2>(res));
}
} else {
STORM_LOG_WARN("Extending order for RegionModelChecker not implemented");
}
return order;
}
template <typename SparseModelType, typename ConstantType>
void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::extendLocalMonotonicityResult(storm::storage::ParameterRegion<ValueType> const& region, std::shared_ptr<storm::analysis::Order> order, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonotonicityResult) {
if (this->monotoneIncrParameters && !localMonotonicityResult->isFixedParametersSet()) {
for (auto & var : this->monotoneIncrParameters.get()) {
localMonotonicityResult->setMonotoneIncreasing(var);
}
for (auto & var : this->monotoneDecrParameters.get()) {
localMonotonicityResult->setMonotoneDecreasing(var);
}
}
auto state = order->getNextDoneState(-1);
auto const variablesAtState = parameterLifter->getOccurringVariablesAtState();
while (state != order->getNumberOfStates()) {
if (localMonotonicityResult->getMonotonicity(state) == nullptr) {
auto variables = variablesAtState[state];
if (variables.size() == 0 || order->isBottomState(state) || order->isTopState(state)) {
localMonotonicityResult->setConstant(state);
} else {
for (auto const &var : variables) {
auto monotonicity = localMonotonicityResult->getMonotonicity(state, var);
if (monotonicity == Monotonicity::Unknown || monotonicity == Monotonicity::Not) {
monotonicity = monotonicityChecker->checkLocalMonotonicity(order, state, var, region);
if (monotonicity == Monotonicity::Unknown || monotonicity == Monotonicity::Not) {
// TODO: Skip for now?
} else {
localMonotonicityResult->setMonotonicity(state, var, monotonicity);
}
}
}
}
} else {
// Do nothing, we already checked this one
}
state = order->getNextDoneState(state);
}
auto const statesAtVariable = parameterLifter->getOccuringStatesAtVariable();
bool allDone = true;
for (auto const & entry : statesAtVariable) {
auto states = entry.second;
auto var = entry.first;
bool done = true;
for (auto const& state : states) {
done &= order->contains(state) && localMonotonicityResult->getMonotonicity(state, var) != Monotonicity::Unknown;
auto check = localMonotonicityResult->getMonotonicity(state, var);
if (!done) {
break;
}
}
allDone &= done;
if (done) {
localMonotonicityResult->getGlobalMonotonicityResult()->setDoneForVar(var);
}
}
if (allDone) {
localMonotonicityResult->setDone();
while (order->existsNextState()) {
// Simply add the states we couldn't add sofar between =) and =( as we could find local monotonicity for all parametric states
order->add(order->getNextStateNumber().second);
}
assert (order->getDoneBuilding());
}
}
template <typename SparseModelType, typename ConstantType>
void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::splitSmart (
storm::storage::ParameterRegion<ValueType> &region, std::vector<storm::storage::ParameterRegion<ValueType>> &regionVector,
std::shared_ptr<storm::analysis::Order> order, storm::analysis::MonotonicityResult<VariableType> & monRes, bool splitForExtremum) const {
assert (regionVector.size() == 0);
std::multimap<double, VariableType> sortedOnValues;
std::set<VariableType> consideredVariables;
if (splitForExtremum) {
if (regionSplitEstimationsEnabled && useRegionSplitEstimates) {
STORM_LOG_INFO("Splitting based on region split estimates");
for (auto &entry : regionSplitEstimates) {
assert (!this->isUseMonotonicitySet() || (!monRes.isMonotone(entry.first) && this->possibleMonotoneParameters.find(entry.first) != this->possibleMonotoneParameters.end()));
// sortedOnValues.insert({-(entry.second * storm::utility::convertNumber<double>(region.getDifference(entry.first))* storm::utility::convertNumber<double>(region.getDifference(entry.first))), entry.first});
sortedOnValues.insert({-(entry.second ), entry.first});
}
for (auto itr = sortedOnValues.begin(); itr != sortedOnValues.end() && consideredVariables.size() < region.getSplitThreshold(); ++itr) {
consideredVariables.insert(itr->second);
}
assert (consideredVariables.size() > 0);
region.split(region.getCenterPoint(), regionVector, std::move(consideredVariables));
} else {
STORM_LOG_INFO("Splitting based on sorting");
auto &sortedOnDifference = region.getVariablesSorted();
for (auto itr = sortedOnDifference.begin(); itr != sortedOnDifference.end() && consideredVariables.size() < region.getSplitThreshold(); ++itr) {
if (!this->isUseMonotonicitySet() || !monRes.isMonotone(itr->second)) {
consideredVariables.insert(itr->second);
}
}
assert (consideredVariables.size() > 0 || (monRes.isDone() && monRes.isAllMonotonicity()));
region.split(region.getCenterPoint(), regionVector, std::move(consideredVariables));
}
} else {
// split for pla
if (regionSplitEstimationsEnabled && useRegionSplitEstimates) {
STORM_LOG_INFO("Splitting based on region split estimates");
ConstantType diff = this->lastValue - (this->currentCheckTask->getFormula().asOperatorFormula().template getThresholdAs<ConstantType>());
for (auto &entry : regionSplitEstimates) {
if ((!this->isUseMonotonicitySet() || !monRes.isMonotone(entry.first)) && entry.second > diff) {
sortedOnValues.insert({-(entry.second * storm::utility::convertNumber<double>(region.getDifference(entry.first)) * storm::utility::convertNumber<double>(region.getDifference(entry.first))), entry.first});
}
}
for (auto itr = sortedOnValues.begin(); itr != sortedOnValues.end() && consideredVariables.size() < region.getSplitThreshold(); ++itr) {
consideredVariables.insert(itr->second);
}
}
if (consideredVariables.size() == 0) {
auto &sortedOnDifference = region.getVariablesSorted();
for (auto itr = sortedOnDifference.begin(); itr != sortedOnDifference.end() && consideredVariables.size() < region.getSplitThreshold(); ++itr) {
consideredVariables.insert(itr->second);
}
}
assert (consideredVariables.size() > 0);
region.split(region.getCenterPoint(), regionVector, std::move(consideredVariables));
}
}
template class SparseDtmcParameterLiftingModelChecker<storm::models::sparse::Dtmc<storm::RationalFunction>, double>;
template class SparseDtmcParameterLiftingModelChecker<storm::models::sparse::Dtmc<storm::RationalFunction>, storm::RationalNumber>;
}
}

51
src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.h

@ -18,21 +18,31 @@ namespace storm {
template <typename SparseModelType, typename ConstantType>
class SparseDtmcParameterLiftingModelChecker : public SparseParameterLiftingModelChecker<SparseModelType, ConstantType> {
public:
typedef typename SparseModelType::ValueType ValueType;
typedef typename RegionModelChecker<ValueType>::VariableType VariableType;
typedef typename storm::analysis::MonotonicityResult<VariableType>::Monotonicity Monotonicity;
typedef typename storm::storage::ParameterRegion<ValueType>::CoefficientType CoefficientType;
SparseDtmcParameterLiftingModelChecker();
SparseDtmcParameterLiftingModelChecker(std::unique_ptr<storm::solver::MinMaxLinearEquationSolverFactory<ConstantType>>&& solverFactory);
virtual ~SparseDtmcParameterLiftingModelChecker() = default;
virtual bool canHandle(std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask) const override;
virtual bool canHandle(std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, ValueType> const& checkTask) const override;
virtual void specify(Environment const& env, std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask, bool generateRegionSplitEstimates = false, bool allowModelSimplification = true) override;
void specify_internal(Environment const& env, std::shared_ptr<SparseModelType> parametricModel, CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask, bool generateRegionSplitEstimates, bool skipModelSimplification);
virtual void specify(Environment const& env, std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, ValueType> const& checkTask, bool generateRegionSplitEstimates = false, bool allowModelSimplification = true) override;
void specify_internal(Environment const& env, std::shared_ptr<SparseModelType> parametricModel, CheckTask<storm::logic::Formula, ValueType> const& checkTask, bool generateRegionSplitEstimates, bool skipModelSimplification);
boost::optional<storm::storage::Scheduler<ConstantType>> getCurrentMinScheduler();
boost::optional<storm::storage::Scheduler<ConstantType>> getCurrentMaxScheduler();
virtual bool isRegionSplitEstimateSupported() const override;
virtual std::map<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType, double> getRegionSplitEstimate() const override;
virtual std::map<VariableType, double> getRegionSplitEstimate() const override;
virtual std::shared_ptr<storm::analysis::Order> extendOrder(Environment const& env, std::shared_ptr<storm::analysis::Order> order, storm::storage::ParameterRegion<ValueType> region) override;
virtual void extendLocalMonotonicityResult(storm::storage::ParameterRegion<ValueType> const& region, std::shared_ptr<storm::analysis::Order> order, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonotonicityResult) override;
protected:
virtual void specifyBoundedUntilFormula(Environment const& env, CheckTask<storm::logic::BoundedUntilFormula, ConstantType> const& checkTask) override;
@ -41,23 +51,30 @@ namespace storm {
virtual void specifyCumulativeRewardFormula(Environment const& env, CheckTask<storm::logic::CumulativeRewardFormula, ConstantType> const& checkTask) override;
virtual storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& getInstantiationChecker() override;
virtual std::unique_ptr<CheckResult> computeQuantitativeValues(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters) override;
virtual storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& getInstantiationCheckerSAT() override;
virtual storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& getInstantiationCheckerVIO() override;
virtual std::unique_ptr<CheckResult> computeQuantitativeValues(Environment const& env, storm::storage::ParameterRegion<ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonotonicityResult = nullptr) override;
void computeRegionSplitEstimates(std::vector<ConstantType> const& quantitativeResult, std::vector<uint_fast64_t> const& schedulerChoices, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters);
void computeRegionSplitEstimates(std::vector<ConstantType> const& quantitativeResult, std::vector<uint_fast64_t> const& schedulerChoices, storm::storage::ParameterRegion<ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters);
virtual void reset() override;
private:
virtual void splitSmart(storm::storage::ParameterRegion<ValueType> & region, std::vector<storm::storage::ParameterRegion<ValueType>> &regionVector, std::shared_ptr<storm::analysis::Order> order, storm::analysis::MonotonicityResult<VariableType> & monRes, bool splitForExtremum) const override;
private:
storm::storage::BitVector maybeStates;
std::vector<ConstantType> resultsForNonMaybeStates;
boost::optional<uint_fast64_t> stepBound;
boost::optional<ConstantType> thresholdTask;
std::unique_ptr<storm::modelchecker::SparseDtmcInstantiationModelChecker<SparseModelType, ConstantType>> instantiationChecker;
std::unique_ptr<storm::transformer::ParameterLifter<typename SparseModelType::ValueType, ConstantType>> parameterLifter;
std::unique_ptr<storm::modelchecker::SparseDtmcInstantiationModelChecker<SparseModelType, ConstantType>> instantiationCheckerSAT;
std::unique_ptr<storm::modelchecker::SparseDtmcInstantiationModelChecker<SparseModelType, ConstantType>> instantiationCheckerVIO;
std::unique_ptr<storm::transformer::ParameterLifter<ValueType, ConstantType>> parameterLifter;
std::unique_ptr<storm::solver::MinMaxLinearEquationSolverFactory<ConstantType>> solverFactory;
bool solvingRequiresUpperRewardBounds;
@ -67,7 +84,13 @@ namespace storm {
boost::optional<ConstantType> lowerResultBound, upperResultBound;
bool regionSplitEstimationsEnabled;
std::map<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType, double> regionSplitEstimates;
std::map<VariableType, double> regionSplitEstimates;
// Used for monotonicity
bool useRegionSplitEstimates;
std::unique_ptr<storm::analysis::MonotonicityChecker<ValueType>> monotonicityChecker;
};
}
}

10
src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.cpp

@ -43,21 +43,21 @@ namespace storm {
result &= checkTask.getFormula().isInFragment(storm::logic::reachability().setRewardOperatorsAllowed(true).setReachabilityRewardFormulasAllowed(true).setBoundedUntilFormulasAllowed(true).setCumulativeRewardFormulasAllowed(true).setStepBoundedUntilFormulasAllowed(true).setTimeBoundedCumulativeRewardFormulasAllowed(true).setStepBoundedCumulativeRewardFormulasAllowed(true).setTimeBoundedUntilFormulasAllowed(true));
return result;
}
template <typename SparseModelType, typename ConstantType>
void SparseMdpParameterLiftingModelChecker<SparseModelType, ConstantType>::specify(Environment const& env, std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask, bool generateRegionSplitEstimates, bool allowModelSimplifications) {
auto mdp = parametricModel->template as<SparseModelType>();
specify_internal(env, mdp, checkTask, generateRegionSplitEstimates, !allowModelSimplifications);
}
template <typename SparseModelType, typename ConstantType>
void SparseMdpParameterLiftingModelChecker<SparseModelType, ConstantType>::specify_internal(Environment const& env, std::shared_ptr<SparseModelType> parametricModel, CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask, bool generateRegionSplitEstimates, bool skipModelSimplification) {
STORM_LOG_ASSERT(this->canHandle(parametricModel, checkTask), "specified model and formula can not be handled by this.");
reset();
if (skipModelSimplification) {
this->parametricModel = parametricModel;
this->specifyFormula(env, checkTask);
@ -240,7 +240,7 @@ namespace storm {
}
template <typename SparseModelType, typename ConstantType>
std::unique_ptr<CheckResult> SparseMdpParameterLiftingModelChecker<SparseModelType, ConstantType>::computeQuantitativeValues(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters) {
std::unique_ptr<CheckResult> SparseMdpParameterLiftingModelChecker<SparseModelType, ConstantType>::computeQuantitativeValues(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult) {
if (maybeStates.empty()) {
return std::make_unique<storm::modelchecker::ExplicitQuantitativeCheckResult<ConstantType>>(resultsForNonMaybeStates);

2
src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.h

@ -41,7 +41,7 @@ namespace storm {
virtual storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& getInstantiationChecker() override;
virtual std::unique_ptr<CheckResult> computeQuantitativeValues(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters) override;
virtual std::unique_ptr<CheckResult> computeQuantitativeValues(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult = nullptr) override;
virtual void reset() override;

482
src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.cpp

@ -1,6 +1,8 @@
#include "storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.h"
#include <queue>
#include <boost/container/flat_set.hpp>
#include <storm-pars/analysis/MonotonicityChecker.h>
#include "storm/adapters/RationalFunctionAdapter.h"
#include "storm/logic/FragmentSpecification.h"
@ -28,7 +30,7 @@ namespace storm {
currentFormula = checkTask.getFormula().asSharedPointer();
currentCheckTask = std::make_unique<storm::modelchecker::CheckTask<storm::logic::Formula, ConstantType>>(checkTask.substituteFormula(*currentFormula).template convertValueType<ConstantType>());
if(currentCheckTask->getFormula().isProbabilityOperatorFormula()) {
if (currentCheckTask->getFormula().isProbabilityOperatorFormula()) {
auto const& probOpFormula = currentCheckTask->getFormula().asProbabilityOperatorFormula();
if(probOpFormula.getSubformula().isBoundedUntilFormula()) {
specifyBoundedUntilFormula(env, currentCheckTask->substituteFormula(probOpFormula.getSubformula().asBoundedUntilFormula()));
@ -50,8 +52,11 @@ namespace storm {
}
template <typename SparseModelType, typename ConstantType>
RegionResult SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::analyzeRegion(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, RegionResultHypothesis const& hypothesis, RegionResult const& initialResult, bool sampleVerticesOfRegion) {
RegionResult SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::analyzeRegion(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, RegionResultHypothesis const& hypothesis, RegionResult const& initialResult, bool sampleVerticesOfRegion, std::shared_ptr<storm::analysis::Order> reachabilityOrder, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult) {
typedef typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType VariableType;
typedef typename storm::analysis::MonotonicityResult<VariableType>::Monotonicity Monotonicity;
typedef typename storm::utility::parametric::Valuation<typename SparseModelType::ValueType> Valuation;
typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::CoefficientType CoefficientType;
STORM_LOG_THROW(this->currentCheckTask->isOnlyInitialStatesRelevantSet(), storm::exceptions::NotSupportedException, "Analyzing regions with parameter lifting requires a property where only the value in the initial states is relevant.");
STORM_LOG_THROW(this->currentCheckTask->isBoundSet(), storm::exceptions::NotSupportedException, "Analyzing regions with parameter lifting requires a bounded property.");
STORM_LOG_THROW(this->parametricModel->getInitialStates().getNumberOfSetBits() == 1, storm::exceptions::NotSupportedException, "Analyzing regions with parameter lifting requires a model with a single initial state.");
@ -60,22 +65,79 @@ namespace storm {
// Check if we need to check the formula on one point to decide whether to show AllSat or AllViolated
if (hypothesis == RegionResultHypothesis::Unknown && result == RegionResult::Unknown) {
result = getInstantiationChecker().check(env, region.getCenterPoint())->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()] ? RegionResult::CenterSat : RegionResult::CenterViolated;
result = getInstantiationChecker().check(env, region.getCenterPoint())->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()] ? RegionResult::CenterSat : RegionResult::CenterViolated;
}
// try to prove AllSat or AllViolated, depending on the hypothesis or the current result
if (hypothesis == RegionResultHypothesis::AllSat || result == RegionResult::ExistsSat || result == RegionResult::CenterSat) {
bool existsSat = (hypothesis == RegionResultHypothesis::AllSat || result == RegionResult::ExistsSat || result == RegionResult::CenterSat);
bool existsViolated = (hypothesis == RegionResultHypothesis::AllViolated || result == RegionResult::ExistsViolated || result == RegionResult::CenterViolated);
// Here we check on global monotonicity
if (localMonotonicityResult != nullptr && localMonotonicityResult->isDone()) {
// Try to check it with a global monotonicity result
auto monRes = localMonotonicityResult->getGlobalMonotonicityResult();
bool lowerBound = isLowerBound(this->currentCheckTask->getBound().comparisonType);
if (monRes->isDone() && monRes->isAllMonotonicity()) {
// Build valuations
auto monMap = monRes->getMonotonicityResult();
Valuation valuationToCheckSat;
Valuation valuationToCheckViolated;
for (auto var : region.getVariables()) {
auto monVar = monMap[var];
if (monVar == Monotonicity::Constant) {
valuationToCheckSat.insert(std::pair<VariableType, CoefficientType>(var, region.getLowerBoundary(var)));
valuationToCheckViolated.insert(std::pair<VariableType, CoefficientType>(var, region.getLowerBoundary(var)));
} else if (monVar == Monotonicity::Decr) {
if (lowerBound) {
valuationToCheckSat.insert(std::pair<VariableType, CoefficientType>(var, region.getUpperBoundary(var)));
valuationToCheckViolated.insert(std::pair<VariableType, CoefficientType>(var, region.getLowerBoundary(var)));
} else {
valuationToCheckSat.insert(std::pair<VariableType, CoefficientType>(var, region.getLowerBoundary(var)));
valuationToCheckViolated.insert(std::pair<VariableType, CoefficientType>(var, region.getUpperBoundary(var)));
}
} else if (monVar == Monotonicity::Incr) {
if (lowerBound) {
valuationToCheckSat.insert(std::pair<VariableType, CoefficientType>(var, region.getLowerBoundary(var)));
valuationToCheckViolated.insert(std::pair<VariableType, CoefficientType>(var, region.getUpperBoundary(var)));
} else {
valuationToCheckSat.insert(std::pair<VariableType, CoefficientType>(var, region.getUpperBoundary(var)));
valuationToCheckViolated.insert(std::pair<VariableType, CoefficientType>(var, region.getLowerBoundary(var)));
}
}
}
// Check for result
if (existsSat && getInstantiationCheckerSAT().check(env, valuationToCheckSat)->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()]) {
STORM_LOG_INFO("Region " << region << " is AllSat, discovered with instantiation checker on " << valuationToCheckSat << " and help of monotonicity" << std::endl);
RegionModelChecker<typename SparseModelType::ValueType>::numberOfRegionsKnownThroughMonotonicity++;
return RegionResult::AllSat;
}
if (existsViolated && !getInstantiationCheckerVIO().check(env, valuationToCheckViolated)->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()]) {
STORM_LOG_INFO("Region " << region << " is AllViolated, discovered with instantiation checker on " << valuationToCheckViolated << " and help of monotonicity" << std::endl);
RegionModelChecker<typename SparseModelType::ValueType>::numberOfRegionsKnownThroughMonotonicity++;
return RegionResult::AllViolated;
}
return RegionResult::ExistsBoth;
}
}
// Try to prove AllSat or AllViolated, depending on the hypothesis or the current result
if (existsSat) {
// show AllSat:
storm::solver::OptimizationDirection parameterOptimizationDirection = isLowerBound(this->currentCheckTask->getBound().comparisonType) ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
if (this->check(env, region, parameterOptimizationDirection)->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()]) {
auto checkResult = this->check(env, region, parameterOptimizationDirection, reachabilityOrder, localMonotonicityResult);
if (checkResult->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()]) {
result = RegionResult::AllSat;
} else if (sampleVerticesOfRegion) {
result = sampleVertices(env, region, result);
}
} else if (hypothesis == RegionResultHypothesis::AllViolated || result == RegionResult::ExistsViolated || result == RegionResult::CenterViolated) {
} else if (existsViolated) {
// show AllViolated:
storm::solver::OptimizationDirection parameterOptimizationDirection = isLowerBound(this->currentCheckTask->getBound().comparisonType) ? storm::solver::OptimizationDirection::Maximize : storm::solver::OptimizationDirection::Minimize;
if (!this->check(env, region, parameterOptimizationDirection)->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()]) {
auto checkResult = this->check(env, region, parameterOptimizationDirection, reachabilityOrder, localMonotonicityResult);
if (!checkResult->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()]) {
result = RegionResult::AllViolated;
} else if (sampleVerticesOfRegion) {
result = sampleVertices(env, region, result);
@ -122,10 +184,10 @@ namespace storm {
return result;
}
template <typename SparseModelType, typename ConstantType>
std::unique_ptr<CheckResult> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::check(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters) {
auto quantitativeResult = computeQuantitativeValues(env, region, dirForParameters);
std::unique_ptr<CheckResult> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::check(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, std::shared_ptr<storm::analysis::Order> reachabilityOrder, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult) {
auto quantitativeResult = computeQuantitativeValues(env, region, dirForParameters, localMonotonicityResult);
lastValue = quantitativeResult->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()];
if(currentCheckTask->getFormula().hasQuantitativeResult()) {
return quantitativeResult;
} else {
@ -134,9 +196,9 @@ namespace storm {
}
template <typename SparseModelType, typename ConstantType>
std::unique_ptr<QuantitativeCheckResult<ConstantType>> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getBound(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters) {
std::unique_ptr<QuantitativeCheckResult<ConstantType>> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getBound(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult) {
STORM_LOG_WARN_COND(this->currentCheckTask->getFormula().hasQuantitativeResult(), "Computing quantitative bounds for a qualitative formula...");
return std::make_unique<ExplicitQuantitativeCheckResult<ConstantType>>(std::move(computeQuantitativeValues(env, region, dirForParameters)->template asExplicitQuantitativeCheckResult<ConstantType>()));
return std::make_unique<ExplicitQuantitativeCheckResult<ConstantType>>(std::move(computeQuantitativeValues(env, region, dirForParameters, localMonotonicityResult)->template asExplicitQuantitativeCheckResult<ConstantType>()));
}
template <typename SparseModelType, typename ConstantType>
@ -144,80 +206,268 @@ namespace storm {
STORM_LOG_THROW(this->parametricModel->getInitialStates().getNumberOfSetBits() == 1, storm::exceptions::NotSupportedException, "Getting a bound at the initial state requires a model with a single initial state.");
return storm::utility::convertNumber<typename SparseModelType::ValueType>(getBound(env, region, dirForParameters)->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()]);
}
template <typename SparseModelType, typename ConstantType>
storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getInstantiationCheckerSAT() {
return getInstantiationChecker();
}
template <typename SparseModelType, typename ConstantType>
storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getInstantiationCheckerVIO() {
return getInstantiationChecker();
}
template <typename SparseModelType, typename ConstantType>
struct RegionBound {
typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::VariableType VariableType;
RegionBound(RegionBound<SparseModelType, ConstantType> const& other) = default;
RegionBound(storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& r, ConstantType const& b) : region(r), bound(b) {}
RegionBound(storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& r, std::shared_ptr<storm::analysis::Order> o, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> l, ConstantType const& b) : region(r), order(o), localMonRes(l), bound(b) {}
storm::storage::ParameterRegion<typename SparseModelType::ValueType> region;
std::shared_ptr<storm::analysis::Order> order;
std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonRes;
ConstantType bound;
};
template <typename SparseModelType, typename ConstantType>
std::pair<typename SparseModelType::ValueType, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::computeExtremalValue(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dir, typename SparseModelType::ValueType const& precision) {
template<typename SparseModelType, typename ConstantType>
std::pair<typename SparseModelType::ValueType, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::computeExtremalValue(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dir, typename SparseModelType::ValueType const& precision, boost::optional<ConstantType> const& initialValue) {
typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::CoefficientType CoefficientType;
typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation Valuation;
STORM_LOG_THROW(this->parametricModel->getInitialStates().getNumberOfSetBits() == 1, storm::exceptions::NotSupportedException, "Getting extremal values at the initial state requires a model with a single initial state.");
boost::optional<ConstantType> value;
typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation valuation;
for (auto const& v : region.getVerticesOfRegion(region.getVariables())) {
auto currValue = getInstantiationChecker().check(env, v)->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()];
if (!value.is_initialized() || (storm::solver::minimize(dir) ? currValue < value.get() : currValue > value.get())) {
value = currValue;
valuation = v;
STORM_LOG_INFO("Current value for extremum: " << value.get() << ".");
}
}
bool const useMonotonicity = this->isUseMonotonicitySet();
bool const minimize = storm::solver::minimize(dir);
// Comparator for the region queue
auto cmp = storm::solver::minimize(dir) ?
[](RegionBound<SparseModelType, ConstantType> const& lhs, RegionBound<SparseModelType, ConstantType> const& rhs) { return lhs.bound > rhs.bound; } :
[](RegionBound<SparseModelType, ConstantType> const& lhs, RegionBound<SparseModelType, ConstantType> const& rhs) { return lhs.bound < rhs.bound; };
[](RegionBound<SparseModelType, ConstantType> const& lhs, RegionBound<SparseModelType, ConstantType> const& rhs) { return lhs.bound > rhs.bound; } :
[](RegionBound<SparseModelType, ConstantType> const& lhs, RegionBound<SparseModelType, ConstantType> const& rhs) { return lhs.bound < rhs.bound; };
std::priority_queue<RegionBound<SparseModelType, ConstantType>, std::vector<RegionBound<SparseModelType, ConstantType>>, decltype(cmp)> regionQueue(cmp);
regionQueue.emplace(region, storm::utility::zero<ConstantType>());
auto totalArea = storm::utility::convertNumber<ConstantType>(region.area());
auto coveredArea = storm::utility::zero<ConstantType>();
while (!regionQueue.empty()) {
auto const& currRegion = regionQueue.top().region;
// Check whether this region contains a new 'good' value
auto currValue = getInstantiationChecker().check(env, currRegion.getCenterPoint())->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()];
if (storm::solver::minimize(dir) ? currValue < value.get() : currValue > value.get()) {
value = currValue;
valuation = currRegion.getCenterPoint();
}
storm::utility::Stopwatch initialWatch(true);
// Check whether this region needs further investigation (i.e. splitting)
auto currBound = getBound(env, currRegion, dir)->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()];
std::vector<storm::storage::ParameterRegion<typename SparseModelType::ValueType>> newRegions;
if (storm::solver::minimize(dir)) {
if (currBound < value.get() - storm::utility::convertNumber<ConstantType>(precision)) {
currRegion.split(currRegion.getCenterPoint(), newRegions);
storm::utility::Stopwatch boundsWatch(false);
auto numberOfPLACallsBounds = 0;
ConstantType initBound;
if (minimize) {
initBound = storm::utility::zero<ConstantType>();
} else {
initBound = storm::utility::one<ConstantType>();
}
if (useMonotonicity) {
if (this->isUseBoundsSet()) {
numberOfPLACallsBounds++;
numberOfPLACallsBounds++;
auto minBound = getBound(env, region, storm::solver::OptimizationDirection::Minimize, nullptr)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
auto maxBound = getBound(env, region, storm::solver::OptimizationDirection::Maximize, nullptr)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
if (minimize) {
initBound = minBound[*this->parametricModel->getInitialStates().begin()];
} else {
initBound = maxBound[*this->parametricModel->getInitialStates().begin()];
}
orderExtender->setMinValuesInit(minBound);
orderExtender->setMaxValuesInit(maxBound);
}
auto order = this->extendOrder(env, nullptr, region);
auto monRes = std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>>(new storm::analysis::LocalMonotonicityResult<VariableType>(order->getNumberOfStates()));
storm::utility::Stopwatch monotonicityWatch(true);
this->extendLocalMonotonicityResult(region, order, monRes);
monotonicityWatch.stop();
STORM_LOG_INFO(std::endl << "Total time for monotonicity checking: " << monotonicityWatch << "." << std::endl << std::endl);
if (minimize) {
regionQueue.emplace(region, order, monRes, initBound);
} else {
if (currBound > value.get() + storm::utility::convertNumber<ConstantType>(precision)) {
currRegion.split(currRegion.getCenterPoint(), newRegions);
}
regionQueue.emplace(region, order, monRes, initBound);
}
if (newRegions.empty()) {
coveredArea += storm::utility::convertNumber<ConstantType>(currRegion.area());
} else {
if (minimize) {
regionQueue.emplace(region, nullptr, nullptr, initBound);
} else {
regionQueue.emplace(region, nullptr, nullptr, initBound);
}
regionQueue.pop();
for (auto const& r : newRegions) {
regionQueue.emplace(r, currBound);
}
// The results
boost::optional<ConstantType> value;
Valuation valuation;
if (!initialValue) {
auto init = getGoodInitialPoint(env, region, dir, regionQueue.top().localMonRes);
value = storm::utility::convertNumber<ConstantType>(init.first);
valuation = std::move(init.second);
} else {
value = initialValue;
}
initialWatch.stop();
STORM_LOG_INFO(std::endl << "Total time for initial points: " << initialWatch << "." << std::endl << std::endl);
if (!initialValue) {
STORM_LOG_INFO("Initial value: " << value.get() << " at " << valuation);
} else {
STORM_LOG_INFO("Initial value: " << value.get() << " as provided by the user");
}
auto numberOfSplits = 0;
auto numberOfPLACalls = 0;
auto numberOfOrderCopies = 0;
auto numberOfMonResCopies = 0;
bool first = true;
storm::utility::Stopwatch loopWatch(true);
if (!(useMonotonicity && regionQueue.top().localMonRes->getGlobalMonotonicityResult()->isDone() && regionQueue.top().localMonRes->getGlobalMonotonicityResult()->isAllMonotonicity())) {
// Doing the extremal computation, only when we don't use monotonicity or there are possibly not monotone variables.
auto totalArea = storm::utility::convertNumber<ConstantType>(region.area());
auto coveredArea = storm::utility::zero<ConstantType>();
while (!regionQueue.empty()) {
assert (value);
auto currRegion = regionQueue.top().region;
auto order = regionQueue.top().order;
auto localMonotonicityResult = regionQueue.top().localMonRes;
auto currBound = regionQueue.top().bound;
STORM_LOG_INFO("Currently looking at region: " << currRegion);
std::vector<storm::storage::ParameterRegion<typename SparseModelType::ValueType>> newRegions;
// Check whether this region needs further investigation based on the bound of the parent region
bool investigateBounds = (minimize && currBound < value.get() - storm::utility::convertNumber<ConstantType>(precision))
|| (!minimize && currBound > value.get() + storm::utility::convertNumber<ConstantType>(precision));
if (investigateBounds) {
numberOfPLACalls++;
auto bounds = getBound(env, currRegion, dir, localMonotonicityResult)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
currBound = bounds[*this->parametricModel->getInitialStates().begin()];
// Check whether this region needs further investigation based on the bound of this region
bool lookAtRegion = (minimize && currBound < value.get() - storm::utility::convertNumber<ConstantType>(precision))
|| (!minimize && currBound > value.get() + storm::utility::convertNumber<ConstantType>(precision));
if (lookAtRegion) {
if (useMonotonicity) {
// Continue extending order/monotonicity result
bool changedOrder = false;
if (!order->getDoneBuilding() && orderExtender->isHope(order, currRegion)) {
if (numberOfCopiesOrder[order] != 1) {
numberOfCopiesOrder[order]--;
order = copyOrder(order);
numberOfOrderCopies++;
} else {
assert (numberOfCopiesOrder[order] == 1);
}
this->extendOrder(env, order, currRegion);
changedOrder = true;
}
if (changedOrder) {
assert(!localMonotonicityResult->isDone());
if (numberOfCopiesMonRes[localMonotonicityResult] != 1) {
numberOfCopiesMonRes[localMonotonicityResult]--;
localMonotonicityResult = localMonotonicityResult->copy();
numberOfMonResCopies++;
} else {
assert (numberOfCopiesMonRes[localMonotonicityResult] == 1);
}
this->extendLocalMonotonicityResult(currRegion, order, localMonotonicityResult);
STORM_LOG_INFO("Order and monotonicity result got extended");
}
}
// Check whether this region contains a new 'good' value and set this value
auto point = useMonotonicity ? currRegion.getPoint(dir, *(localMonotonicityResult->getGlobalMonotonicityResult())) : currRegion.getCenterPoint();
auto currValue = getInstantiationChecker().check(env, point)->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()];
if (!value || (minimize ? currValue <= value.get() : currValue >= value.get())) {
value = currValue;
valuation = point;
}
if ((minimize && currBound < value.get() - storm::utility::convertNumber<ConstantType>(precision))
|| (!minimize && currBound > value.get() + storm::utility::convertNumber<ConstantType>(precision))) {
// We will split the region in this case, but first we set the bounds to extend the order for the new regions.
if (useMonotonicity && this->isUseBoundsSet() && !order->getDoneBuilding()) {
boundsWatch.start();
numberOfPLACallsBounds++;
if (minimize) {
orderExtender->setMinMaxValues(order, bounds, getBound(env, currRegion, storm::solver::OptimizationDirection::Maximize, localMonotonicityResult)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector());
} else {
orderExtender->setMinMaxValues(order, getBound(env, currRegion, storm::solver::OptimizationDirection::Maximize, localMonotonicityResult)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector(), bounds);
}
boundsWatch.stop();
}
// Now split the region
if (useMonotonicity) {
this->splitSmart(currRegion, newRegions, order, *(localMonotonicityResult->getGlobalMonotonicityResult()), true);
} else if (this->isRegionSplitEstimateSupported()) {
auto empty = storm::analysis::MonotonicityResult<VariableType>();
this->splitSmart(currRegion, newRegions, order, empty, true);
} else {
currRegion.split(currRegion.getCenterPoint(), newRegions);
}
}
}
}
if (newRegions.empty()) {
// When the newRegions is empty we are done with the current region
coveredArea += storm::utility::convertNumber<ConstantType>(currRegion.area());
if (order != nullptr) {
numberOfCopiesOrder[order]--;
numberOfCopiesMonRes[localMonotonicityResult]--;
}
regionQueue.pop();
} else {
regionQueue.pop();
STORM_LOG_INFO("Splitting region " << currRegion << " into " << newRegions.size());
numberOfSplits++;
// Add the new regions to the queue
if (useMonotonicity) {
for (auto &r : newRegions) {
r.setBoundParent(storm::utility::convertNumber<CoefficientType>(currBound));
regionQueue.emplace(r, order, localMonotonicityResult, currBound);
}
if (numberOfCopiesOrder.find(order) != numberOfCopiesOrder.end()) {
numberOfCopiesOrder[order] += newRegions.size();
numberOfCopiesMonRes[localMonotonicityResult] += newRegions.size();
} else {
numberOfCopiesOrder[order] = newRegions.size();
numberOfCopiesMonRes[localMonotonicityResult] = newRegions.size();
}
} else {
for (auto &r : newRegions) {
r.setBoundParent(storm::utility::convertNumber<CoefficientType>(currBound));
regionQueue.emplace(r, nullptr, nullptr, currBound);
}
}
}
STORM_LOG_INFO("Current value : " << value.get() << ", current bound: " << currBound << ".");
STORM_LOG_INFO("Covered " << (coveredArea * storm::utility::convertNumber<ConstantType>(100.0) / totalArea) << "% of the region." << std::endl);
}
STORM_LOG_INFO("Current value : " << value.get() << ", current bound: " << regionQueue.top().bound << ".");
STORM_LOG_INFO("Covered " << (coveredArea * storm::utility::convertNumber<ConstantType>(100.0) / totalArea) << "% of the region.");
loopWatch.stop();
}
STORM_LOG_INFO("Total number of splits: " << numberOfSplits << std::endl);
STORM_PRINT("Total number of plaCalls: " << numberOfPLACalls << std::endl);
if (useMonotonicity) {
STORM_PRINT("Total number of plaCalls for bounds for monotonicity checking: " << numberOfPLACallsBounds << std::endl);
STORM_PRINT("Total number of copies of the order: " << numberOfOrderCopies << std::endl);
STORM_PRINT("Total number of copies of the local monotonicity result: " << numberOfMonResCopies
<< std::endl);
}
STORM_LOG_INFO(std::endl << "Total time for region refinement: " << loopWatch << "." << std::endl << std::endl);
STORM_LOG_INFO(std::endl << "Total time for additional bounds: " << boundsWatch << "." << std::endl << std::endl);
return std::make_pair(storm::utility::convertNumber<typename SparseModelType::ValueType>(value.get()), valuation);
}
template <typename SparseModelType, typename ConstantType>
std::pair<typename SparseModelType::ValueType, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::computeExtremalValue(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dir, typename SparseModelType::ValueType const& precision) {
return computeExtremalValue(env, region, dir, precision, boost::none);
}
template <typename SparseModelType, typename ConstantType>
bool SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::checkExtremalValue(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dir, typename SparseModelType::ValueType const& precision, typename SparseModelType::ValueType const& valueToCheck) {
auto res = computeExtremalValue(env, region, dir, precision, storm::utility::convertNumber<ConstantType>(valueToCheck)).first;
return storm::solver::minimize(dir) ? storm::utility::convertNumber<ConstantType>(res) >= storm::utility::convertNumber<ConstantType>(valueToCheck) : storm::utility::convertNumber<ConstantType>(res) <= storm::utility::convertNumber<ConstantType>(valueToCheck);
}
template <typename SparseModelType, typename ConstantType>
SparseModelType const& SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getConsideredParametricModel() const {
return *parametricModel;
}
template <typename SparseModelType, typename ConstantType>
CheckTask<storm::logic::Formula, ConstantType> const& SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getCurrentCheckTask() const {
return *currentCheckTask;
@ -250,10 +500,108 @@ namespace storm {
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Parameter lifting is not supported for the given property.");
}
template<typename SparseModelType, typename ConstantType>
std::shared_ptr<storm::analysis::Order> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::copyOrder(std::shared_ptr<storm::analysis::Order> order) {
auto res = order->copy();
if (orderExtender) {
orderExtender->setUnknownStates(order, res);
orderExtender->copyMinMax(order, res);
}
return res;
}
template<typename SparseModelType, typename ConstantType>
std::pair<typename SparseModelType::ValueType, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::checkForPossibleMonotonicity(Environment const& env,
const storage::ParameterRegion<typename SparseModelType::ValueType> &region,
std::set<VariableType>& possibleMonotoneIncrParameters,
std::set<VariableType>& possibleMonotoneDecrParameters,
std::set<VariableType>& possibleNotMonotoneParameters,
std::set<VariableType>const& consideredVariables,
storm::solver::OptimizationDirection const& dir) {
bool minimize = storm::solver::minimize(dir);
typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation Valuation;
typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::CoefficientType CoefficientType;
ConstantType value = storm::solver::minimize(dir) ? 1 : 0;
Valuation valuation;
for (auto& var : consideredVariables) {
ConstantType previousCenter = -1;
bool monDecr = true;
bool monIncr = true;
auto valuationCenter = region.getCenterPoint();
valuationCenter[var] = region.getLowerBoundary(var);
// TODO: make cmdline argument or 1/precision
int numberOfSamples = 50;
auto stepSize = (region.getUpperBoundary(var) - region.getLowerBoundary(var)) / (numberOfSamples - 1);
while (valuationCenter[var] <= region.getUpperBoundary(var)) {
// Create valuation
ConstantType valueCenter = getInstantiationChecker().check(env, valuationCenter)->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()];
if (storm::solver::minimize(dir) ? valueCenter <= value : valueCenter >= value) {
value = valueCenter;
valuation = valuationCenter;
}
// Calculate difference with result for previous valuation
ConstantType diffCenter = previousCenter - valueCenter;
assert (previousCenter == -1 || (diffCenter >= -1 && diffCenter <= 1));
if (previousCenter != -1) {
assert (previousCenter != -1 && previousCenter != -1);
monDecr &= diffCenter > 0 && diffCenter > 0 && diffCenter > 0; // then previous value is larger than the current value from the initial states
monIncr &= diffCenter < 0 && diffCenter < 0 && diffCenter < 0;
}
previousCenter = valueCenter;
if (!monDecr && ! monIncr) {
break;
}
valuationCenter[var] += stepSize;
}
if (monIncr) {
possibleMonotoneParameters.insert(var);
possibleMonotoneIncrParameters.insert(var);
} else if (monDecr) {
possibleMonotoneParameters.insert(var);
possibleMonotoneDecrParameters.insert(var);
} else {
possibleNotMonotoneParameters.insert(var);
}
}
return std::make_pair(storm::utility::convertNumber<typename SparseModelType::ValueType>(value), std::move(valuation));
}
template<typename SparseModelType, typename ConstantType>
std::pair<typename SparseModelType::ValueType, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getGoodInitialPoint(const Environment &env, const storage::ParameterRegion<typename SparseModelType::ValueType> &region, const OptimizationDirection &dir, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonRes) {
typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation Valuation;
typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::CoefficientType CoefficientType;
ConstantType value = storm::solver::minimize(dir) ? 1 : 0;
Valuation valuation;
std::set<VariableType> monIncr, monDecr, notMon, notMonFirst;
STORM_LOG_INFO("Number of parameters: " << region.getVariables().size() << std::endl;);
if (localMonRes != nullptr) {
localMonRes->getGlobalMonotonicityResult()->splitBasedOnMonotonicity(region.getVariables(), monIncr, monDecr, notMonFirst);
auto numMon = monIncr.size() + monDecr.size();
STORM_LOG_INFO("Number of monotone parameters: " << numMon << std::endl;);
if (numMon < region.getVariables().size()) {
checkForPossibleMonotonicity(env, region, monIncr, monDecr, notMon, notMonFirst, dir);
STORM_LOG_INFO("Number of possible monotone parameters: " << (monIncr.size() + monDecr.size() - numMon) << std::endl;);
STORM_LOG_INFO("Number of definitely not monotone parameters: " << notMon.size() << std::endl;);
}
valuation = region.getPoint(dir, monIncr, monDecr);
} else {
valuation = region.getCenterPoint();
}
value = getInstantiationChecker().check(env, valuation)->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()];
return std::make_pair(storm::utility::convertNumber<typename SparseModelType::ValueType>(value), std::move(valuation));
}
template class SparseParameterLiftingModelChecker<storm::models::sparse::Dtmc<storm::RationalFunction>, double>;
template class SparseParameterLiftingModelChecker<storm::models::sparse::Mdp<storm::RationalFunction>, double>;
template class SparseParameterLiftingModelChecker<storm::models::sparse::Dtmc<storm::RationalFunction>, storm::RationalNumber>;
template class SparseParameterLiftingModelChecker<storm::models::sparse::Mdp<storm::RationalFunction>, storm::RationalNumber>;
}
}

35
src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.h

@ -22,14 +22,17 @@ namespace storm {
template <typename SparseModelType, typename ConstantType>
class SparseParameterLiftingModelChecker : public RegionModelChecker<typename SparseModelType::ValueType> {
public:
typedef typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType VariableType;
typedef typename storm::analysis::MonotonicityResult<VariableType>::Monotonicity Monotonicity;
SparseParameterLiftingModelChecker();
virtual ~SparseParameterLiftingModelChecker() = default;
/*!
* Analyzes the given region by means of parameter lifting.
*/
virtual RegionResult analyzeRegion(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, RegionResultHypothesis const& hypothesis = RegionResultHypothesis::Unknown, RegionResult const& initialResult = RegionResult::Unknown, bool sampleVerticesOfRegion = false) override;
virtual RegionResult analyzeRegion(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, RegionResultHypothesis const& hypothesis = RegionResultHypothesis::Unknown, RegionResult const& initialResult = RegionResult::Unknown, bool sampleVerticesOfRegion = false, std::shared_ptr<storm::analysis::Order> reachabilityOrder = nullptr, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult = nullptr) override;
/*!
* Analyzes the 2^#parameters corner points of the given region.
@ -43,9 +46,9 @@ namespace storm {
* @param region the region on which parameter lifting is applied
* @param dirForParameters The optimization direction for the parameter choices. If this is, e.g., minimize, then the returned result will be a lower bound for all results induced by the parameter evaluations inside the region.
*/
std::unique_ptr<CheckResult> check(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters);
std::unique_ptr<CheckResult> check(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, std::shared_ptr<storm::analysis::Order> reachabilityOrder = nullptr, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult = nullptr);
std::unique_ptr<QuantitativeCheckResult<ConstantType>> getBound(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters);
std::unique_ptr<QuantitativeCheckResult<ConstantType>> getBound(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult = nullptr);
virtual typename SparseModelType::ValueType getBoundAtInitState(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters) override;
@ -55,7 +58,8 @@ namespace storm {
* The actual maximum (minimum) lies in the interval [v, v+precision] ([v-precision, v])
*/
virtual std::pair<typename SparseModelType::ValueType, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation> computeExtremalValue(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, typename SparseModelType::ValueType const& precision) override;
virtual bool checkExtremalValue(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, typename SparseModelType::ValueType const& precision, typename SparseModelType::ValueType const& valueToCheck) override;
SparseModelType const& getConsideredParametricModel() const;
CheckTask<storm::logic::Formula, ConstantType> const& getCurrentCheckTask() const;
@ -73,17 +77,28 @@ namespace storm {
virtual storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& getInstantiationChecker() = 0;
virtual std::unique_ptr<CheckResult> computeQuantitativeValues(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters) = 0;
virtual storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& getInstantiationCheckerSAT();
virtual storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& getInstantiationCheckerVIO();
virtual std::unique_ptr<CheckResult> computeQuantitativeValues(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult = nullptr) = 0;
std::shared_ptr<SparseModelType> parametricModel;
std::unique_ptr<CheckTask<storm::logic::Formula, ConstantType>> currentCheckTask;
ConstantType lastValue;
boost::optional<storm::analysis::OrderExtender<typename SparseModelType::ValueType, ConstantType>> orderExtender;
std::pair<typename SparseModelType::ValueType, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation> checkForPossibleMonotonicity(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, std::set<VariableType>& possibleMonotoneIncrParameters, std::set<VariableType>& possibleMonotoneDecrParameters, std::set<VariableType>& possibleNotMonotoneParameters, std::set<VariableType>const& consideredVariables, storm::solver::OptimizationDirection const& dir);
std::pair<typename SparseModelType::ValueType, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation> getGoodInitialPoint(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dir, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonRes);
std::set<VariableType> possibleMonotoneParameters;
private:
// store the current formula. Note that currentCheckTask only stores a reference to the formula.
std::shared_ptr<storm::logic::Formula const> currentFormula;
std::shared_ptr<storm::analysis::Order> copyOrder(std::shared_ptr<storm::analysis::Order> order);
std::map<std::shared_ptr<storm::analysis::Order>, uint_fast64_t> numberOfCopiesOrder;
std::map<std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>>, uint_fast64_t> numberOfCopiesMonRes;
std::pair<typename SparseModelType::ValueType, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation> computeExtremalValue(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, typename SparseModelType::ValueType const& precision, boost::optional<ConstantType> const& initialValue);
};
}
}

8
src/storm-pars/modelchecker/region/ValidatingSparseDtmcParameterLiftingModelChecker.cpp

@ -13,20 +13,20 @@ namespace storm {
ValidatingSparseDtmcParameterLiftingModelChecker<SparseModelType, ImpreciseType, PreciseType>::ValidatingSparseDtmcParameterLiftingModelChecker() {
// Intentionally left empty
}
template <typename SparseModelType, typename ImpreciseType, typename PreciseType>
void ValidatingSparseDtmcParameterLiftingModelChecker<SparseModelType, ImpreciseType, PreciseType>::specify(Environment const& env, std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask, bool generateRegionSplitEstimates, bool allowModelSimplifications) {
STORM_LOG_ASSERT(this->canHandle(parametricModel, checkTask), "specified model and formula can not be handled by this.");
auto dtmc = parametricModel->template as<SparseModelType>();
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<SparseModelType>(*dtmc);
if (!simplifier.simplify(checkTask.getFormula())) {
STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Simplifying the model was not successfull.");
}
auto simplifiedTask = checkTask.substituteFormula(*simplifier.getSimplifiedFormula());
impreciseChecker.specify(env, simplifier.getSimplifiedModel(), simplifiedTask, false, true);
preciseChecker.specify(env, simplifier.getSimplifiedModel(), simplifiedTask, false, true);
}

2
src/storm-pars/modelchecker/region/ValidatingSparseDtmcParameterLiftingModelChecker.h

@ -11,7 +11,7 @@ namespace storm {
public:
ValidatingSparseDtmcParameterLiftingModelChecker();
virtual ~ValidatingSparseDtmcParameterLiftingModelChecker() = default;
virtual void specify(Environment const& env, std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask, bool generateRegionSplitEstimates = false, bool allowModelSimplifications = true) override;
protected:

10
src/storm-pars/modelchecker/region/ValidatingSparseMdpParameterLiftingModelChecker.cpp

@ -13,21 +13,21 @@ namespace storm {
ValidatingSparseMdpParameterLiftingModelChecker<SparseModelType, ImpreciseType, PreciseType>::ValidatingSparseMdpParameterLiftingModelChecker() {
// Intentionally left empty
}
template <typename SparseModelType, typename ImpreciseType, typename PreciseType>
void ValidatingSparseMdpParameterLiftingModelChecker<SparseModelType, ImpreciseType, PreciseType>::specify(Environment const& env, std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask, bool generateRegionSplitEstimates, bool allowModelSimplifications) {
STORM_LOG_ASSERT(this->canHandle(parametricModel, checkTask), "specified model and formula can not be handled by this.");
auto mdp = parametricModel->template as<SparseModelType>();
auto simplifier = storm::transformer::SparseParametricMdpSimplifier<SparseModelType>(*mdp);
if (!simplifier.simplify(checkTask.getFormula())) {
STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Simplifying the model was not successfull.");
}
auto simplifiedTask = checkTask.substituteFormula(*simplifier.getSimplifiedFormula());
impreciseChecker.specify(env, simplifier.getSimplifiedModel(), simplifiedTask, false, true);
preciseChecker.specify(env, simplifier.getSimplifiedModel(), simplifiedTask, false, true);
}

2
src/storm-pars/modelchecker/region/ValidatingSparseMdpParameterLiftingModelChecker.h

@ -11,7 +11,7 @@ namespace storm {
public:
ValidatingSparseMdpParameterLiftingModelChecker();
virtual ~ValidatingSparseMdpParameterLiftingModelChecker() = default;
virtual void specify(Environment const& env, std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask, bool generateRegionSplitEstimates = false, bool allowModelSimplifications = true) override;
protected:

4
src/storm-pars/modelchecker/region/ValidatingSparseParameterLiftingModelChecker.cpp

@ -28,9 +28,9 @@ namespace storm {
}
template <typename SparseModelType, typename ImpreciseType, typename PreciseType>
RegionResult ValidatingSparseParameterLiftingModelChecker<SparseModelType, ImpreciseType, PreciseType>::analyzeRegion(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, RegionResultHypothesis const& hypothesis, RegionResult const& initialResult, bool sampleVerticesOfRegion) {
RegionResult ValidatingSparseParameterLiftingModelChecker<SparseModelType, ImpreciseType, PreciseType>::analyzeRegion(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, RegionResultHypothesis const& hypothesis, RegionResult const& initialResult, bool sampleVerticesOfRegion, std::shared_ptr<storm::analysis::Order> reachabilityOrder, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult) {
RegionResult currentResult = getImpreciseChecker().analyzeRegion(env, region, hypothesis, initialResult, false);
if (currentResult == RegionResult::AllSat || currentResult == RegionResult::AllViolated) {

2
src/storm-pars/modelchecker/region/ValidatingSparseParameterLiftingModelChecker.h

@ -23,7 +23,7 @@ namespace storm {
* We first apply unsound solution methods (standard value iteratio with doubles) and then validate the obtained result
* by means of exact and soud methods.
*/
virtual RegionResult analyzeRegion(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, RegionResultHypothesis const& hypothesis = RegionResultHypothesis::Unknown, RegionResult const& initialResult = RegionResult::Unknown, bool sampleVerticesOfRegion = false) override;
virtual RegionResult analyzeRegion(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, RegionResultHypothesis const& hypothesis = RegionResultHypothesis::Unknown, RegionResult const& initialResult = RegionResult::Unknown, bool sampleVerticesOfRegion = false, std::shared_ptr<storm::analysis::Order> reachabilityOrder = nullptr, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult = nullptr) override;
protected:

73
src/storm-pars/parser/MonotonicityParser.cpp

@ -0,0 +1,73 @@
#include "storm-pars/parser/MonotonicityParser.h"
#include <storm/exceptions/WrongFormatException.h>
#include "storm/utility/macros.h"
#include "storm/exceptions/InvalidArgumentException.h"
#include "storm/utility/constants.h"
#include "storm/io/file.h"
namespace storm {
namespace parser {
template<typename VariableType>
std::pair<std::set<VariableType>, std::set<VariableType>> MonotonicityParser<VariableType>::parseMonotoneVariablesFromFile(std::string const& fileName, std::set<VariableType> const& consideredVariables) {
// Open file and initialize result.
std::ifstream inputFileStream;
storm::utility::openFile(fileName, inputFileStream);
std::set<VariableType> monotoneIncrVars;
std::set<VariableType> monotoneDecrVars;
// Now try to parse the contents of the file.
try {
std::string fileContent((std::istreambuf_iterator<char>(inputFileStream)), (std::istreambuf_iterator<char>()));
std::vector<std::string> fileSplitted;
boost::split(fileSplitted, fileContent, boost::is_any_of(";"));
STORM_LOG_THROW(fileSplitted.size() == 2, storm::exceptions::WrongFormatException, "Expecting content to contain \";\" between monotone variables");
std::vector<std::string> monotoneIncrVarsString;
boost::split(monotoneIncrVarsString, fileSplitted[0], boost::is_any_of(" "));
std::vector<std::string> monotoneDecrVarsString;
boost::split(monotoneDecrVarsString, fileSplitted[0], boost::is_any_of(" "));
// TODO: throw errors if file not formatted correctly
for (auto varString : monotoneIncrVarsString) {
VariableType var;
for (auto const& v : consideredVariables) {
std::stringstream stream;
stream << v;
if (varString == stream.str()) {
var = v;
break;
}
}
monotoneIncrVars.insert(var);
}
for (auto varString : monotoneDecrVarsString) {
VariableType var;
for (auto const& v : consideredVariables) {
std::stringstream stream;
stream << v;
if (varString == stream.str()) {
var = v;
break;
}
}
monotoneDecrVars.insert(var);
}
} catch(std::exception& e) {
// In case of an exception properly close the file before passing exception.
storm::utility::closeFile(inputFileStream);
throw e;
}
// Close the stream in case everything went smoothly and return result.
storm::utility::closeFile(inputFileStream);
return {std::move(monotoneIncrVars), std::move(monotoneDecrVars)};
}
template class MonotonicityParser<storm::RationalFunctionVariable>;
}
}

16
src/storm-pars/parser/MonotonicityParser.h

@ -0,0 +1,16 @@
#pragma once
#include <set>
#include <string>
namespace storm {
namespace parser {
template<typename VariableType>
class MonotonicityParser{
public:
static std::pair<std::set<VariableType>, std::set<VariableType>> parseMonotoneVariablesFromFile(std::string const& fileName, std::set<VariableType> const& consideredVariables);
};
}
}

34
src/storm-pars/parser/ParameterRegionParser.cpp

@ -42,7 +42,7 @@ namespace storm {
}
template<typename ParametricType>
storm::storage::ParameterRegion<ParametricType> ParameterRegionParser<ParametricType>::parseRegion(std::string const& regionString, std::set<VariableType> const& consideredVariables) {
storm::storage::ParameterRegion<ParametricType> ParameterRegionParser<ParametricType>::parseRegion(std::string const& regionString, std::set<VariableType> const& consideredVariables, boost::optional<int> splittingThreshold) {
Valuation lowerBoundaries;
Valuation upperBoundaries;
std::vector<std::string> parameterBoundaries;
@ -58,24 +58,46 @@ namespace storm {
STORM_LOG_THROW(lowerBoundaries.count(v) > 0, storm::exceptions::WrongFormatException, "Variable " << v << " was not defined in region string.");
STORM_LOG_ASSERT(upperBoundaries.count(v) > 0, "Variable " << v << " has a lower but not an upper bound.");
}
return storm::storage::ParameterRegion<ParametricType>(std::move(lowerBoundaries), std::move(upperBoundaries));
auto res = storm::storage::ParameterRegion<ParametricType>(std::move(lowerBoundaries), std::move(upperBoundaries));
if (splittingThreshold) {
res.setSplitThreshold(splittingThreshold.get());
}
return res;
}
template<typename ParametricType>
storm::storage::ParameterRegion<ParametricType> ParameterRegionParser<ParametricType>::createRegion(std::string const& regionBound, std::set<VariableType> const& consideredVariables, boost::optional<int> splittingThreshold) {
Valuation lowerBoundaries;
Valuation upperBoundaries;
std::vector<std::string> parameterBoundaries;
CoefficientType bound = storm::utility::convertNumber<CoefficientType>(regionBound);
STORM_LOG_THROW(0 < bound && bound < 1, storm::exceptions::WrongFormatException, "Bound must be between 0 and 1, " << bound << " is not.");
for (auto const& v : consideredVariables) {
lowerBoundaries.emplace(std::make_pair(v, 0+bound));
upperBoundaries.emplace(std::make_pair(v, 1-bound));
}
auto res = storm::storage::ParameterRegion<ParametricType>(std::move(lowerBoundaries), std::move(upperBoundaries));
if (splittingThreshold) {
res.setSplitThreshold(splittingThreshold.get());
}
return res;
}
template<typename ParametricType>
std::vector<storm::storage::ParameterRegion<ParametricType>> ParameterRegionParser<ParametricType>::parseMultipleRegions(std::string const& regionsString, std::set<VariableType> const& consideredVariables) {
std::vector<storm::storage::ParameterRegion<ParametricType>> ParameterRegionParser<ParametricType>::parseMultipleRegions(std::string const& regionsString, std::set<VariableType> const& consideredVariables, boost::optional<int> splittingThreshold) {
std::vector<storm::storage::ParameterRegion<ParametricType>> result;
std::vector<std::string> regionsStrVec;
boost::split(regionsStrVec, regionsString, boost::is_any_of(";"));
for (auto const& regionStr : regionsStrVec){
if (!std::all_of(regionStr.begin(),regionStr.end(), ::isspace)){ //skip this string if it only consists of space
result.emplace_back(parseRegion(regionStr, consideredVariables));
result.emplace_back(parseRegion(regionStr, consideredVariables, splittingThreshold));
}
}
return result;
}
template<typename ParametricType>
std::vector<storm::storage::ParameterRegion<ParametricType>> ParameterRegionParser<ParametricType>::parseMultipleRegionsFromFile(std::string const& fileName, std::set<VariableType> const& consideredVariables) {
std::vector<storm::storage::ParameterRegion<ParametricType>> ParameterRegionParser<ParametricType>::parseMultipleRegionsFromFile(std::string const& fileName, std::set<VariableType> const& consideredVariables, boost::optional<int> splittingThreshold) {
// Open file and initialize result.
std::ifstream inputFileStream;
@ -86,7 +108,7 @@ namespace storm {
// Now try to parse the contents of the file.
try {
std::string fileContent((std::istreambuf_iterator<char>(inputFileStream)), (std::istreambuf_iterator<char>()));
result = parseMultipleRegions(fileContent, consideredVariables);
result = parseMultipleRegions(fileContent, consideredVariables, splittingThreshold);
} catch(std::exception& e) {
// In case of an exception properly close the file before passing exception.
storm::utility::closeFile(inputFileStream);

7
src/storm-pars/parser/ParameterRegionParser.h

@ -25,20 +25,21 @@ namespace storm {
* Parse a single region from a string of the form "0.3<=p<=0.5,0.4<=q<=0.7".
*
*/
static storm::storage::ParameterRegion<ParametricType> parseRegion(std::string const& regionString, std::set<VariableType> const& consideredVariables);
static storm::storage::ParameterRegion<ParametricType> parseRegion(std::string const& regionString, std::set<VariableType> const& consideredVariables, boost::optional<int> splittingThreshold = boost::none);
static storm::storage::ParameterRegion<ParametricType> createRegion(std::string const& regionBound, std::set<VariableType> const& consideredVariables, boost::optional<int> splittingThreshold = boost::none);
/*
* Parse a vector of region from a string of the form "0.3<=p<=0.5,0.4<=q<=0.7;0.1<=p<=0.3,0.2<=q<=0.4".
*
*/
static std::vector<storm::storage::ParameterRegion<ParametricType>> parseMultipleRegions(std::string const& regionsString, std::set<VariableType> const& consideredVariables);
static std::vector<storm::storage::ParameterRegion<ParametricType>> parseMultipleRegions(std::string const& regionsString, std::set<VariableType> const& consideredVariables, boost::optional<int> splittingThreshold = boost::none);
/*
* Parse multiple regions from a file
*
*/
static std::vector<storm::storage::ParameterRegion<ParametricType>> parseMultipleRegionsFromFile(std::string const& fileName, std::set<VariableType> const& consideredVariables);
static std::vector<storm::storage::ParameterRegion<ParametricType>> parseMultipleRegionsFromFile(std::string const& fileName, std::set<VariableType> const& consideredVariables, boost::optional<int> splittingThreshold = boost::none);
};
}

76
src/storm-pars/settings/modules/MonotonicitySettings.cpp

@ -11,43 +11,83 @@
namespace storm {
namespace settings {
namespace modules {
const std::string MonotonicitySettings::moduleName = "monotonicity";
// TODO @Svenja, check what the module prefix is, maybe instead of doing mon- we could set this to true for the onces where we now have mon-"optionname"
const std::string MonotonicitySettings::moduleName = "mon";
const std::string MonotonicitySettings::monotonicityAnalysis = "monotonicity-analysis";
const std::string MonotonicitySettings::sccElimination = "mon-elim-scc";
const std::string MonotonicitySettings::validateAssumptions = "mon-validate-assumptions";
const std::string MonotonicitySettings::samplesMonotonicityAnalysis = "mon-samples";
const std::string MonotonicitySettings::precision = "mon-precision";
const std::string MonotonicitySettings::monotonicityAnalysisShortName = "ma";
const std::string MonotonicitySettings::usePLABounds = "useBounds";
const std::string MonotonicitySettings::sccElimination = "eliminateSCCs";
const std::string MonotonicitySettings::samplesMonotonicityAnalysis = "samples";
const std::string MonotonicitySettings::dotOutput = "dotOutput";
const std::string MonotonicitySettings::exportMonotonicityName = "exportMonotonicity";
const std::string MonotonicitySettings::monSolution ="solutionFunction";
const std::string MonotonicitySettings::monSolutionShortName ="msf";
const std::string MonotonicitySettings::monotonicityThreshold ="depth";
const std::string MonotonicitySettings::monotoneParameters ="parameters";
MonotonicitySettings::MonotonicitySettings() : ModuleSettings(moduleName) {
this->addOption(storm::settings::OptionBuilder(moduleName, monotonicityAnalysis, false, "Sets whether monotonicity analysis is done").setIsAdvanced().build());
this->addOption(storm::settings::OptionBuilder(moduleName, sccElimination, false, "Sets whether SCCs should be eliminated in the monotonicity analysis").setIsAdvanced().build());
this->addOption(storm::settings::OptionBuilder(moduleName, validateAssumptions, false, "Sets whether assumptions made in monotonicity analysis are validated").setIsAdvanced().build());
this->addOption(storm::settings::OptionBuilder(moduleName, samplesMonotonicityAnalysis, false, "Sets whether monotonicity should be checked on samples").setIsAdvanced()
.addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("mon-samples", "The number of samples taken in monotonicity-analysis can be given, default is 0, no samples").setDefaultValueUnsignedInteger(0).build()).build());
this->addOption(storm::settings::OptionBuilder(moduleName, precision, false, "Sets precision of monotonicity checking on samples").setIsAdvanced()
.addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("mon-precision", "The precision of checking monotonicity on samples, default is 1e-6").setDefaultValueDouble(0.000001).build()).build());
this->addOption(storm::settings::OptionBuilder(moduleName, monotonicityAnalysis, false, "Sets whether monotonicity analysis is done").setIsAdvanced().setShortName(monotonicityAnalysisShortName).build());
this->addOption(storm::settings::OptionBuilder(moduleName, usePLABounds, true, "Sets whether pla bounds should be used for monotonicity analysis").setIsAdvanced().build());
this->addOption(storm::settings::OptionBuilder(moduleName, sccElimination, true, "Sets whether SCCs should be eliminated in the monotonicity analysis").setIsAdvanced().build());
this->addOption(storm::settings::OptionBuilder(moduleName, samplesMonotonicityAnalysis, true, "Sets whether monotonicity should be checked on samples").setIsAdvanced()
.addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument(samplesMonotonicityAnalysis, "The number of samples taken in monotonicity-analysis can be given, default is 0, no samples").setDefaultValueUnsignedInteger(0).build()).build());
this->addOption(storm::settings::OptionBuilder(moduleName, monSolution, true, "Sets whether monotonicity should be checked on solution function or reachability order").setIsAdvanced().setShortName(monSolutionShortName).build());
this->addOption(storm::settings::OptionBuilder(moduleName, dotOutput, true, "Sets whether a dot output of the ROs is needed").setIsAdvanced().addArgument(storm::settings::ArgumentBuilder::createStringArgument("dotFilename", "The output file.").setDefaultValueString("dotOutput").makeOptional().build()).build());
this->addOption(storm::settings::OptionBuilder(moduleName, exportMonotonicityName, true, "Exports the result of monotonicity checking to the given file.").setIsAdvanced().addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "The output file.").build()).build());
this->addOption(storm::settings::OptionBuilder(moduleName, monotonicityThreshold, true, "Sets for region refinement after which depth whether monotonicity checking should be used.").setIsAdvanced()
.addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument(monotonicityThreshold, "The depth threshold from which on monotonicity is used for Parameter Lifting").setDefaultValueUnsignedInteger(0).build()).build());
this->addOption(storm::settings::OptionBuilder(moduleName, monotoneParameters, true, "Sets monotone parameters from file.").setIsAdvanced().addArgument(storm::settings::ArgumentBuilder::createStringArgument("monotoneParametersFilename", "The file where the monotone parameters are set").build()).build());
}
bool MonotonicitySettings::isMonotonicityAnalysisSet() const {
return this->getOption(monotonicityAnalysis).getHasOptionBeenSet();
}
bool MonotonicitySettings::isUsePLABoundsSet() const {
return this->getOption(usePLABounds).getHasOptionBeenSet();
}
bool MonotonicitySettings::isSccEliminationSet() const {
return this->getOption(sccElimination).getHasOptionBeenSet();
}
bool MonotonicitySettings::isValidateAssumptionsSet() const {
return this->getOption(validateAssumptions).getHasOptionBeenSet();
bool MonotonicitySettings::isDotOutputSet() const {
return this->getOption(dotOutput).getHasOptionBeenSet();
}
bool MonotonicitySettings::isMonotoneParametersSet() const {
return this->getOption(monotoneParameters).getHasOptionBeenSet();
}
std::string MonotonicitySettings::getDotOutputFilename() const {
return this->getOption(dotOutput).getArgumentByName("dotFilename").getValueAsString();
}
std::string MonotonicitySettings::getMonotoneParameterFilename() const {
return this->getOption(monotoneParameters).getArgumentByName("monotoneParametersFilename").getValueAsString();
}
uint_fast64_t MonotonicitySettings::getNumberOfSamples() const {
return this->getOption(samplesMonotonicityAnalysis).getArgumentByName("mon-samples").getValueAsUnsignedInteger();
return this->getOption(samplesMonotonicityAnalysis).getArgumentByName("samples").getValueAsUnsignedInteger();
}
bool MonotonicitySettings::isExportMonotonicitySet() const {
return this->getOption(exportMonotonicityName).getHasOptionBeenSet();
}
std::string MonotonicitySettings::getExportMonotonicityFilename() const {
return this->getOption(exportMonotonicityName).getArgumentByName("filename").getValueAsString();
}
uint_fast64_t MonotonicitySettings::getMonotonicityThreshold() const {
return this->getOption(monotonicityThreshold).getArgumentByName("depth").getValueAsUnsignedInteger();
}
double MonotonicitySettings::getMonotonicityAnalysisPrecision() const {
return this->getOption(precision).getArgumentByName("mon-precision").getValueAsDouble();
bool MonotonicitySettings::isMonSolutionSet() const {
return this->getOption(monSolution).getHasOptionBeenSet();
}
} // namespace modules
} // namespace settings

45
src/storm-pars/settings/modules/MonotonicitySettings.h

@ -23,15 +23,26 @@ namespace storm {
*/
bool isMonotonicityAnalysisSet() const;
bool isUsePLABoundsSet() const;
/*!
* Retrieves whether SCCs in the monotonicity analysis should be eliminated.
*/
bool isSccEliminationSet() const;
/*!
* Retrieves whether assumptions in monotonicity analysis should be validated
* Retrieves whether a dot output of the reachability orders should be given
*/
bool isDotOutputSet() const;
bool isMonotoneParametersSet() const;
/*!
* Retrieves the name of the file for a possible dot output
*/
bool isValidateAssumptionsSet() const;
std::string getDotOutputFilename() const;
std::string getMonotoneParameterFilename() const;
/*!
* Retrieves the number of samples used for sampling in the monotonicity analysis
@ -39,18 +50,38 @@ namespace storm {
uint_fast64_t getNumberOfSamples() const;
/*!
* Retrieves the precision for the extremal value
*/
double getMonotonicityAnalysisPrecision() const;
*
*/
bool isExportMonotonicitySet() const;
bool isMonSolutionSet() const;
/*!
*
*/
std::string getExportMonotonicityFilename() const;
/*!
* Retrieves the depth threshold from which on monotonicity should be used in parameter lifting
*/
uint64_t getMonotonicityThreshold() const;
const static std::string moduleName;
private:
const static std::string monotonicityAnalysis;
const static std::string monotonicityAnalysisShortName;
const static std::string usePLABounds;
const static std::string sccElimination;
const static std::string validateAssumptions;
const static std::string samplesMonotonicityAnalysis;
const static std::string precision;
const static std::string dotOutput;
static const std::string exportMonotonicityName;
const static std::string monotonicityThreshold;
const static std::string monotoneParameters;
const static std::string monSolution;
const static std::string monSolutionShortName;
};
} // namespace modules

11
src/storm-pars/settings/modules/ParametricSettings.cpp

@ -21,6 +21,8 @@ namespace storm {
const std::string ParametricSettings::samplesOptionName = "samples";
const std::string ParametricSettings::samplesGraphPreservingOptionName = "samples-graph-preserving";
const std::string ParametricSettings::sampleExactOptionName = "sample-exact";
const std::string ParametricSettings::useMonotonicityName = "use-monotonicity";
// const std::string ParametricSettings::onlyGlobalName = "onlyGlobal";
ParametricSettings::ParametricSettings() : ModuleSettings(moduleName) {
this->addOption(storm::settings::OptionBuilder(moduleName, exportResultOptionName, false, "A path to a file where the parametric result should be saved.")
@ -32,6 +34,8 @@ namespace storm {
.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());
this->addOption(storm::settings::OptionBuilder(moduleName, useMonotonicityName, false, "If set, monotonicity will be used.").build());
// this->addOption(storm::settings::OptionBuilder(moduleName, onlyGlobalName, false, "If set, only global monotonicity will be used.").build());
}
bool ParametricSettings::exportResultToFile() const {
@ -66,6 +70,13 @@ namespace storm {
return this->getOption(sampleExactOptionName).getHasOptionBeenSet();
}
bool ParametricSettings::isUseMonotonicitySet() const {
return this->getOption(useMonotonicityName).getHasOptionBeenSet();
}
// bool ParametricSettings::isOnlyGlobalSet() const {
// return this->getOption(onlyGlobalName).getHasOptionBeenSet();
// }
} // namespace modules
} // namespace settings
} // namespace storm

10
src/storm-pars/settings/modules/ParametricSettings.h

@ -64,6 +64,13 @@ namespace storm {
*/
bool isSampleExactSet() const;
/*!
* Retrieves whether monotonicity should be used
*/
bool isUseMonotonicitySet() const;
// bool isOnlyGlobalSet() const;
const static std::string moduleName;
private:
@ -75,6 +82,9 @@ namespace storm {
const static std::string samplesOptionName;
const static std::string samplesGraphPreservingOptionName;
const static std::string sampleExactOptionName;
const static std::string useMonotonicityName;
// const static std::string onlyGlobalName;
};
} // namespace modules

57
src/storm-pars/settings/modules/RegionSettings.cpp

@ -1,10 +1,11 @@
#include <storm-pars/modelchecker/region/RegionResultHypothesis.h>
#include "storm-pars/settings/modules/RegionSettings.h"
#include "storm/settings/Option.h"
#include "storm/settings/SettingsManager.h"
#include "storm/settings/modules/GeneralSettings.h"
#include "storm/settings/OptionBuilder.h"
#include "storm/settings/ArgumentBuilder.h"
#include "storm/settings/Argument.h"
#include "storm/utility/macros.h"
#include "storm/exceptions/IllegalArgumentValueException.h"
@ -17,10 +18,13 @@ namespace storm {
const std::string RegionSettings::moduleName = "region";
const std::string RegionSettings::regionOptionName = "region";
const std::string RegionSettings::regionShortOptionName = "reg";
const std::string RegionSettings::regionBoundOptionName = "regionbound";
const std::string RegionSettings::hypothesisOptionName = "hypothesis";
const std::string RegionSettings::hypothesisShortOptionName = "hyp";
const std::string RegionSettings::refineOptionName = "refine";
const std::string RegionSettings::extremumOptionName = "extremum";
const std::string RegionSettings::extremumSuggestionOptionName = "extremum-init";
const std::string RegionSettings::splittingThresholdName = "splitting-threshold";
const std::string RegionSettings::checkEngineOptionName = "engine";
const std::string RegionSettings::printNoIllustrationOptionName = "noillustration";
const std::string RegionSettings::printFullResultOptionName = "printfullresult";
@ -28,7 +32,10 @@ namespace storm {
RegionSettings::RegionSettings() : ModuleSettings(moduleName) {
this->addOption(storm::settings::OptionBuilder(moduleName, regionOptionName, false, "Sets the region(s) considered for analysis.").setShortName(regionShortOptionName)
.addArgument(storm::settings::ArgumentBuilder::createStringArgument("regioninput", "The region(s) given in format a<=x<=b,c<=y<=d seperated by ';'. Can also be a file.").build()).build());
this->addOption(storm::settings::OptionBuilder(moduleName, regionBoundOptionName, false, "Sets the region bound considered for analysis.")
.addArgument(storm::settings::ArgumentBuilder::createStringArgument("regionbound", "The bound for the region result for all variables: 0+bound <= var <=1-bound").build()).build());
std::vector<std::string> hypotheses = {"unknown", "allsat", "allviolated"};
this->addOption(storm::settings::OptionBuilder(moduleName, hypothesisOptionName, false, "Sets a hypothesis for region analysis. If given, the region(s) are only analyzed w.r.t. that hypothesis.").setShortName(hypothesisShortOptionName)
.addArgument(storm::settings::ArgumentBuilder::createStringArgument("hypothesis", "The hypothesis.").addValidatorString(ArgumentValidatorFactory::createMultipleChoiceValidator(hypotheses)).setDefaultValueString("unknown").build()).build());
@ -41,7 +48,13 @@ namespace storm {
this->addOption(storm::settings::OptionBuilder(moduleName, extremumOptionName, false, "Computes the extremum within the region.")
.addArgument(storm::settings::ArgumentBuilder::createStringArgument("direction", "The optimization direction").addValidatorString(storm::settings::ArgumentValidatorFactory::createMultipleChoiceValidator(directions)).build())
.addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("precision", "The desired precision").setDefaultValueDouble(0.05).makeOptional().addValidatorDouble(storm::settings::ArgumentValidatorFactory::createDoubleRangeValidatorIncluding(0.0,1.0)).build()).build());
this->addOption(storm::settings::OptionBuilder(moduleName, extremumSuggestionOptionName, false, "Checks whether the provided value is indeed the extremum")
.addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("extremum-suggestion", "The provided value for the extremum").addValidatorDouble(storm::settings::ArgumentValidatorFactory::createDoubleRangeValidatorIncluding(0.0,1.0)).build()).build());
this->addOption(storm::settings::OptionBuilder(moduleName, splittingThresholdName, false, "Sets the threshold for number of parameters in which to split regions.")
.addArgument(storm::settings::ArgumentBuilder::createIntegerArgument("splitting-threshold", "The threshold for splitting, should be an integer > 0").build()).build());
std::vector<std::string> engines = {"pl", "exactpl", "validatingpl"};
this->addOption(storm::settings::OptionBuilder(moduleName, checkEngineOptionName, true, "Sets which engine is used for analyzing regions.")
.addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of the engine to use.").addValidatorString(ArgumentValidatorFactory::createMultipleChoiceValidator(engines)).setDefaultValueString("pl").build()).build());
@ -58,6 +71,14 @@ namespace storm {
std::string RegionSettings::getRegionString() const {
return this->getOption(regionOptionName).getArgumentByName("regioninput").getValueAsString();
}
bool RegionSettings::isRegionBoundSet() const {
return this->getOption(regionBoundOptionName).getHasOptionBeenSet();
}
std::string RegionSettings::getRegionBoundString() const {
return this->getOption(regionBoundOptionName).getArgumentByName("regionbound").getValueAsString();
}
bool RegionSettings::isHypothesisSet() const {
return this->getOption(hypothesisOptionName).getHasOptionBeenSet();
@ -114,9 +135,23 @@ namespace storm {
}
double RegionSettings::getExtremumValuePrecision() const {
auto generalSettings = storm::settings::getModule<storm::settings::modules::GeneralSettings>();
if (!generalSettings.isPrecisionSet() && generalSettings.isSoundSet()) {
double prec = this->getOption(extremumOptionName).getArgumentByName("precision").getValueAsDouble() / 10;
generalSettings.setPrecision(std::to_string(prec));
STORM_LOG_WARN("Reset precision for solver to " << prec << " this is sufficient for extremum value precision of " << (prec)*10 << std::endl);
}
return this->getOption(extremumOptionName).getArgumentByName("precision").getValueAsDouble();
}
bool RegionSettings::isExtremumSuggestionSet() const {
return this->getOption(extremumSuggestionOptionName).getHasOptionBeenSet();
}
double RegionSettings::getExtremumSuggestion() const {
return this->getOption(extremumSuggestionOptionName).getArgumentByName("extremum-suggestion").getValueAsDouble();
}
storm::modelchecker::RegionCheckEngine RegionSettings::getRegionCheckEngine() const {
std::string engineString = this->getOption(checkEngineOptionName).getArgumentByName("name").getValueAsString();
@ -139,6 +174,10 @@ namespace storm {
STORM_LOG_ERROR("Can not compute extremum values AND perform region refinement.");
return false;
}
if (getExtremumValuePrecision() < storm::settings::getModule<storm::settings::modules::GeneralSettings>().getPrecision()) {
STORM_LOG_ERROR("Computing extremum value for precision " << getExtremumValuePrecision() << " makes no sense when solver precision is set to " << storm::settings::getModule<storm::settings::modules::GeneralSettings>().getPrecision());
return false;
}
return true;
}
@ -149,7 +188,15 @@ namespace storm {
bool RegionSettings::isPrintFullResultSet() const {
return this->getOption(printFullResultOptionName).getHasOptionBeenSet();
}
int RegionSettings::getSplittingThreshold() const {
return this->getOption(splittingThresholdName).getArgumentByName("splitting-threshold").getValueAsInteger();
}
bool RegionSettings::isSplittingThresholdSet() const {
return this->getOption(splittingThresholdName).getHasOptionBeenSet();
}
} // namespace modules
} // namespace settings

22
src/storm-pars/settings/modules/RegionSettings.h

@ -30,7 +30,16 @@ namespace storm {
* Retrieves the region definition string
*/
std::string getRegionString() const;
/*!
* Retrieves whether region bound is declared
*/
bool isRegionBoundSet() const;
/*!
* Retrieves the region definition string
*/
std::string getRegionBoundString() const;
/*!
* Retrieves whether region(s) were declared
*/
@ -77,6 +86,14 @@ namespace storm {
*/
double getExtremumValuePrecision() const;
bool isExtremumSuggestionSet() const;
double getExtremumSuggestion() const;
bool isSplittingThresholdSet() const;
int getSplittingThreshold() const;
/*!
* Retrieves which type of region check should be performed
*/
@ -99,10 +116,13 @@ namespace storm {
private:
const static std::string regionOptionName;
const static std::string regionShortOptionName;
const static std::string regionBoundOptionName;
const static std::string hypothesisOptionName;
const static std::string hypothesisShortOptionName;
const static std::string refineOptionName;
const static std::string splittingThresholdName;
const static std::string extremumOptionName;
const static std::string extremumSuggestionOptionName;
const static std::string checkEngineOptionName;
const static std::string printNoIllustrationOptionName;
const static std::string printFullResultOptionName;

145
src/storm-pars/storage/ParameterRegion.cpp

@ -33,10 +33,12 @@ namespace storm {
STORM_LOG_THROW((variableWithUpperBoundary != upperBoundaries.end()), storm::exceptions::InvalidArgumentException, "Could not create region. No upper boundary specified for Variable " << variableWithLowerBoundary.first);
STORM_LOG_THROW((variableWithLowerBoundary.second<=variableWithUpperBoundary->second), storm::exceptions::InvalidArgumentException, "Could not create region. The lower boundary for " << variableWithLowerBoundary.first << " is larger then the upper boundary");
this->variables.insert(variableWithLowerBoundary.first);
this->sortedOnDifference.insert({variableWithLowerBoundary.second - variableWithUpperBoundary->second, variableWithLowerBoundary.first});
}
for (auto const& variableWithBoundary : this->upperBoundaries) {
STORM_LOG_THROW((this->variables.find(variableWithBoundary.first) != this->variables.end()), storm::exceptions::InvalidArgumentException, "Could not create region. No lower boundary specified for Variable " << variableWithBoundary.first);
}
this->splitThreshold = variables.size();
}
template<typename ParametricType>
@ -45,7 +47,12 @@ namespace storm {
}
template<typename ParametricType>
typename ParameterRegion<ParametricType>::CoefficientType const& ParameterRegion<ParametricType>::getLowerBoundary(VariableType const& variable) const {
std::multimap<typename ParameterRegion<ParametricType>::CoefficientType , typename ParameterRegion<ParametricType>::VariableType> const& ParameterRegion<ParametricType>::getVariablesSorted() const {
return this->sortedOnDifference;
}
template<typename ParametricType>
typename ParameterRegion<ParametricType>::CoefficientType const& ParameterRegion<ParametricType>::getLowerBoundary(VariableType const& variable) const {
auto const& result = lowerBoundaries.find(variable);
STORM_LOG_THROW(result != lowerBoundaries.end(), storm::exceptions::InvalidArgumentException, "Tried to find a lower boundary for variable " << variable << " which is not specified by this region");
return (*result).second;
@ -78,6 +85,16 @@ namespace storm {
STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Tried to find an upper boundary for variableName " << varName << " which is not specified by this region");
}
template<typename ParametricType>
typename ParameterRegion<ParametricType>::CoefficientType ParameterRegion<ParametricType>::getDifference(VariableType const& variable) const {
return getUpperBoundary(variable) - getLowerBoundary(variable);
}
template<typename ParametricType>
typename ParameterRegion<ParametricType>::CoefficientType ParameterRegion<ParametricType>::getDifference(const std::string varName) const {
return getUpperBoundary(varName) - getLowerBoundary(varName);
}
template<typename ParametricType>
typename ParameterRegion<ParametricType>::Valuation const& ParameterRegion<ParametricType>::getUpperBoundaries() const {
return upperBoundaries;
@ -100,7 +117,8 @@ namespace storm {
//the consideredVariables.size() least significant bits of vertex will always represent the next vertex
//(00...0 = lower boundaries for all variables, 11...1 = upper boundaries for all variables)
uint_fast64_t variableIndex = 0;
for (auto const& variable : consideredVariables) {
for (auto variable : consideredVariables) {
if ((vertexId >> variableIndex) % 2 == 0) {
resultingVector[vertexId].insert(std::pair<VariableType, CoefficientType>(variable, getLowerBoundary(variable)));
} else {
@ -112,6 +130,7 @@ namespace storm {
return resultingVector;
}
template<typename ParametricType>
typename ParameterRegion<ParametricType>::Valuation ParameterRegion<ParametricType>::getSomePoint() const {
return this->getLowerBoundaries();
@ -136,30 +155,38 @@ namespace storm {
}
template<typename ParametricType>
void ParameterRegion<ParametricType>::split(Valuation const& splittingPoint, std::vector<ParameterRegion<ParametricType> >& regionVector) const{
//Check if splittingPoint is valid.
STORM_LOG_THROW(splittingPoint.size() == this->variables.size(), storm::exceptions::InvalidArgumentException, "Tried to split a region w.r.t. a point, but the point considers a different number of variables.");
for(auto const& variable : this->variables){
auto splittingPointEntry=splittingPoint.find(variable);
STORM_LOG_THROW(splittingPointEntry != splittingPoint.end(), storm::exceptions::InvalidArgumentException, "Tried to split a region but a variable of this region is not defined by the splitting point.");
STORM_LOG_THROW(this->getLowerBoundary(variable) <=splittingPointEntry->second, storm::exceptions::InvalidArgumentException, "Tried to split a region but the splitting point is not contained in the region.");
STORM_LOG_THROW(this->getUpperBoundary(variable) >=splittingPointEntry->second, storm::exceptions::InvalidArgumentException, "Tried to split a region but the splitting point is not contained in the region.");
}
//Now compute the subregions.
std::vector<Valuation> vertices(this->getVerticesOfRegion(this->variables));
for(auto const& vertex : vertices){
void ParameterRegion<ParametricType>::split(Valuation const& splittingPoint, std::vector<ParameterRegion<ParametricType>>& regionVector) const{
return split(splittingPoint, regionVector, variables);
}
template<typename ParametricType>
void ParameterRegion<ParametricType>::split(Valuation const& splittingPoint, std::vector<storm::storage::ParameterRegion<ParametricType>> &regionVector,
const std::set<VariableType> &consideredVariables) const {
auto vertices = getVerticesOfRegion(consideredVariables);
for (auto const &vertex : vertices) {
//The resulting subregion is the smallest region containing vertex and splittingPoint.
Valuation subLower, subUpper;
for(auto variableBound : this->lowerBoundaries){
for (auto variableBound : this->lowerBoundaries) {
VariableType variable = variableBound.first;
auto vertexEntry=vertex.find(variable);
auto splittingPointEntry=splittingPoint.find(variable);
subLower.insert(typename Valuation::value_type(variable, std::min(vertexEntry->second, splittingPointEntry->second)));
subUpper.insert(typename Valuation::value_type(variable, std::max(vertexEntry->second, splittingPointEntry->second)));
auto vertexEntry = vertex.find(variable);
if (vertexEntry != vertex.end()) {
auto splittingPointEntry = splittingPoint.find(variable);
subLower.insert(typename Valuation::value_type(variable, std::min(vertexEntry->second,
splittingPointEntry->second)));
subUpper.insert(typename Valuation::value_type(variable, std::max(vertexEntry->second,
splittingPointEntry->second)));
} else {
subLower.insert(typename Valuation::value_type(variable, getLowerBoundary(variable)));
subUpper.insert(typename Valuation::value_type(variable, getUpperBoundary(variable)));
}
}
ParameterRegion<ParametricType> subRegion(std::move(subLower), std::move(subUpper));
if(!storm::utility::isZero(subRegion.area())){
subRegion.setSplitThreshold(this->getSplitThreshold());
if (!storm::utility::isZero(subRegion.area())) {
regionVector.push_back(std::move(subRegion));
}
}
@ -192,18 +219,84 @@ namespace storm {
regionstring = regionstring.substr(0, regionstring.length() - 1) + ";";
return regionstring;
}
template <typename ParametricType>
bool ParameterRegion<ParametricType>::isSubRegion(ParameterRegion<ParametricType> subRegion) {
auto varsRegion = getVariables();
auto varsSubRegion = subRegion.getVariables();
for (auto var : varsRegion) {
if (std::find(varsSubRegion.begin(), varsSubRegion.end(), var) != varsSubRegion.end()) {
if (getLowerBoundary(var) > subRegion.getLowerBoundary(var) || getUpperBoundary(var) < getUpperBoundary(var)) {
return false;
}
} else {
return false;
}
}
return true;
}
template<typename ParametricType>
typename ParameterRegion<ParametricType>::Valuation ParameterRegion<ParametricType>::getPoint(storm::solver::OptimizationDirection dir, storm::analysis::MonotonicityResult<VariableType> &monRes) const {
auto val = this->getCenterPoint();
for (auto monResEntry : monRes.getMonotonicityResult()) {
if (monRes.isDoneForVar(monResEntry.first)) {
if (monResEntry.second == storm::analysis::MonotonicityResult<VariableType>::Monotonicity::Incr) {
val[monResEntry.first] = storm::solver::minimize(dir) ? getLowerBoundary(monResEntry.first) : getUpperBoundary(monResEntry.first);
} else if (monResEntry.second == storm::analysis::MonotonicityResult<VariableType>::Monotonicity::Decr) {
val[monResEntry.first] = storm::solver::maximize(dir) ? getLowerBoundary(monResEntry.first) : getUpperBoundary(monResEntry.first);
}
}
}
return val;
}
template<typename ParametricType>
typename ParameterRegion<ParametricType>::Valuation ParameterRegion<ParametricType>::getPoint(storm::solver::OptimizationDirection dir,
std::set<VariableType> const &monIncrParameters,
std::set<VariableType> const &monDecrParameters) const{
auto val = this->getCenterPoint();
for (auto var : monIncrParameters) {
val[var] = storm::solver::minimize(dir) ? getLowerBoundary(var) : getUpperBoundary(var);
}
for (auto var : monDecrParameters) {
val[var] = storm::solver::maximize(dir) ? getLowerBoundary(var) : getUpperBoundary(var);
}
return val;
}
template<typename ParametricType>
typename ParameterRegion<ParametricType>::CoefficientType ParameterRegion<ParametricType>::getBoundParent() {
return parentBound;
}
template<typename ParametricType>
void ParameterRegion<ParametricType>::setBoundParent(CoefficientType bound) {
parentBound = bound;
}
template <typename ParametricType>
std::ostream& operator<<(std::ostream& out, ParameterRegion<ParametricType> const& region) {
out << region.toString();
return out;
}
template <typename ParametricType>
void ParameterRegion<ParametricType>::setSplitThreshold(int splitThreshold) {
this->splitThreshold = splitThreshold;
}
template <typename ParametricType>
int ParameterRegion<ParametricType>::getSplitThreshold() const {
return splitThreshold;
}
#ifdef STORM_HAVE_CARL
template class ParameterRegion<storm::RationalFunction>;
template std::ostream& operator<<(std::ostream& out, ParameterRegion<storm::RationalFunction> const& region);
template class ParameterRegion<storm::RationalFunction>;
template std::ostream& operator<<(std::ostream& out, ParameterRegion<storm::RationalFunction> const& region);
#endif
}
}

25
src/storm-pars/storage/ParameterRegion.h

@ -3,6 +3,9 @@
#include <map>
#include "storm-pars/utility/parametric.h"
#include "storm-pars/analysis/MonotonicityResult.h"
#include "storm-pars/modelchecker/region/RegionResult.h"
#include "storm/solver/OptimizationDirection.h"
namespace storm {
namespace storage {
@ -23,10 +26,13 @@ namespace storm {
virtual ~ParameterRegion() = default;
std::set<VariableType> const& getVariables() const;
std::multimap<CoefficientType, VariableType> const& getVariablesSorted() const;
CoefficientType const& getLowerBoundary(VariableType const& variable) const;
CoefficientType const& getLowerBoundary(const std::string varName) const;
CoefficientType const& getUpperBoundary(VariableType const& variable) const;
CoefficientType const& getUpperBoundary(const std::string varName) const;
CoefficientType getDifference(const std::string varName) const;
CoefficientType getDifference(VariableType const& variable) const;
Valuation const& getLowerBoundaries() const;
Valuation const& getUpperBoundaries() const;
@ -50,7 +56,10 @@ namespace storm {
* Returns the center point of this region
*/
Valuation getCenterPoint() const;
void setSplitThreshold(int splitThreshold);
int getSplitThreshold() const;
/*!
* Returns the area of this region
*/
@ -62,17 +71,31 @@ namespace storm {
* Subregions with area()==0 are not inserted in the vector.
*/
void split(Valuation const& splittingPoint, std::vector<ParameterRegion<ParametricType>>& regionVector) const;
void split(Valuation const& splittingPoint, std::vector<ParameterRegion<ParametricType>>& regionVector, std::set<VariableType> const& consideredVariables) const;
Valuation getPoint(storm::solver::OptimizationDirection dir, storm::analysis::MonotonicityResult<VariableType> & monRes) const;
Valuation getPoint(storm::solver::OptimizationDirection dir, std::set<VariableType> const& possibleMonotoneIncrParameters, std::set<VariableType>const & monDecrParameters) const;
//returns the region as string in the format 0.3<=p<=0.4,0.2<=q<=0.5;
std::string toString(bool boundariesAsDouble = false) const;
bool isSubRegion(ParameterRegion<ParametricType> region);
CoefficientType getBoundParent();
void setBoundParent(CoefficientType bound);
private:
void init();
bool lastSplitMonotone = false;
int splitThreshold;
Valuation lowerBoundaries;
Valuation upperBoundaries;
std::set<VariableType> variables;
std::multimap<CoefficientType, VariableType> sortedOnDifference;
CoefficientType parentBound;
};
template<typename ParametricType>

106
src/storm-pars/transformer/ParameterLifter.cpp

@ -1,10 +1,7 @@
#include "storm-pars/transformer/ParameterLifter.h"
#include "storm/adapters/RationalFunctionAdapter.h"
#include "storm/utility/vector.h"
#include "storm/exceptions/UnexpectedException.h"
#include "storm/exceptions/NotSupportedException.h"
@ -12,41 +9,54 @@ namespace storm {
namespace transformer {
template<typename ParametricType, typename ConstantType>
ParameterLifter<ParametricType, ConstantType>::ParameterLifter(storm::storage::SparseMatrix<ParametricType> const& pMatrix, std::vector<ParametricType> const& pVector, storm::storage::BitVector const& selectedRows, storm::storage::BitVector const& selectedColumns, bool generateRowLabels) {
ParameterLifter<ParametricType, ConstantType>::ParameterLifter(storm::storage::SparseMatrix<ParametricType> const& pMatrix, std::vector<ParametricType> const& pVector, storm::storage::BitVector const& selectedRows, storm::storage::BitVector const& selectedColumns, bool generateRowLabels, bool useMonotonicityInFuture) {
// get a mapping from old column indices to new ones
std::vector<uint_fast64_t> oldToNewColumnIndexMapping(selectedColumns.size(), selectedColumns.size());
oldToNewColumnIndexMapping = std::vector<uint_fast64_t>(selectedColumns.size(), selectedColumns.size());
uint_fast64_t newIndex = 0;
for (auto const& oldColumn : selectedColumns) {
oldToNewColumnIndexMapping[oldColumn] = newIndex++;
}
// create vector, such that the occuringVariables for all states can be stored
occurringVariablesAtState = std::vector<std::set<VariableType>>(pMatrix.getColumnCount());
// Stores which entries of the original matrix/vector are non-constant. Entries for non-selected rows/columns are omitted
storm::storage::BitVector nonConstMatrixEntries(pMatrix.getEntryCount(), false); //this vector has to be resized later
storm::storage::BitVector nonConstVectorEntries(selectedRows.getNumberOfSetBits(), false);
auto nonConstMatrixEntries = storm::storage::BitVector(pMatrix.getEntryCount(), false); //this vector has to be resized later
auto nonConstVectorEntries = storm::storage::BitVector(selectedRows.getNumberOfSetBits(), false);
// Counters for selected entries in the pMatrix and the pVector
uint_fast64_t pMatrixEntryCount = 0;
uint_fast64_t pVectorEntryCount = 0;
// The matrix builder for the new matrix. The correct number of rows and entries is not known yet.
storm::storage::SparseMatrixBuilder<ConstantType> builder(0, selectedColumns.getNumberOfSetBits(), 0, true, true, selectedRows.getNumberOfSetBits());
rowGroupToStateNumber = std::vector<uint_fast64_t>();
uint_fast64_t newRowIndex = 0;
uint_fast64_t countNonParam = 0;
for (auto const& rowIndex : selectedRows) {
builder.newRowGroup(newRowIndex);
rowGroupToStateNumber.push_back(rowIndex);
// Gather the occurring variables within this row and set which entries are non-constant
std::set<VariableType> occurringVariables;
bool constant = true;
for (auto const& entry : pMatrix.getRow(rowIndex)) {
if (selectedColumns.get(entry.getColumn())) {
if (!storm::utility::isConstant(entry.getValue())) {
storm::utility::parametric::gatherOccurringVariables(entry.getValue(), occurringVariables);
nonConstMatrixEntries.set(pMatrixEntryCount, true);
constant = false;
}
++pMatrixEntryCount;
} else {
if (!storm::utility::isConstant(entry.getValue())) {
storm::utility::parametric::gatherOccurringVariables(entry.getValue(), occurringVariables);
}
}
}
if (constant) {
countNonParam++;
}
ParametricType const& pVectorEntry = pVector[rowIndex];
std::set<VariableType> vectorEntryVariables;
if (!storm::utility::isConstant(pVectorEntry)) {
@ -59,15 +69,14 @@ namespace storm {
nonConstVectorEntries.set(pVectorEntryCount, true);
}
++pVectorEntryCount;
// Compute the (abstract) valuation for each row
auto rowValuations = getVerticesOfAbstractRegion(occurringVariables);
for (auto const& val : rowValuations) {
if (generateRowLabels) {
rowLabels.push_back(val);
}
auto countPlaceHolders = 0;
// Insert matrix entries for each valuation. For non-constant entries, a dummy value is inserted and the function and the valuation are collected.
// The placeholder for the collected function/valuation are stored in the matrixAssignment. The matrixAssignment is completed after the matrix is finished
for (auto const& entry: pMatrix.getRow(rowIndex)) {
@ -78,6 +87,7 @@ namespace storm {
builder.addNextValue(newRowIndex, oldToNewColumnIndexMapping[entry.getColumn()], storm::utility::one<ConstantType>());
ConstantType& placeholder = functionValuationCollector.add(entry.getValue(), val);
matrixAssignment.push_back(std::pair<typename storm::storage::SparseMatrix<ConstantType>::iterator, ConstantType&>(typename storm::storage::SparseMatrix<ConstantType>::iterator(), placeholder));
countPlaceHolders++;
}
}
}
@ -100,15 +110,22 @@ namespace storm {
++newRowIndex;
}
if (useMonotonicityInFuture) {
// Save the occuringVariables of a state, needed if we want to use monotonicity
for (auto& var : occurringVariables) {
occuringStatesAtVariable[var].insert(rowIndex);
}
occurringVariablesAtState[rowIndex] = std::move(occurringVariables);
}
}
// Matrix and vector are now filled with constant results from constant functions and place holders for non-constant functions.
matrix = builder.build(newRowIndex);
vector.shrink_to_fit();
matrixAssignment.shrink_to_fit();
vectorAssignment.shrink_to_fit();
nonConstMatrixEntries.resize(pMatrixEntryCount);
// Now insert the correct iterators for the matrix and vector assignment
auto matrixAssignmentIt = matrixAssignment.begin();
uint_fast64_t startEntryOfRow = 0;
@ -139,21 +156,41 @@ namespace storm {
void ParameterLifter<ParametricType, ConstantType>::specifyRegion(storm::storage::ParameterRegion<ParametricType> const& region, storm::solver::OptimizationDirection const& dirForParameters) {
// write the evaluation result of each function,evaluation pair into the placeholders
functionValuationCollector.evaluateCollectedFunctions(region, dirForParameters);
//apply the matrix and vector assignments to write the contents of the placeholder into the matrix/vector
for(auto& assignment : matrixAssignment) {
for (auto &assignment : matrixAssignment) {
STORM_LOG_WARN_COND(!storm::utility::isZero(assignment.second), "Parameter lifting on region " << region.toString() << " affects the underlying graph structure (the region is not strictly well defined). The result for this region might be incorrect.");
assignment.first->setValue(assignment.second);
}
for(auto& assignment : vectorAssignment) {
for (auto &assignment : vectorAssignment) {
*assignment.first = assignment.second;
}
}
template<typename ParametricType, typename ConstantType>
uint_fast64_t ParameterLifter<ParametricType, ConstantType>::getRowGroupIndex(uint_fast64_t originalState) const {
return matrix.getRowGroupIndices()[oldToNewColumnIndexMapping[originalState]];
}
template<typename ParametricType, typename ConstantType>
uint_fast64_t ParameterLifter<ParametricType, ConstantType>::getOriginalStateNumber(uint_fast64_t newState) const {
return rowGroupToStateNumber[newState];
}
template<typename ParametricType, typename ConstantType>
uint_fast64_t ParameterLifter<ParametricType, ConstantType>::getRowGroupSize(uint_fast64_t originalState) const {
return matrix.getRowGroupSize(oldToNewColumnIndexMapping[originalState]);
}
template<typename ParametricType, typename ConstantType>
uint_fast64_t ParameterLifter<ParametricType, ConstantType>::getRowGroupCount() const {
return matrix.getRowGroupCount();
}
template<typename ParametricType, typename ConstantType>
storm::storage::SparseMatrix<ConstantType> const& ParameterLifter<ParametricType, ConstantType>::getMatrix() const {
return matrix;
}
template<typename ParametricType, typename ConstantType>
@ -188,6 +225,16 @@ namespace storm {
return result;
}
template<typename ParametricType, typename ConstantType>
const std::vector<std::set<typename ParameterLifter<ParametricType, ConstantType>::VariableType>> & ParameterLifter<ParametricType, ConstantType>::getOccurringVariablesAtState() const {
return occurringVariablesAtState;
}
template<typename ParametricType, typename ConstantType>
std::map<typename ParameterLifter<ParametricType, ConstantType>::VariableType, std::set<uint_fast64_t>> ParameterLifter<ParametricType, ConstantType>::getOccuringStatesAtVariable() const {
return occuringStatesAtVariable;
}
template<typename ParametricType, typename ConstantType>
bool ParameterLifter<ParametricType, ConstantType>::AbstractValuation::operator==(AbstractValuation const& other) const {
return this->lowerPars == other.lowerPars && this->upperPars == other.upperPars && this->unspecifiedPars == other.unspecifiedPars;
@ -268,7 +315,13 @@ namespace storm {
}
return result;
}
template<typename ParametricType, typename ConstantType>
uint_fast64_t ParameterLifter<ParametricType, ConstantType>::AbstractValuation::getOriginalState(
uint_fast64_t newStateNumber) const {
return 0;
}
template<typename ParametricType, typename ConstantType>
ConstantType& ParameterLifter<ParametricType, ConstantType>::FunctionValuationCollector::add(ParametricType const& function, AbstractValuation const& valuation) {
ParametricType simplifiedFunction = function;
@ -284,17 +337,16 @@ namespace storm {
template<typename ParametricType, typename ConstantType>
void ParameterLifter<ParametricType, ConstantType>::FunctionValuationCollector::evaluateCollectedFunctions(storm::storage::ParameterRegion<ParametricType> const& region, storm::solver::OptimizationDirection const& dirForUnspecifiedParameters) {
for (auto& collectedFunctionValuationPlaceholder : collectedFunctions) {
ParametricType const& function = collectedFunctionValuationPlaceholder.first.first;
AbstractValuation const& abstrValuation = collectedFunctionValuationPlaceholder.first.second;
ConstantType& placeholder = collectedFunctionValuationPlaceholder.second;
for (auto &collectedFunctionValuationPlaceholder : collectedFunctions) {
ParametricType const &function = collectedFunctionValuationPlaceholder.first.first;
AbstractValuation const &abstrValuation = collectedFunctionValuationPlaceholder.first.second;
ConstantType &placeholder = collectedFunctionValuationPlaceholder.second;
auto concreteValuations = abstrValuation.getConcreteValuations(region);
auto concreteValuationIt = concreteValuations.begin();
placeholder = storm::utility::convertNumber<ConstantType>(storm::utility::parametric::evaluate(function, *concreteValuationIt));
for(++concreteValuationIt; concreteValuationIt != concreteValuations.end(); ++concreteValuationIt) {
for (++concreteValuationIt; concreteValuationIt != concreteValuations.end(); ++concreteValuationIt) {
ConstantType currentResult = storm::utility::convertNumber<ConstantType>(storm::utility::parametric::evaluate(function, *concreteValuationIt));
if(storm::solver::minimize(dirForUnspecifiedParameters)) {
if (storm::solver::minimize(dirForUnspecifiedParameters)) {
placeholder = std::min(placeholder, currentResult);
} else {
placeholder = std::max(placeholder, currentResult);

58
src/storm-pars/transformer/ParameterLifter.h

@ -11,6 +11,9 @@
#include "storm/storage/BitVector.h"
#include "storm/storage/SparseMatrix.h"
#include "storm/solver/OptimizationDirection.h"
#include "storm-pars/analysis/Order.h"
#include "storm-pars/analysis/MonotonicityChecker.h"
namespace storm {
namespace transformer {
@ -31,7 +34,8 @@ namespace storm {
typedef typename storm::utility::parametric::VariableType<ParametricType>::type VariableType;
typedef typename storm::utility::parametric::CoefficientType<ParametricType>::type CoefficientType;
typedef typename storm::analysis::MonotonicityResult<VariableType>::Monotonicity Monotonicity;
/*!
* Lifts the parameter choices to nondeterminisim. The computation is performed on the submatrix specified by the selected rows and columns
* @param pMatrix the parametric matrix
@ -39,16 +43,41 @@ namespace storm {
* @param selectedRows a Bitvector that specifies which rows of the matrix and the vector are considered.
* @param selectedColumns a Bitvector that specifies which columns of the matrix are considered.
*/
ParameterLifter(storm::storage::SparseMatrix<ParametricType> const& pMatrix, std::vector<ParametricType> const& pVector, storm::storage::BitVector const& selectedRows, storm::storage::BitVector const& selectedColumns, bool generateRowLabels = false);
ParameterLifter(storm::storage::SparseMatrix<ParametricType> const& pMatrix, std::vector<ParametricType> const& pVector, storm::storage::BitVector const& selectedRows, storm::storage::BitVector const& selectedColumns, bool generateRowLabels = false, bool useMonotonicity = false);
void specifyRegion(storm::storage::ParameterRegion<ParametricType> const& region, storm::solver::OptimizationDirection const& dirForParameters);
/*!
* Specifies the region for the parameterlifter, the Bitvector works as a fixed (partial) scheduler, this might not give sound results!
* @param region the region
* @param dirForParameters the optimization direction
* @param selectedRows a Bitvector that specifies which rows of the matrix and the vector are considered.
*/
void specifyRegion(storm::storage::ParameterRegion<ParametricType> const& region, storm::solver::OptimizationDirection const& dirForParameters, storm::storage::BitVector const& selectedRows);
/*!
* Specifies the region for the parameterlifter, the reachability order is used to see if there is local monotonicity, such that a fixed (partial) scheduler can be used
* @param region the region
* @param dirForParameters the optimization direction
* @param reachabilityOrder a (possibly insufficient) reachability order, used for local monotonicity
*/
void specifyRegion(storm::storage::ParameterRegion<ParametricType> const& region, storm::solver::OptimizationDirection const& dirForParameters, std::shared_ptr<storm::analysis::Order> reachabilityOrder, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonotonicityResult);
// Returns the resulting matrix. Should only be called AFTER specifying a region
storm::storage::SparseMatrix<ConstantType> const& getMatrix() const;
// Returns the resulting vector. Should only be called AFTER specifying a region
std::vector<ConstantType> const& getVector() const;
std::vector<std::set<VariableType>> const& getOccurringVariablesAtState() const;
std::map<VariableType, std::set<uint_fast64_t>> getOccuringStatesAtVariable() const;
uint_fast64_t getRowGroupIndex(uint_fast64_t originalState) const;
uint_fast64_t getOriginalStateNumber(uint_fast64_t newState) const;
uint_fast64_t getRowGroupSize(uint_fast64_t originalState) const;
uint_fast64_t getRowGroupCount() const;
/*
* During initialization, the actual regions are not known. Hence, we consider abstract valuations,
* where it is only known whether a parameter will be set to either the lower/upper bound of the region or whether this is unspecified
@ -68,7 +97,8 @@ namespace storm {
std::set<VariableType> const& getLowerParameters() const;
std::set<VariableType> const& getUpperParameters() const;
std::set<VariableType> const& getUnspecifiedParameters() const;
uint_fast64_t getOriginalState(uint_fast64_t newStateNumber) const;
/*!
* Returns the concrete valuation(s) (w.r.t. the provided region) represented by this abstract valuation.
* Note that an abstract valuation represents 2^(#unspecified parameters) many concrete valuations.
@ -82,7 +112,6 @@ namespace storm {
// Returns for each row the abstract valuation for this row
// Note: the returned vector might be empty if row label generaion was disabled initially
std::vector<AbstractValuation> const& getRowLabels() const;
private:
/*
@ -97,18 +126,19 @@ namespace storm {
class FunctionValuationCollector {
public:
FunctionValuationCollector() = default;
/*!
* Adds the provided function and valuation.
* Returns a reference to a placeholder in which the evaluation result will be written upon calling evaluateCollectedFunctions)
*/
ConstantType& add(ParametricType const& function, AbstractValuation const& valuation);
void evaluateCollectedFunctions(storm::storage::ParameterRegion<ParametricType> const& region, storm::solver::OptimizationDirection const& dirForUnspecifiedParameters);
private:
// Stores a function and a valuation. The valuation is stored as an index of the collectedValuations-vector.
typedef std::pair<ParametricType, AbstractValuation> FunctionValuation;
class FuncValHash{
public:
std::size_t operator()(FunctionValuation const& fv) const {
@ -118,7 +148,7 @@ namespace storm {
return seed;
}
};
// Stores the collected functions with the valuations together with a placeholder for the result.
std::unordered_map<FunctionValuation, ConstantType, FuncValHash> collectedFunctions;
};
@ -130,12 +160,18 @@ namespace storm {
std::vector<AbstractValuation> rowLabels;
std::vector<uint_fast64_t> oldToNewColumnIndexMapping; // Mapping from old to new columnIndex used for monotonicity
std::vector<uint_fast64_t> rowGroupToStateNumber; // Mapping from new to old columnIndex used for monotonicity
storm::storage::SparseMatrix<ConstantType> matrix; //The resulting matrix;
std::vector<std::pair<typename storm::storage::SparseMatrix<ConstantType>::iterator, ConstantType&>> matrixAssignment; // Connection of matrix entries with placeholders
std::vector<ConstantType> vector; //The resulting vector
std::vector<std::pair<typename std::vector<ConstantType>::iterator, ConstantType&>> vectorAssignment; // Connection of vector entries with placeholders
// Used for monotonicity in sparsedtmcparameterlifter
std::vector<std::set<VariableType>> occurringVariablesAtState;
std::map<VariableType, std::set<uint_fast64_t>> occuringStatesAtVariable;
};
}

9
src/storm/settings/modules/GeneralSettings.cpp

@ -73,7 +73,14 @@ namespace storm {
uint64_t GeneralSettings::getShowProgressDelay() const {
return this->getOption(showProgressOptionName).getArgumentByName("delay").getValueAsUnsignedInteger();
}
bool GeneralSettings::isPrecisionSet() const {
return this->getOption(precisionOptionName).getHasOptionBeenSet();
}
void GeneralSettings::setPrecision(std::string precision) {
this->getOption(precisionOptionName).getArgumentByName("value").setFromStringValue(precision);
}
double GeneralSettings::getPrecision() const {
return this->getOption(precisionOptionName).getArgumentByName("value").getValueAsDouble();
}

6
src/storm/settings/modules/GeneralSettings.h

@ -98,7 +98,11 @@ namespace storm {
* @return True iff the option was set.
*/
bool isParametricSet() const;
bool isPrecisionSet() const;
void setPrecision(std::string precision);
/*!
* Retrieves whether the option enabling exact model checking is set and we should use infinite precision rationals.
*

14
src/storm/settings/modules/IOSettings.cpp

@ -26,7 +26,6 @@ namespace storm {
const std::string IOSettings::exportCdfOptionShortName = "cdf";
const std::string IOSettings::exportSchedulerOptionName = "exportscheduler";
const std::string IOSettings::exportCheckResultOptionName = "exportresult";
const std::string IOSettings::exportMonotonicityName = "exportmonotonicity";
const std::string IOSettings::explicitOptionName = "explicit";
const std::string IOSettings::explicitOptionShortName = "exp";
const std::string IOSettings::explicitDrnOptionName = "explicit-drn";
@ -66,7 +65,6 @@ namespace storm {
this->addOption(storm::settings::OptionBuilder(moduleName, exportCdfOptionName, false, "Exports the cumulative density function for reward bounded properties into a .csv file.").setIsAdvanced().setShortName(exportCdfOptionShortName).addArgument(storm::settings::ArgumentBuilder::createStringArgument("directory", "A path to an existing directory where the cdf files will be stored.").build()).build());
this->addOption(storm::settings::OptionBuilder(moduleName, exportSchedulerOptionName, false, "Exports the choices of an optimal scheduler to the given file (if supported by engine).").setIsAdvanced().addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "The output file. Use file extension '.json' to export in json.").build()).build());
this->addOption(storm::settings::OptionBuilder(moduleName, exportCheckResultOptionName, false, "Exports the result to a given file (if supported by engine). The export will be in json.").setIsAdvanced().addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "The output file.").build()).build());
this->addOption(storm::settings::OptionBuilder(moduleName, exportMonotonicityName, false, "Exports the result of monotonicity checking to the given file.").setIsAdvanced().addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "The output file.").build()).build());
this->addOption(storm::settings::OptionBuilder(moduleName, exportExplicitOptionName, "", "If given, the loaded model will be written to the specified file in the drn format.")
.addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "the name of the file to which the model is to be writen.").build()).build());
this->addOption(storm::settings::OptionBuilder(moduleName, preventDRNPlaceholderOptionName, true, "If given, the exported DRN contains no placeholders").setIsAdvanced().build());
@ -179,23 +177,15 @@ namespace storm {
std::string IOSettings::getExportSchedulerFilename() const {
return this->getOption(exportSchedulerOptionName).getArgumentByName("filename").getValueAsString();
}
bool IOSettings::isExportCheckResultSet() const {
return this->getOption(exportCheckResultOptionName).getHasOptionBeenSet();
}
std::string IOSettings::getExportCheckResultFilename() const {
return this->getOption(exportCheckResultOptionName).getArgumentByName("filename").getValueAsString();
}
bool IOSettings::isExportMonotonicitySet() const {
return this->getOption(exportMonotonicityName).getHasOptionBeenSet();
}
std::string IOSettings::getExportMonotonicityFilename() const {
return this->getOption(exportMonotonicityName).getArgumentByName("filename").getValueAsString();
}
bool IOSettings::isExplicitSet() const {
return this->getOption(explicitOptionName).getHasOptionBeenSet();
}

15
src/storm/settings/modules/IOSettings.h

@ -105,27 +105,17 @@ namespace storm {
* Retrieves a filename to which an optimal scheduler will be exported.
*/
std::string getExportSchedulerFilename() const;
/*!
* Retrieves whether the check result should be exported.
*/
bool isExportCheckResultSet() const;
/*!
* Retrieves a filename to which the check result should be exported.
*/
std::string getExportCheckResultFilename() const;
/*!
* Retrieves whether an optimal scheduler is to be exported
*/
bool isExportMonotonicitySet() const;
/*!
* Retrieves a filename to which an optimal scheduler will be exported.
*/
std::string getExportMonotonicityFilename() const;
/*!
* Retrieves whether the explicit option was set.
*
@ -376,7 +366,6 @@ namespace storm {
static const std::string exportCdfOptionShortName;
static const std::string exportSchedulerOptionName;
static const std::string exportCheckResultOptionName;
static const std::string exportMonotonicityName;
static const std::string explicitOptionName;
static const std::string explicitOptionShortName;
static const std::string explicitDrnOptionName;

61
src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp

@ -97,12 +97,19 @@ namespace storm {
// Resolve the nondeterminism according to the given scheduler.
bool convertToEquationSystem = this->linearEquationSolverFactory->getEquationProblemFormat(env) == LinearEquationSolverProblemFormat::EquationSystem;
storm::storage::SparseMatrix<ValueType> submatrix = this->A->selectRowsFromRowGroups(scheduler, convertToEquationSystem);
storm::storage::SparseMatrix<ValueType> submatrix;
if (this->fixedStates) {
for (auto state : this->fixedStates.get()) {
assert (this->A->getRowGroupSize(state) == 1);
}
}
submatrix = this->A->selectRowsFromRowGroups(scheduler, convertToEquationSystem);
if (convertToEquationSystem) {
submatrix.convertToEquationSystem();
}
storm::utility::vector::selectVectorValues<ValueType>(subB, scheduler, this->A->getRowGroupIndices(), originalB);
// Check whether the linear equation solver is already initialized
if (!linearEquationSolver) {
// Initialize the equation solver
@ -165,29 +172,36 @@ namespace storm {
// Go through the multiplication result and see whether we can improve any of the choices.
bool schedulerImproved = false;
// Group staat voor de states?
for (uint_fast64_t group = 0; group < this->A->getRowGroupCount(); ++group) {
uint_fast64_t currentChoice = scheduler[group];
for (uint_fast64_t choice = this->A->getRowGroupIndices()[group]; choice < this->A->getRowGroupIndices()[group + 1]; ++choice) {
// If the choice is the currently selected one, we can skip it.
if (choice - this->A->getRowGroupIndices()[group] == currentChoice) {
continue;
}
// Create the value of the choice.
ValueType choiceValue = storm::utility::zero<ValueType>();
for (auto const& entry : this->A->getRow(choice)) {
choiceValue += entry.getValue() * x[entry.getColumn()];
}
choiceValue += b[choice];
// If the value is strictly better than the solution of the inner system, we need to improve the scheduler.
// TODO: If the underlying solver is not precise, this might run forever (i.e. when a state has two choices where the (exact) values are equal).
// only changing the scheduler if the values are not equal (modulo precision) would make this unsound.
if (valueImproved(dir, x[group], choiceValue)) {
schedulerImproved = true;
scheduler[group] = choice - this->A->getRowGroupIndices()[group];
x[group] = std::move(choiceValue);
// TODO: remove, as this should already be fixed by implementation to determine matrix/vector
if (!this->fixedStates || (this->fixedStates && !(this->fixedStates.get()[group]))) {
for (uint_fast64_t choice = this->A->getRowGroupIndices()[group];
choice < this->A->getRowGroupIndices()[group + 1]; ++choice) {
// If the choice is the currently selected one, we can skip it.
if (choice - this->A->getRowGroupIndices()[group] == currentChoice) {
continue;
}
// Create the value of the choice.
ValueType choiceValue = storm::utility::zero<ValueType>();
for (auto const &entry : this->A->getRow(choice)) {
choiceValue += entry.getValue() * x[entry.getColumn()];
}
choiceValue += b[choice];
// If the value is strictly better than the solution of the inner system, we need to improve the scheduler.
// TODO: If the underlying solver is not precise, this might run forever (i.e. when a state has two choices where the (exact) values are equal).
// only changing the scheduler if the values are not equal (modulo precision) would make this unsound.
if (valueImproved(dir, x[group], choiceValue)) {
schedulerImproved = true;
scheduler[group] = choice - this->A->getRowGroupIndices()[group];
x[group] = std::move(choiceValue);
}
}
} else {
STORM_LOG_INFO("Ignoring state" << group << " as this state is locally monotone");
}
}
@ -203,7 +217,8 @@ namespace storm {
// Potentially show progress.
this->showProgressIterative(iterations);
} while (status == SolverStatus::InProgress);
STORM_LOG_INFO("Number of iterations: " << iterations);
this->reportStatus(status, iterations);
// If requested, we store the scheduler for retrieval.

17
src/storm/solver/MinMaxLinearEquationSolver.cpp

@ -128,6 +128,7 @@ namespace storm {
template<typename ValueType>
void MinMaxLinearEquationSolver<ValueType>::setInitialScheduler(std::vector<uint_fast64_t>&& choices) {
assert (!this->fixedStates || this->fixedStates.get().size() == choices.size());
initialScheduler = std::move(choices);
}
@ -155,7 +156,21 @@ namespace storm {
bool MinMaxLinearEquationSolver<ValueType>::isRequirementsCheckedSet() const {
return requirementsChecked;
}
template<class ValueType>
void MinMaxLinearEquationSolver<ValueType>::setFixedStates(storm::storage::BitVector&& states) {
this->fixedStates = std::move(states);
assert (this->fixedStates);
}
template<class ValueType>
void MinMaxLinearEquationSolver<ValueType>::updateScheduler() {
assert (this->initialScheduler && this->fixedStates);
for (auto state : this->fixedStates.get()) {
this->initialScheduler.get()[state] = 0;
}
}
template<typename ValueType>
MinMaxLinearEquationSolverFactory<ValueType>::MinMaxLinearEquationSolverFactory() : requirementsChecked(false) {
// Intentionally left empty

9
src/storm/solver/MinMaxLinearEquationSolver.h

@ -69,7 +69,10 @@ namespace storm {
* Unsets the optimization direction to use for calls to methods that do not explicitly provide one.
*/
void unsetOptimizationDirection();
void setFixedStates(storm::storage::BitVector&& states);
void updateScheduler();
/*!
* Sets whether the solution to the min max equation system is known to be unique.
*/
@ -182,7 +185,9 @@ namespace storm {
// A scheduler that can be used by solvers that require a valid initial scheduler.
boost::optional<std::vector<uint_fast64_t>> initialScheduler;
boost::optional<storm::storage::BitVector> fixedStates;
private:
/// Whether the solver can assume that the min-max equation system has a unique solution
bool uniqueSolution;

128
src/storm/solver/TopologicalMinMaxLinearEquationSolver.cpp

@ -69,8 +69,8 @@ namespace storm {
}
bool returnValue = true;
if (this->sortedSccDecomposition->size() == 1) {
// Handle the case where there is just one large SCC
if (this->sortedSccDecomposition->size() == 1 && (!this->fixedStates || this->fixedStates.get().empty())) {
// Handle the case where there is just one large SCC, as there are no fixed states, we solve it like this
returnValue = solveFullyConnectedEquationSystem(sccSolverEnvironment, dir, x, b);
} else {
// Solve each SCC individually
@ -89,15 +89,24 @@ namespace storm {
progress.startNewMeasurement(0);
for (auto const& scc : *this->sortedSccDecomposition) {
if (scc.size() == 1) {
// TODO: directly use localMonRes on this
returnValue = solveTrivialScc(*scc.begin(), dir, x, b) && returnValue;
} else {
STORM_LOG_TRACE("Solving SCC of size " << scc.size() << ".");
sccRowGroupsAsBitVector.clear();
sccRowsAsBitVector.clear();
for (auto const& group : scc) {
for (auto const& group : scc) { // Group refers to state
bool allIgnored = true;
sccRowGroupsAsBitVector.set(group, true);
for (uint64_t row = this->A->getRowGroupIndices()[group]; row < this->A->getRowGroupIndices()[group + 1]; ++row) {
if (!this->fixedStates || !this->fixedStates.get()[group]) {
for (uint64_t row = this->A->getRowGroupIndices()[group]; row < this->A->getRowGroupIndices()[group + 1]; ++row) {
sccRowsAsBitVector.set(row, true);
}
} else {
auto row = this->A->getRowGroupIndices()[group]+this->getInitialScheduler()[group];
sccRowsAsBitVector.set(row, true);
STORM_LOG_INFO("Fixing state " << group << " to option " << this->getInitialScheduler()[group] << " because of local monotonicity.");
}
}
returnValue = solveScc(sccSolverEnvironment, dir, sccRowGroupsAsBitVector, sccRowsAsBitVector, x, b) && returnValue;
@ -141,12 +150,13 @@ namespace storm {
ValueType& xi = globalX[sccState];
bool firstRow = true;
uint64_t bestRow;
for (uint64_t row = this->A->getRowGroupIndices()[sccState]; row < this->A->getRowGroupIndices()[sccState + 1]; ++row) {
if (this->fixedStates && this->fixedStates.get()[sccState]) {
assert (this->hasInitialScheduler());
uint64_t row = this->A->getRowGroupIndices()[sccState] + this->initialScheduler.get()[sccState];
ValueType rowValue = globalB[row];
bool hasDiagonalEntry = false;
ValueType denominator;
for (auto const& entry : this->A->getRow(row)) {
for (auto const &entry : this->A->getRow(row)) {
if (entry.getColumn() == sccState) {
hasDiagonalEntry = true;
denominator = storm::utility::one<ValueType>() - entry.getValue();
@ -155,38 +165,68 @@ namespace storm {
}
}
if (hasDiagonalEntry) {
STORM_LOG_WARN_COND_DEBUG(storm::NumberTraits<ValueType>::IsExact || !storm::utility::isAlmostZero(denominator) || storm::utility::isZero(denominator), "State " << sccState << " has a selfloop with probability '1-(" << denominator << ")'. This could be an indication for numerical issues.");
if (storm::utility::isZero(denominator)) {
// In this case we have a selfloop on this state. This can never an optimal choice:
// When minimizing, we are looking for the largest fixpoint (which will never be attained by this action)
// When maximizing, this choice reflects probability zero (non-optimal) or reward infinity (should already be handled during preprocessing).
continue;
} else {
rowValue /= denominator;
}
STORM_LOG_WARN_COND_DEBUG( storm::NumberTraits<ValueType>::IsExact || !storm::utility::isAlmostZero(denominator) ||
storm::utility::isZero(denominator), "State " << sccState << " has a selfloop with probability '1-(" << denominator << ")'. This could be an indication for numerical issues.");
assert (!storm::utility::isZero(denominator));
rowValue /= denominator;
}
if (firstRow) {
if (minimize(dir)) {
xi = std::move(rowValue);
bestRow = row;
firstRow = false;
} else {
if (minimize(dir)) {
if (rowValue < xi) {
xi = std::move(rowValue);
bestRow = row;
xi = std::move(rowValue);
}
STORM_LOG_INFO("Ignoring state" << sccState << " as the scheduler is fixed by monotonicity, current probability for this state is: " << this->schedulerChoices.get()[sccState]);
} else {
for (uint64_t row = this->A->getRowGroupIndices()[sccState]; row < this->A->getRowGroupIndices()[sccState + 1]; ++row) {
ValueType rowValue = globalB[row];
bool hasDiagonalEntry = false;
ValueType denominator;
for (auto const &entry : this->A->getRow(row)) {
if (entry.getColumn() == sccState) {
hasDiagonalEntry = true;
denominator = storm::utility::one<ValueType>() - entry.getValue();
} else {
rowValue += entry.getValue() * globalX[entry.getColumn()];
}
}
if (hasDiagonalEntry) {
STORM_LOG_WARN_COND_DEBUG(
storm::NumberTraits<ValueType>::IsExact || !storm::utility::isAlmostZero(denominator) ||
storm::utility::isZero(denominator),
"State " << sccState << " has a selfloop with probability '1-(" << denominator
<< ")'. This could be an indication for numerical issues.");
if (storm::utility::isZero(denominator)) {
// In this case we have a selfloop on this state. This can never an optimal choice:
// When minimizing, we are looking for the largest fixpoint (which will never be attained by this action)
// When maximizing, this choice reflects probability zero (non-optimal) or reward infinity (should already be handled during preprocessing).
continue;
} else {
rowValue /= denominator;
}
}
if (firstRow) {
xi = std::move(rowValue);
bestRow = row;
firstRow = false;
} else {
if (rowValue > xi) {
xi = std::move(rowValue);
bestRow = row;
if (minimize(dir)) {
if (rowValue < xi) {
xi = std::move(rowValue);
bestRow = row;
}
} else {
if (rowValue > xi) {
xi = std::move(rowValue);
bestRow = row;
}
}
}
}
if (this->isTrackSchedulerSet()) {
this->schedulerChoices.get()[sccState] = bestRow - this->A->getRowGroupIndices()[sccState];
}
STORM_LOG_THROW(!firstRow, storm::exceptions::UnexpectedException, "Empty row group in MinMax equation system.");
}
if (this->isTrackSchedulerSet()) {
this->schedulerChoices.get()[sccState] = bestRow - this->A->getRowGroupIndices()[sccState];
}
STORM_LOG_THROW(!firstRow, storm::exceptions::UnexpectedException, "Empty row group in MinMax equation system.");
//std::cout << "Solved trivial scc " << sccState << " with result " << globalX[sccState] << std::endl;
return true;
}
@ -231,19 +271,38 @@ namespace storm {
template<typename ValueType>
bool TopologicalMinMaxLinearEquationSolver<ValueType>::solveScc(storm::Environment const& sccSolverEnvironment, OptimizationDirection dir, storm::storage::BitVector const& sccRowGroups, storm::storage::BitVector const& sccRows, std::vector<ValueType>& globalX, std::vector<ValueType> const& globalB) const {
// Set up the SCC solver
if (!this->sccSolver) {
this->sccSolver = GeneralMinMaxLinearEquationSolverFactory<ValueType>().create(sccSolverEnvironment);
this->sccSolver->setCachingEnabled(true);
}
if (this->fixedStates) {
// convert fixed states to only fixed states of sccs
storm::storage::BitVector fixedStatesSCC(sccRowGroups.getNumberOfSetBits());
auto j = 0;
for (auto i : sccRowGroups) {
fixedStatesSCC.set(j, this->fixedStates.get()[i]);
j++;
}
assert (j = sccRowGroups.getNumberOfSetBits());
this->sccSolver->setFixedStates(std::move(fixedStatesSCC));
}
this->sccSolver->setHasUniqueSolution(this->hasUniqueSolution());
this->sccSolver->setHasNoEndComponents(this->hasNoEndComponents());
this->sccSolver->setTrackScheduler(this->isTrackSchedulerSet());
// SCC Matrix
storm::storage::SparseMatrix<ValueType> sccA = this->A->getSubmatrix(true, sccRowGroups, sccRowGroups);
//std::cout << "Matrix is " << sccA << std::endl;
storm::storage::SparseMatrix<ValueType> sccA;
if (this->fixedStates) {
sccA = this->A->getSubmatrix(false, sccRows, sccRowGroups);
} else {
sccA = this->A->getSubmatrix(true, sccRowGroups, sccRowGroups);
}
// std::cout << "Matrix is " << sccA << std::endl;
this->sccSolver->setMatrix(std::move(sccA));
// x Vector
@ -266,6 +325,9 @@ namespace storm {
if (this->hasInitialScheduler()) {
auto sccInitChoices = storm::utility::vector::filterVector(this->getInitialScheduler(), sccRowGroups);
this->sccSolver->setInitialScheduler(std::move(sccInitChoices));
if (this->fixedStates) {
this->sccSolver->updateScheduler();
}
}
// lower/upper bounds

40
src/storm/utility/graph.cpp

@ -1665,6 +1665,40 @@ namespace storm {
}
}
template <typename T>
std::vector<uint_fast64_t> getBFSSort(storm::storage::SparseMatrix<T> const& matrix, std::vector<uint_fast64_t> const& firstStates) {
storm::storage::BitVector seenStates(matrix.getRowGroupCount());
std::vector<uint_fast64_t> stateQueue;
stateQueue.reserve(matrix.getRowGroupCount());
std::vector<uint_fast64_t> result;
result.reserve(matrix.getRowGroupCount());
storm::storage::sparse::state_type currentPosition = 0;
auto count = matrix.getRowGroupCount() - 1;
for (auto const& state : firstStates) {
stateQueue.push_back(state);
result[count] = state;
count--;
}
// Perform a BFS.
while (!stateQueue.empty()) {
auto state = stateQueue.back();
stateQueue.pop_back();
seenStates.set(state);
for (auto const& successorEntry : matrix.getRowGroup(state)) {
auto succ = successorEntry.geColumn();
if (!seenStates[succ]) {
result[count] = succ;
count--;
stateQueue.insert(stateQueue.begin(), succ);
}
}
}
return result;
}
template <typename T>
std::vector<uint_fast64_t> getTopologicalSort(storm::storage::SparseMatrix<T> const& matrix, std::vector<uint64_t> const& firstStates) {
if (matrix.getRowCount() != matrix.getColumnCount()) {
@ -1673,7 +1707,7 @@ namespace storm {
}
uint_fast64_t numberOfStates = matrix.getRowCount();
// Prepare the result. This relies on the matrix being square.
std::vector<uint_fast64_t> topologicalSort;
topologicalSort.reserve(numberOfStates);
@ -1696,7 +1730,6 @@ namespace storm {
return topologicalSort;
}
template storm::storage::BitVector getReachableStates(storm::storage::SparseMatrix<double> const& transitionMatrix, storm::storage::BitVector const& initialStates, storm::storage::BitVector const& constraintStates, storm::storage::BitVector const& targetStates, bool useStepBound, uint_fast64_t maximalSteps, boost::optional<storm::storage::BitVector> const& choiceFilter);
template storm::storage::BitVector getBsccCover(storm::storage::SparseMatrix<double> const& transitionMatrix);
@ -1774,7 +1807,7 @@ namespace storm {
template ExplicitGameProb01Result performProb1(storm::storage::SparseMatrix<double> const& transitionMatrix, std::vector<uint64_t> const& player1RowGrouping, storm::storage::SparseMatrix<double> const& player1BackwardTransitions, std::vector<uint64_t> const& player2BackwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, storm::OptimizationDirection const& player1Direction, storm::OptimizationDirection const& player2Direction, storm::abstraction::ExplicitGameStrategyPair* strategyPair, boost::optional<storm::storage::BitVector> const& player1Candidates);
template std::vector<uint_fast64_t> getTopologicalSort(storm::storage::SparseMatrix<double> const& matrix, std::vector<uint64_t> const& firstStates) ;
// Instantiations for storm::RationalNumber.
#ifdef STORM_HAVE_CARL
template storm::storage::BitVector getReachableStates(storm::storage::SparseMatrix<storm::RationalNumber> const& transitionMatrix, storm::storage::BitVector const& initialStates, storm::storage::BitVector const& constraintStates, storm::storage::BitVector const& targetStates, bool useStepBound, uint_fast64_t maximalSteps, boost::optional<storm::storage::BitVector> const& choiceFilter);
@ -1888,7 +1921,6 @@ namespace storm {
template std::vector<uint_fast64_t> getTopologicalSort(storm::storage::SparseMatrix<storm::RationalFunction> const& matrix, std::vector<uint64_t> const& firstStates);
#endif
// Instantiations for CUDD.

4
src/storm/utility/graph.h

@ -3,6 +3,7 @@
#include <set>
#include <limits>
#include <storm/storage/StronglyConnectedComponent.h>
#include "storm/utility/OsDetection.h"
@ -706,6 +707,9 @@ namespace storm {
template <typename T>
std::vector<uint_fast64_t> getTopologicalSort(storm::storage::SparseMatrix<T> const& matrix, std::vector<uint64_t> const& firstStates = {}) ;
template <typename T>
std::vector<uint_fast64_t> getBFSSort(storm::storage::SparseMatrix<T> const& matrix, std::vector<uint_fast64_t> const& firstStates) ;
} // namespace graph
} // namespace utility
} // namespace storm

2
src/test/storm-pars/CMakeLists.txt

@ -12,7 +12,7 @@ include_directories(${GTEST_INCLUDE_DIR})
foreach (testsuite analysis 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)
add_executable (test-pars-${testsuite} ${TEST_${testsuite}_FILES} ${STORM_TESTS_BASE_PATH}/storm-test.cpp analysis/MonotonicityCheckerTest.cpp)
target_link_libraries(test-pars-${testsuite} storm-pars storm-parsers)
target_link_libraries(test-pars-${testsuite} ${STORM_TEST_LINK_LIBRARIES})

284
src/test/storm-pars/analysis/AssumptionCheckerTest.cpp

@ -1,29 +1,22 @@
#include "test/storm_gtest.h"
#include <storm/storage/StronglyConnectedComponentDecomposition.h>
#include "storm-config.h"
#include "test/storm_gtest.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-pars/api/analysis.h"
#include "storm-pars/api/region.h"
#include "storm-pars/transformer/SparseParametricDtmcSimplifier.h"
#include "storm-parsers/api/storm-parsers.h"
#include "storm-parsers/parser/AutoParser.h"
#include "storm-parsers/parser/PrismParser.h"
#include "storm/storage/expressions/ExpressionManager.h"
#include "storm/api/builder.h"
#include "storm-pars/analysis/AssumptionChecker.h"
#include "storm-pars/analysis/Order.h"
#include "storm/storage/expressions/BinaryRelationExpression.h"
#include "storm-pars/transformer/SparseParametricDtmcSimplifier.h"
#include "storm-pars/storage/ParameterRegion.h"
#include "storm-pars/api/storm-pars.h"
#include "storm/api/builder.h"
#include "storm/api/storm.h"
#include "storm/logic/Formulas.h"
#include "storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h"
#include "storm/storage/expressions/BinaryRelationExpression.h"
#include "storm/storage/expressions/ExpressionManager.h"
#include "storm-parsers/api/storm-parsers.h"
#include "storm-pars/api/region.h"
#include "test/storm_gtest.h"
TEST(AssumptionCheckerTest, Brp_no_bisimulation) {
@ -43,35 +36,38 @@ TEST(AssumptionCheckerTest, Brp_no_bisimulation) {
dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
ASSERT_EQ(dtmc->getNumberOfStates(), 193ull);
ASSERT_EQ(dtmc->getNumberOfTransitions(), 383ull);
ASSERT_EQ(dtmc->getNumberOfStates(), 193);
ASSERT_EQ(dtmc->getNumberOfTransitions(), 383);
// Create the region
storm::storage::ParameterRegion<storm::RationalFunction>::Valuation lowerBoundaries;
storm::storage::ParameterRegion<storm::RationalFunction>::Valuation upperBoundaries;
auto vars = storm::models::sparse::getProbabilityParameters(*dtmc);
auto region = storm::api::parseRegion<storm::RationalFunction>("0.00001 <= pK <= 0.00001, 0.00001 <= pL <= 0.99999", vars);
auto checker = storm::analysis::AssumptionChecker<storm::RationalFunction>(formulas[0], dtmc, region, 3);
// Check on samples
auto checker = storm::analysis::AssumptionChecker<storm::RationalFunction, double>(dtmc->getTransitionMatrix());
auto expressionManager = std::make_shared<storm::expressions::ExpressionManager>(storm::expressions::ExpressionManager());
expressionManager->declareRationalVariable("7");
expressionManager->declareRationalVariable("5");
storm::storage::BitVector above(193);
above.set(0);
storm::storage::BitVector below(193);
below.set(1);
auto assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
expressionManager->getVariable("7").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("5").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Greater));
EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, checker.checkOnSamples(assumption));
storm::storage::StronglyConnectedComponentDecompositionOptions options;
options.forceTopologicalSort();
auto decomposition = storm::storage::StronglyConnectedComponentDecomposition<storm::RationalFunction>(model->getTransitionMatrix(), options);
auto statesSorted = storm::utility::graph::getTopologicalSort(model->getTransitionMatrix());
auto dummyOrder = std::shared_ptr<storm::analysis::Order>(new storm::analysis::Order(&above, &below, 193, decomposition, statesSorted));
auto assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(), expressionManager->getVariable("7").getExpression().getBaseExpressionPointer(), expressionManager->getVariable("5").getExpression().getBaseExpressionPointer(), storm::expressions::BinaryRelationExpression::RelationType::Greater));
EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, checker.validateAssumption(assumption, dummyOrder, region));
assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
expressionManager->getVariable("5").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("7").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Greater));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.checkOnSamples(assumption));
EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, checker.validateAssumption(assumption, dummyOrder, region));
assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
@ -79,40 +75,31 @@ TEST(AssumptionCheckerTest, Brp_no_bisimulation) {
expressionManager->getVariable("7").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("5").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Equal));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.checkOnSamples(assumption));
EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, checker.validateAssumption(assumption, dummyOrder, region));
storm::storage::BitVector above(8);
above.set(0);
storm::storage::BitVector below(8);
below.set(1);
storm::storage::BitVector initialMiddle(8);
checker.initializeCheckingOnSamples(formulas[0], dtmc, region, 3);
assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
expressionManager->getVariable("7").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("5").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Greater));
EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, checker.validateAssumption(assumption, dummyOrder, region));
std::vector<uint_fast64_t> statesSorted = storm::utility::graph::getTopologicalSort(model->getTransitionMatrix());
assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
expressionManager->getVariable("5").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("7").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Greater));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, dummyOrder, region));
auto dummyOrder = new storm::analysis::Order(&above, &below, &initialMiddle, 8, &statesSorted);
// Validate assumption
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, dummyOrder));
// EXPECT_FALSE(checker.validated(assumption));
expressionManager->declareRationalVariable("6");
expressionManager->declareRationalVariable("8");
assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
expressionManager->getVariable("6").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("8").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Greater));
above = storm::storage::BitVector(13);
above.set(12);
below = storm::storage::BitVector(13);
below.set(9);
initialMiddle = storm::storage::BitVector(13);
dummyOrder = new storm::analysis::Order(&above, &below, &initialMiddle, 13, &statesSorted);
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.checkOnSamples(assumption));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, dummyOrder));
// EXPECT_EQ(checker.validated(assumption));
// EXPECT_FALSE(checker.valid(assumption));
expressionManager->getVariable("7").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("5").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Equal));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, dummyOrder, region));
}
TEST(AssumptionCheckerTest, Simple1) {
@ -131,44 +118,76 @@ TEST(AssumptionCheckerTest, Simple1) {
model = simplifier.getSimplifiedModel();
dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
ASSERT_EQ(dtmc->getNumberOfStates(), 5ul);
ASSERT_EQ(dtmc->getNumberOfTransitions(), 8ul);
ASSERT_EQ(dtmc->getNumberOfStates(), 5);
ASSERT_EQ(dtmc->getNumberOfTransitions(), 8);
// Create the region
auto vars = storm::models::sparse::getProbabilityParameters(*dtmc);
auto region = storm::api::parseRegion<storm::RationalFunction>("0.00001 <= p <= 0.99999", vars);
auto checker = storm::analysis::AssumptionChecker<storm::RationalFunction>(formulas[0], dtmc, region, 3);
auto checker = storm::analysis::AssumptionChecker<storm::RationalFunction, double>(dtmc->getTransitionMatrix());
auto expressionManager = std::make_shared<storm::expressions::ExpressionManager>(storm::expressions::ExpressionManager());
expressionManager->declareRationalVariable("1");
expressionManager->declareRationalVariable("2");
storm::storage::StronglyConnectedComponentDecompositionOptions options;
options.forceTopologicalSort();
auto decomposition = storm::storage::StronglyConnectedComponentDecomposition<storm::RationalFunction>(model->getTransitionMatrix(), options);
// Checking on samples
storm::storage::BitVector above(5);
above.set(3);
storm::storage::BitVector below(5);
below.set(4);
auto statesSorted = storm::utility::graph::getTopologicalSort(model->getTransitionMatrix());
auto order = std::shared_ptr<storm::analysis::Order>(new storm::analysis::Order(&above, &below, 5, decomposition, statesSorted));
// Validating
auto assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Greater));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.checkOnSamples(assumption));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Greater));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Equal));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
region = storm::api::parseRegion<storm::RationalFunction>("0.51 <= p <= 0.99", vars);
assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Greater));
EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, checker.validateAssumption(assumption, order, region));
assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Greater));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.checkOnSamples(assumption));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Equal));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.checkOnSamples(assumption));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
}
TEST(AssumptionCheckerTest, Simple2) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/simple2.pm";
TEST(AssumptionCheckerTest, Casestudy1) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy1.pm";
std::string formulaAsString = "P=? [F s=3]";
std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
@ -183,14 +202,14 @@ TEST(AssumptionCheckerTest, Simple2) {
model = simplifier.getSimplifiedModel();
dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
ASSERT_EQ(dtmc->getNumberOfStates(), 5ul);
ASSERT_EQ(dtmc->getNumberOfTransitions(), 8ul);
ASSERT_EQ(dtmc->getNumberOfStates(), 5);
ASSERT_EQ(dtmc->getNumberOfTransitions(), 8);
// Create the region
auto vars = storm::models::sparse::getProbabilityParameters(*dtmc);
auto region = storm::api::parseRegion<storm::RationalFunction>("0.00001 <= p <= 0.99999", vars);
auto checker = storm::analysis::AssumptionChecker<storm::RationalFunction>(formulas[0], dtmc, region, 3);
auto checker = storm::analysis::AssumptionChecker<storm::RationalFunction, double>(dtmc->getTransitionMatrix());
auto expressionManager = std::make_shared<storm::expressions::ExpressionManager>(storm::expressions::ExpressionManager());
expressionManager->declareRationalVariable("1");
@ -200,28 +219,27 @@ TEST(AssumptionCheckerTest, Simple2) {
above.set(3);
storm::storage::BitVector below(5);
below.set(4);
storm::storage::BitVector initialMiddle(5);
std::vector<uint_fast64_t> statesSorted = storm::utility::graph::getTopologicalSort(model->getTransitionMatrix());
storm::storage::StronglyConnectedComponentDecompositionOptions options;
options.forceTopologicalSort();
auto decomposition = storm::storage::StronglyConnectedComponentDecomposition<storm::RationalFunction>(model->getTransitionMatrix(), options);
auto statesSorted = storm::utility::graph::getTopologicalSort(model->getTransitionMatrix());
auto order = std::shared_ptr<storm::analysis::Order>(new storm::analysis::Order(&above, &below, 5, decomposition, statesSorted));
auto order = new storm::analysis::Order(&above, &below, &initialMiddle, 5, &statesSorted);
// Checking on samples and validate
// Validating
auto assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Greater));
EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, checker.checkOnSamples(assumption));
EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, checker.validateAssumption(assumption, order));
EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, checker.validateAssumption(assumption, order, region));
assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Greater));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.checkOnSamples(assumption));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
@ -229,12 +247,34 @@ TEST(AssumptionCheckerTest, Simple2) {
expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Equal));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.checkOnSamples(assumption));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
checker.initializeCheckingOnSamples(formulas[0], dtmc, region, 3);
assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Greater));
EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, checker.validateAssumption(assumption, order, region));
assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Greater));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Equal));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
}
TEST(AssumptionCheckerTest, Simple3) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/simple3.pm";
TEST(AssumptionCheckerTest, Casestudy2) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy2.pm";
std::string formulaAsString = "P=? [F s=4]";
std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
@ -256,7 +296,7 @@ TEST(AssumptionCheckerTest, Simple3) {
auto vars = storm::models::sparse::getProbabilityParameters(*dtmc);
auto region = storm::api::parseRegion<storm::RationalFunction>("0.00001 <= p <= 0.99999", vars);
auto checker = storm::analysis::AssumptionChecker<storm::RationalFunction>(formulas[0], dtmc, region, 3);
auto checker = storm::analysis::AssumptionChecker<storm::RationalFunction, double>(dtmc->getTransitionMatrix());
auto expressionManager = std::make_shared<storm::expressions::ExpressionManager>(storm::expressions::ExpressionManager());
expressionManager->declareRationalVariable("1");
@ -266,11 +306,12 @@ TEST(AssumptionCheckerTest, Simple3) {
above.set(4);
storm::storage::BitVector below(6);
below.set(5);
storm::storage::BitVector initialMiddle(6);
std::vector<uint_fast64_t> statesSorted = storm::utility::graph::getTopologicalSort(model->getTransitionMatrix());
auto order = new storm::analysis::Order(&above, &below, &initialMiddle, 6, &statesSorted);
storm::storage::StronglyConnectedComponentDecompositionOptions options;
options.forceTopologicalSort();
auto decomposition = storm::storage::StronglyConnectedComponentDecomposition<storm::RationalFunction>(model->getTransitionMatrix(), options);
auto statesSorted = storm::utility::graph::getTopologicalSort(model->getTransitionMatrix());
auto order = std::shared_ptr<storm::analysis::Order>(new storm::analysis::Order(&above, &below, 6, decomposition, statesSorted));
order->add(3);
// Checking on samples and validate
@ -279,31 +320,25 @@ TEST(AssumptionCheckerTest, Simple3) {
expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Greater));
EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, checker.checkOnSamples(assumption));
EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, checker.validateAssumption(assumption, order));
EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, checker.validateAssumptionSMTSolver(assumption, order));
EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, checker.validateAssumption(assumption, order, region));
assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Greater));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.checkOnSamples(assumption));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumptionSMTSolver(assumption, order));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Equal));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.checkOnSamples(assumption));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumptionSMTSolver(assumption, order));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
}
TEST(AssumptionCheckerTest, Simple4) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/simple4.pm";
TEST(AssumptionCheckerTest, Casestudy3) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy3.pm";
std::string formulaAsString = "P=? [F s=3]";
std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
@ -318,14 +353,14 @@ TEST(AssumptionCheckerTest, Simple4) {
model = simplifier.getSimplifiedModel();
dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
ASSERT_EQ(dtmc->getNumberOfStates(), 5ul);
ASSERT_EQ(dtmc->getNumberOfTransitions(), 8ul);
ASSERT_EQ(dtmc->getNumberOfStates(), 5);
ASSERT_EQ(dtmc->getNumberOfTransitions(), 8);
// Create the region
auto vars = storm::models::sparse::getProbabilityParameters(*dtmc);
auto region = storm::api::parseRegion<storm::RationalFunction>("0.00001 <= p <= 0.4", vars);
auto checker = storm::analysis::AssumptionChecker<storm::RationalFunction>(formulas[0], dtmc, region, 3);
auto checker = storm::analysis::AssumptionChecker<storm::RationalFunction, double>(dtmc->getTransitionMatrix());
auto expressionManager = std::make_shared<storm::expressions::ExpressionManager>(storm::expressions::ExpressionManager());
expressionManager->declareRationalVariable("1");
@ -336,38 +371,53 @@ TEST(AssumptionCheckerTest, Simple4) {
above.set(3);
storm::storage::BitVector below(5);
below.set(4);
storm::storage::BitVector initialMiddle(5);
std::vector<uint_fast64_t> statesSorted = storm::utility::graph::getTopologicalSort(model->getTransitionMatrix());
auto order = new storm::analysis::Order(&above, &below, &initialMiddle, 5, &statesSorted);
storm::storage::StronglyConnectedComponentDecompositionOptions options;
options.forceTopologicalSort();
auto decomposition = storm::storage::StronglyConnectedComponentDecomposition<storm::RationalFunction>(model->getTransitionMatrix(), options);
auto statesSorted = storm::utility::graph::getTopologicalSort(model->getTransitionMatrix());
auto order = std::shared_ptr<storm::analysis::Order>(new storm::analysis::Order(&above, &below, 5, decomposition, statesSorted));
auto assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Greater));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.checkOnSamples(assumption));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumptionSMTSolver(assumption, order));
EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, checker.validateAssumption(assumption, order, region));
assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Greater));
EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, checker.checkOnSamples(assumption));
EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, checker.validateAssumption(assumption, order));
EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, checker.validateAssumptionSMTSolver(assumption, order));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Equal));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.checkOnSamples(assumption));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumptionSMTSolver(assumption, order));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
checker.initializeCheckingOnSamples(formulas[0], dtmc, region, 3);
assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Greater));
EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, checker.validateAssumption(assumption, order, region));
assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Greater));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
storm::expressions::BinaryRelationExpression::RelationType::Equal));
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
}

341
src/test/storm-pars/analysis/AssumptionMakerTest.cpp

@ -1,32 +1,19 @@
//
// Created by Jip Spel on 20.09.18.
//
// TODO: cleanup includes
#include "test/storm_gtest.h"
#include <storm/storage/StronglyConnectedComponentDecomposition.h>
#include "storm-config.h"
#include "test/storm_gtest.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-pars/api/analysis.h"
#include "storm-pars/transformer/SparseParametricDtmcSimplifier.h"
#include "storm-parsers/api/storm-parsers.h"
#include "storm-parsers/parser/AutoParser.h"
#include "storm-parsers/parser/PrismParser.h"
#include "storm/storage/expressions/ExpressionManager.h"
#include "storm/api/builder.h"
#include "storm-pars/analysis/Order.h"
#include "storm-pars/analysis/AssumptionMaker.h"
#include "storm-pars/analysis/AssumptionChecker.h"
#include "storm-pars/analysis/OrderExtender.h"
#include "storm-pars/transformer/SparseParametricDtmcSimplifier.h"
#include "storm-pars/api/storm-pars.h"
#include "storm/api/builder.h"
#include "storm/api/storm.h"
#include "storm-parsers/api/storm-parsers.h"
#include "storm/logic/Formulas.h"
#include "storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h"
#include "storm/models/sparse/StandardRewardModel.h"
TEST(AssumptionMakerTest, Brp_without_bisimulation) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2.pm";
@ -38,280 +25,138 @@ TEST(AssumptionMakerTest, Brp_without_bisimulation) {
program = storm::utility::prism::preprocess(program, constantsAsString);
std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*dtmc);
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
model = simplifier.getSimplifiedModel();
dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
model = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
// Create the region
auto vars = storm::models::sparse::getProbabilityParameters(*dtmc);
auto vars = storm::models::sparse::getProbabilityParameters(*model);
auto region = storm::api::parseRegion<storm::RationalFunction>("0.00001 <= pK <= 0.999999, 0.00001 <= pL <= 0.999999", vars);
ASSERT_EQ(dtmc->getNumberOfStates(), 193ull);
ASSERT_EQ(dtmc->getNumberOfTransitions(), 383ull);
ASSERT_EQ(model->getNumberOfStates(), 193);
ASSERT_EQ(model->getNumberOfTransitions(), 383);
auto *extender = new storm::analysis::OrderExtender<storm::RationalFunction>(dtmc);
auto criticalTuple = extender->toOrder(formulas);
ASSERT_EQ(183ul, std::get<1>(criticalTuple));
ASSERT_EQ(186ul, std::get<2>(criticalTuple));
auto *extender = new storm::analysis::OrderExtender<storm::RationalFunction, double>(model, formulas[0]);
auto criticalTuple = extender->toOrder(region, nullptr);
ASSERT_EQ(183, std::get<1>(criticalTuple));
ASSERT_EQ(186, std::get<2>(criticalTuple));
auto assumptionChecker = storm::analysis::AssumptionChecker<storm::RationalFunction>(formulas[0], dtmc, region, 3);
auto assumptionMaker = storm::analysis::AssumptionMaker<storm::RationalFunction>(&assumptionChecker, dtmc->getNumberOfStates(), true);
auto result = assumptionMaker.createAndCheckAssumption(std::get<1>(criticalTuple), std::get<2>(criticalTuple), std::get<0>(criticalTuple));
auto assumptionMaker = storm::analysis::AssumptionMaker<storm::RationalFunction, double>(model->getTransitionMatrix());
auto result = assumptionMaker.createAndCheckAssumptions(std::get<1>(criticalTuple), std::get<2>(criticalTuple), std::get<0>(criticalTuple), region);
EXPECT_EQ(3ul, result.size());
EXPECT_EQ(3, result.size());
bool foundFirst = false;
bool foundSecond = false;
bool foundThird = false;
for (auto itr = result.begin(); itr != result.end(); ++itr) {
if (!foundFirst && itr->first->getFirstOperand()->asVariableExpression().getVariable().getName() == "183") {
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, itr->second);
EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
EXPECT_EQ("186", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Greater, itr->first->getRelationType());
foundFirst = true;
} else if (!foundSecond && itr->first->getRelationType() == storm::expressions::BinaryRelationExpression::RelationType::Greater) {
EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, itr->second);
EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
EXPECT_EQ("186", itr->first->getFirstOperand()->asVariableExpression().getVariable().getName());
EXPECT_EQ("183", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Greater, itr->first->getRelationType());
foundSecond = true;
} else if (!foundThird && itr->first->getRelationType() == storm::expressions::BinaryRelationExpression::RelationType::Equal) {
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, itr->second);
EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
EXPECT_EQ("186", itr->first->getFirstOperand()->asVariableExpression().getVariable().getName());
EXPECT_EQ("183", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Equal, itr->first->getRelationType());
foundThird = true;
} else {
EXPECT_TRUE(false);
}
for (auto res : result) {
EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, res.second);
EXPECT_EQ(true, res.first->getFirstOperand()->isVariable());
EXPECT_EQ(true, res.first->getSecondOperand()->isVariable());
}
EXPECT_TRUE(foundFirst);
EXPECT_TRUE(foundSecond);
EXPECT_TRUE(foundThird);
}
TEST(AssumptionMakerTest, Brp_without_bisimulation_no_validation) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2.pm";
std::string formulaAsString = "P=? [F s=4 & i=N ]";
std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
// Program and formula
storm::prism::Program program = storm::api::parseProgram(programFile);
program = storm::utility::prism::preprocess(program, constantsAsString);
std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*dtmc);
ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
model = simplifier.getSimplifiedModel();
dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
// Create the region
auto vars = storm::models::sparse::getProbabilityParameters(*dtmc);
auto region = storm::api::parseRegion<storm::RationalFunction>("0.00001 <= pK <= 0.999999, 0.00001 <= pL <= 0.999999", vars);
ASSERT_EQ(dtmc->getNumberOfStates(), 193ull);
ASSERT_EQ(dtmc->getNumberOfTransitions(), 383ull);
auto *extender = new storm::analysis::OrderExtender<storm::RationalFunction>(dtmc);
auto criticalTuple = extender->toOrder(formulas);
ASSERT_EQ(183ul, std::get<1>(criticalTuple));
ASSERT_EQ(186ul, std::get<2>(criticalTuple));
auto assumptionChecker = storm::analysis::AssumptionChecker<storm::RationalFunction>(formulas[0], dtmc, region, 3);
// This one does not validate the assumptions!
auto assumptionMaker = storm::analysis::AssumptionMaker<storm::RationalFunction>(&assumptionChecker, dtmc->getNumberOfStates(), false);
auto result = assumptionMaker.createAndCheckAssumption(std::get<1>(criticalTuple), std::get<2>(criticalTuple), std::get<0>(criticalTuple));
EXPECT_EQ(3ul, result.size());
bool foundFirst = false;
bool foundSecond = false;
bool foundThird = false;
for (auto itr = result.begin(); itr != result.end(); ++itr) {
if (!foundFirst && itr->first->getFirstOperand()->asVariableExpression().getVariable().getName() == "183") {
EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, itr->second);
EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
EXPECT_EQ("186", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Greater, itr->first->getRelationType());
foundFirst = true;
} else if (!foundSecond && itr->first->getRelationType() == storm::expressions::BinaryRelationExpression::RelationType::Greater) {
EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, itr->second);
EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
EXPECT_EQ("186", itr->first->getFirstOperand()->asVariableExpression().getVariable().getName());
EXPECT_EQ("183", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Greater, itr->first->getRelationType());
foundSecond = true;
} else if (!foundThird && itr->first->getRelationType() == storm::expressions::BinaryRelationExpression::RelationType::Equal) {
EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, itr->second);
EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
EXPECT_EQ("186", itr->first->getFirstOperand()->asVariableExpression().getVariable().getName());
EXPECT_EQ("183", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Equal, itr->first->getRelationType());
foundThird = true;
} else {
EXPECT_TRUE(false);
}
}
EXPECT_TRUE(foundFirst);
EXPECT_TRUE(foundSecond);
EXPECT_TRUE(foundThird);
assumptionMaker.initializeCheckingOnSamples(formulas[0], model, region, 10);
result = assumptionMaker.createAndCheckAssumptions(std::get<1>(criticalTuple), std::get<2>(criticalTuple), std::get<0>(criticalTuple), region);
EXPECT_EQ(1, result.size());
auto itr = result.begin();
EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, itr->second);
EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
EXPECT_EQ("186", itr->first->getFirstOperand()->asVariableExpression().getVariable().getName());
EXPECT_EQ("183", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Greater, itr->first->getRelationType());
}
TEST(AssumptionMakerTest, Simple1) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/simple1.pm";
std::string formulaAsString = "P=? [F s=3]";
std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
std::string formulaAsString = "P=? [F s=3 ]";
std::string constantsAsString = "";
storm::prism::Program program = storm::api::parseProgram(programFile);
program = storm::utility::prism::preprocess(program, constantsAsString);
std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*dtmc);
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
model = simplifier.getSimplifiedModel();
dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
// Create the region
auto vars = storm::models::sparse::getProbabilityParameters(*dtmc);
auto vars = storm::models::sparse::getProbabilityParameters(*model);
auto region = storm::api::parseRegion<storm::RationalFunction>("0.00001 <= p <= 0.999999", vars);
ASSERT_EQ(dtmc->getNumberOfStates(), 5ul);
ASSERT_EQ(dtmc->getNumberOfTransitions(), 8ul);
ASSERT_EQ(model->getNumberOfStates(), 5);
ASSERT_EQ(model->getNumberOfTransitions(), 8);
storm::storage::BitVector above(5);
above.set(3);
storm::storage::BitVector below(5);
below.set(4);
storm::storage::BitVector initialMiddle(5);
std::vector<uint_fast64_t> statesSorted = storm::utility::graph::getTopologicalSort(model->getTransitionMatrix());
auto order = new storm::analysis::Order(&above, &below, &initialMiddle, 5, &statesSorted);
auto assumptionChecker = storm::analysis::AssumptionChecker<storm::RationalFunction>(formulas[0], dtmc, region, 3);
auto assumptionMaker = storm::analysis::AssumptionMaker<storm::RationalFunction>(&assumptionChecker, dtmc->getNumberOfStates(), true);
auto result = assumptionMaker.createAndCheckAssumption(1, 2, order);
EXPECT_EQ(3ul, result.size());
bool foundFirst = false;
bool foundSecond = false;
bool foundThird = false;
for (auto itr = result.begin(); itr != result.end(); ++itr) {
if (!foundFirst && itr->first->getFirstOperand()->asVariableExpression().getVariable().getName() == "1") {
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, itr->second);
EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
EXPECT_EQ("2", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Greater, itr->first->getRelationType());
foundFirst = true;
} else if (!foundSecond && itr->first->getRelationType() == storm::expressions::BinaryRelationExpression::RelationType::Greater) {
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, itr->second);
EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
EXPECT_EQ("2", itr->first->getFirstOperand()->asVariableExpression().getVariable().getName());
EXPECT_EQ("1", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Greater, itr->first->getRelationType());
foundSecond = true;
} else if (!foundThird && itr->first->getRelationType() == storm::expressions::BinaryRelationExpression::RelationType::Equal) {
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, itr->second);
EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
EXPECT_EQ("2", itr->first->getFirstOperand()->asVariableExpression().getVariable().getName());
EXPECT_EQ("1", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Equal, itr->first->getRelationType());
foundThird = true;
} else {
EXPECT_TRUE(false);
}
}
EXPECT_TRUE(foundFirst);
EXPECT_TRUE(foundSecond);
EXPECT_TRUE(foundThird);
storm::storage::StronglyConnectedComponentDecompositionOptions options;
options.forceTopologicalSort();
auto decomposition = storm::storage::StronglyConnectedComponentDecomposition<storm::RationalFunction>(model->getTransitionMatrix(), options);
auto statesSorted = storm::utility::graph::getTopologicalSort(model->getTransitionMatrix());
auto order = std::shared_ptr<storm::analysis::Order>(new storm::analysis::Order(&above, &below, 5, decomposition, statesSorted));
auto assumptionMaker = storm::analysis::AssumptionMaker<storm::RationalFunction, double>(model->getTransitionMatrix());
auto result = assumptionMaker.createAndCheckAssumptions(1, 2, order, region);
EXPECT_EQ(0, result.size());
assumptionMaker.initializeCheckingOnSamples(formulas[0], model, region, 10);
result = assumptionMaker.createAndCheckAssumptions(1, 2, order, region);
EXPECT_EQ(0, result.size());
region = storm::api::parseRegion<storm::RationalFunction>("0.500001 <= p <= 0.999999", vars);
std::vector<std::vector<double>> samples;
assumptionMaker.setSampleValues(samples);
result = assumptionMaker.createAndCheckAssumptions(1, 2, order, region);
EXPECT_EQ(1, result.size());
auto itr = result.begin();
EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, itr->second);
EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
EXPECT_EQ("1", itr->first->getFirstOperand()->asVariableExpression().getVariable().getName());
EXPECT_EQ("2", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Greater, itr->first->getRelationType());
}
TEST(AssumptionMakerTest, Simple2) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/simple2.pm";
std::string formulaAsString = "P=? [F s=3]";
std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
TEST(AssumptionMakerTest, Casestudy1) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy1.pm";
std::string formulaAsString = "P=? [F s=3 ]";
std::string constantsAsString = "";
storm::prism::Program program = storm::api::parseProgram(programFile);
program = storm::utility::prism::preprocess(program, constantsAsString);
std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*dtmc);
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
model = simplifier.getSimplifiedModel();
dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
model = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
// Create the region
auto vars = storm::models::sparse::getProbabilityParameters(*dtmc);
auto vars = storm::models::sparse::getProbabilityParameters(*model);
auto region = storm::api::parseRegion<storm::RationalFunction>("0.00001 <= p <= 0.999999", vars);
ASSERT_EQ(dtmc->getNumberOfStates(), 5ul);
ASSERT_EQ(dtmc->getNumberOfTransitions(), 8ul);
ASSERT_EQ(model->getNumberOfStates(), 5);
ASSERT_EQ(model->getNumberOfTransitions(), 8);
storm::storage::BitVector above(5);
above.set(3);
storm::storage::BitVector below(5);
below.set(4);
storm::storage::BitVector initialMiddle(5);
std::vector<uint_fast64_t> statesSorted = storm::utility::graph::getTopologicalSort(model->getTransitionMatrix());
auto order = new storm::analysis::Order(&above, &below, &initialMiddle, 5, &statesSorted);
auto assumptionChecker = storm::analysis::AssumptionChecker<storm::RationalFunction>(formulas[0], dtmc, region, 3);
auto assumptionMaker = storm::analysis::AssumptionMaker<storm::RationalFunction>(&assumptionChecker, dtmc->getNumberOfStates(), true);
auto result = assumptionMaker.createAndCheckAssumption(1, 2, order);
EXPECT_EQ(3ul, result.size());
bool foundFirst = false;
bool foundSecond = false;
bool foundThird = false;
for (auto itr = result.begin(); itr != result.end(); ++itr) {
if (!foundFirst && itr->first->getFirstOperand()->asVariableExpression().getVariable().getName() == "1") {
EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, itr->second);
EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
EXPECT_EQ("2", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Greater, itr->first->getRelationType());
foundFirst = true;
} else if (!foundSecond && itr->first->getRelationType() == storm::expressions::BinaryRelationExpression::RelationType::Greater) {
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, itr->second);
EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
EXPECT_EQ("2", itr->first->getFirstOperand()->asVariableExpression().getVariable().getName());
EXPECT_EQ("1", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Greater, itr->first->getRelationType());
foundSecond = true;
} else if (!foundThird && itr->first->getRelationType() == storm::expressions::BinaryRelationExpression::RelationType::Equal) {
EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, itr->second);
EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
EXPECT_EQ("2", itr->first->getFirstOperand()->asVariableExpression().getVariable().getName());
EXPECT_EQ("1", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Equal, itr->first->getRelationType());
foundThird = true;
} else {
EXPECT_TRUE(false);
}
}
EXPECT_TRUE(foundFirst);
EXPECT_TRUE(foundSecond);
EXPECT_TRUE(foundThird);
storm::storage::StronglyConnectedComponentDecompositionOptions options;
options.forceTopologicalSort();
auto decomposition = storm::storage::StronglyConnectedComponentDecomposition<storm::RationalFunction>(model->getTransitionMatrix(), options);
auto statesSorted = storm::utility::graph::getTopologicalSort(model->getTransitionMatrix());
auto order = std::shared_ptr<storm::analysis::Order>(new storm::analysis::Order(&above, &below, 5, decomposition, statesSorted));
auto assumptionMaker = storm::analysis::AssumptionMaker<storm::RationalFunction, double>(model->getTransitionMatrix());
auto result = assumptionMaker.createAndCheckAssumptions(1, 2, order, region);
EXPECT_EQ(1, result.size());
auto itr = result.begin();
EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, itr->second);
EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
EXPECT_EQ("1", itr->first->getFirstOperand()->asVariableExpression().getVariable().getName());
EXPECT_EQ("2", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Greater, itr->first->getRelationType());
}

375
src/test/storm-pars/analysis/MonotonicityCheckerTest.cpp

@ -1,119 +1,73 @@
//
// Created by Jip Spel on 20.09.18.
//
#include "test/storm_gtest.h"
#include "storm-config.h"
#include "test/storm_gtest.h"
#include "storm-pars/analysis/MonotonicityChecker.h"
#include "storm/storage/expressions/BinaryRelationExpression.h"
#include "storm/storage/SparseMatrix.h"
#include "storm/adapters/RationalFunctionAdapter.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-pars/api/storm-pars.h"
#include "storm-pars/transformer/SparseParametricDtmcSimplifier.h"
#include "storm-parsers/api/storm-parsers.h"
#include "storm-parsers/parser/AutoParser.h"
#include "storm-parsers/parser/PrismParser.h"
#include "storm/storage/expressions/ExpressionManager.h"
#include "storm/adapters/RationalFunctionAdapter.h"
#include "storm/api/builder.h"
#include "storm/api/storm.h"
#include "storm/logic/Formulas.h"
#include "storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h"
#include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h"
#include "storm/storage/SparseMatrix.h"
#include "storm-pars/transformer/SparseParametricDtmcSimplifier.h"
#include "test/storm_gtest.h"
#include "storm-pars/api/storm-pars.h"
#include "storm/api/storm.h"
TEST(MonotonicityCheckerTest, Simple1_larger_region) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/simple1.pm";
std::string formulaAsString = "P=? [F s=3 ]";
std::string constantsAsString = "";
#include "storm-parsers/api/storm-parsers.h"
TEST(MonotonicityCheckerTest, Derivative_checker) {
// model
storm::prism::Program program = storm::api::parseProgram(programFile);
program = storm::utility::prism::preprocess(program, constantsAsString);
std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*dtmc);
ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
model = simplifier.getSimplifiedModel();
// Create the region
typename storm::storage::ParameterRegion<storm::RationalFunction>::Valuation lowerBoundaries;
typename storm::storage::ParameterRegion<storm::RationalFunction>::Valuation upperBoundaries;
auto region = storm::storage::ParameterRegion<storm::RationalFunction>(std::move(lowerBoundaries), std::move(upperBoundaries));
// Derivative 0
auto constFunction = storm::RationalFunction(0);
auto constFunctionRes = storm::analysis::MonotonicityChecker<storm::RationalFunction>::checkDerivative(constFunction, region);
EXPECT_TRUE(constFunctionRes.first);
EXPECT_TRUE(constFunctionRes.second);
// Derivative 5
constFunction = storm::RationalFunction(5);
constFunctionRes = storm::analysis::MonotonicityChecker<storm::RationalFunction>::checkDerivative(constFunction, region);
EXPECT_TRUE(constFunctionRes.first);
EXPECT_FALSE(constFunctionRes.second);
// Derivative -4
constFunction = storm::RationalFunction(storm::RationalFunction(1)-constFunction);
constFunctionRes = storm::analysis::MonotonicityChecker<storm::RationalFunction>::checkDerivative(constFunction, region);
EXPECT_FALSE(constFunctionRes.first);
EXPECT_TRUE(constFunctionRes.second);
std::shared_ptr<storm::RawPolynomialCache> cache = std::make_shared<storm::RawPolynomialCache>();
carl::StringParser parser;
parser.setVariables({"p", "q"});
auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
auto region=storm::api::parseRegion<storm::RationalFunction>("0.1<=p<=0.9", modelParameters);
std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
// Create the region
auto functionP = storm::RationalFunction(storm::Polynomial(parser.template parseMultivariatePolynomial<storm::RationalFunctionCoefficient>("p"), cache));
auto functionQ = storm::RationalFunction(storm::Polynomial(parser.template parseMultivariatePolynomial<storm::RationalFunctionCoefficient>("q"), cache));
auto varsP = functionP.gatherVariables();
auto varsQ = functionQ.gatherVariables();
storm::utility::parametric::Valuation<storm::RationalFunction> lowerBoundaries2;
storm::utility::parametric::Valuation<storm::RationalFunction> upperBoundaries2;
for (auto var : varsP) {
typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType lb = storm::utility::convertNumber<typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType>(0 + 0.000001);
typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType ub = storm::utility::convertNumber<typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType>(1 - 0.000001);
lowerBoundaries2.emplace(std::make_pair(var, lb));
upperBoundaries2.emplace(std::make_pair(var, ub));
}
for (auto var : varsQ) {
typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType lb = storm::utility::convertNumber<typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType>(0 + 0.000001);
typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType ub = storm::utility::convertNumber<typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType>(1 - 0.000001);
lowerBoundaries2.emplace(std::make_pair(var, lb));
upperBoundaries2.emplace(std::make_pair(var, ub));
}
region = storm::storage::ParameterRegion<storm::RationalFunction>(std::move(lowerBoundaries2), std::move(upperBoundaries2));
// Derivative p
auto function = functionP;
auto functionRes = storm::analysis::MonotonicityChecker<storm::RationalFunction>::checkDerivative(function, region);
EXPECT_TRUE(functionRes.first);
EXPECT_FALSE(functionRes.second);
// Derivative 1-p
auto functionDecr = storm::RationalFunction(storm::RationalFunction(1)-function);
auto functionDecrRes = storm::analysis::MonotonicityChecker<storm::RationalFunction>::checkDerivative(functionDecr, region);
EXPECT_TRUE(functionDecrRes.first);
EXPECT_FALSE(functionDecrRes.second);
// Derivative 1-2p
auto functionNonMonotonic = storm::RationalFunction(storm::RationalFunction(1)-storm::RationalFunction(2)*function);
auto functionNonMonotonicRes = storm::analysis::MonotonicityChecker<storm::RationalFunction>::checkDerivative(functionNonMonotonic, region);
EXPECT_FALSE(functionNonMonotonicRes.first);
EXPECT_FALSE(functionNonMonotonicRes.second);
// Derivative -p
functionDecr = storm::RationalFunction(storm::RationalFunction(0)-function);
functionDecrRes = storm::analysis::MonotonicityChecker<storm::RationalFunction>::checkDerivative(functionDecr, region);
EXPECT_FALSE(functionDecrRes.first);
EXPECT_TRUE(functionDecrRes.second);
// Derivative p*q
function = functionP * functionQ ;
functionRes = storm::analysis::MonotonicityChecker<storm::RationalFunction>::checkDerivative(function, region);
EXPECT_TRUE(functionRes.first);
EXPECT_FALSE(functionRes.second);
// For order extender
storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Model<storm::RationalFunction>> propositionalChecker(*model);
storm::storage::BitVector phiStates;
storm::storage::BitVector psiStates;
phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true);
storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula();
psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector();
// Get the maybeStates
std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates);
storm::storage::BitVector topStates = statesWithProbability01.second;
storm::storage::BitVector bottomStates = statesWithProbability01.first;
// OrderExtender
storm::storage::SparseMatrix<storm::RationalFunction> matrix = model->getTransitionMatrix();
auto orderExtender = storm::analysis::OrderExtender<storm::RationalFunction, double>(&topStates, &bottomStates, matrix);
// Order
auto order = std::get<0>(orderExtender.toOrder(region, nullptr));
// monchecker
auto monChecker = new storm::analysis::MonotonicityChecker<storm::RationalFunction>(model->getTransitionMatrix());
//start testing
auto var = modelParameters.begin();
EXPECT_EQ(storm::analysis::MonotonicityChecker<storm::RationalFunction>::Monotonicity::Incr, monChecker->checkLocalMonotonicity(order, 1, *var, region));
EXPECT_EQ(storm::analysis::MonotonicityChecker<storm::RationalFunction>::Monotonicity::Decr, monChecker->checkLocalMonotonicity(order, 2, *var, region));
}
TEST(MonotonicityCheckerTest, Brp_with_bisimulation_no_samples) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2.pm";
std::string formulaAsString = "P=? [true U s=4 & i=N ]";
std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
TEST(MonotonicityCheckerTest, Simple1_small_region) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/simple1.pm";
std::string formulaAsString = "P=? [F s=3 ]";
std::string constantsAsString = "";
// Program and formula
// model
storm::prism::Program program = storm::api::parseProgram(programFile);
program = storm::utility::prism::preprocess(program, constantsAsString);
std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
@ -123,46 +77,92 @@ TEST(MonotonicityCheckerTest, Brp_with_bisimulation_no_samples) {
ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
model = simplifier.getSimplifiedModel();
// Apply bisimulation
storm::storage::BisimulationType bisimType = storm::storage::BisimulationType::Strong;
if (storm::settings::getModule<storm::settings::modules::BisimulationSettings>().isWeakBisimulationSet()) {
bisimType = storm::storage::BisimulationType::Weak;
}
// Create the region
auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
auto region=storm::api::parseRegion<storm::RationalFunction>("0.51<=p<=0.9", modelParameters);
std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
dtmc = storm::api::performBisimulationMinimization<storm::RationalFunction>(model, formulas, bisimType)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
// For order extender
storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Model<storm::RationalFunction>> propositionalChecker(*model);
storm::storage::BitVector phiStates;
storm::storage::BitVector psiStates;
phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true);
storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula();
psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector();
// Get the maybeStates
std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates);
storm::storage::BitVector topStates = statesWithProbability01.second;
storm::storage::BitVector bottomStates = statesWithProbability01.first;
// OrderExtender
storm::storage::SparseMatrix<storm::RationalFunction> matrix = model->getTransitionMatrix();
auto orderExtender = storm::analysis::OrderExtender<storm::RationalFunction, double>(&topStates, &bottomStates, matrix);
// Order
auto order = std::get<0>(orderExtender.toOrder(region, nullptr));
// monchecker
auto monChecker = new storm::analysis::MonotonicityChecker<storm::RationalFunction>(model->getTransitionMatrix());
//start testing
auto var = modelParameters.begin();
EXPECT_EQ(storm::analysis::MonotonicityChecker<storm::RationalFunction>::Monotonicity::Incr, monChecker->checkLocalMonotonicity(order, 0, *var, region));
EXPECT_EQ(storm::analysis::MonotonicityChecker<storm::RationalFunction>::Monotonicity::Incr, monChecker->checkLocalMonotonicity(order, 1, *var, region));
EXPECT_EQ(storm::analysis::MonotonicityChecker<storm::RationalFunction>::Monotonicity::Decr, monChecker->checkLocalMonotonicity(order, 2, *var, region));
}
TEST(MonotonicityCheckerTest, Casestudy1) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy1.pm";
std::string formulaAsString = "P=? [F s=3 ]";
std::string constantsAsString = "";
// model
storm::prism::Program program = storm::api::parseProgram(programFile);
program = storm::utility::prism::preprocess(program, constantsAsString);
std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*dtmc);
ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
model = simplifier.getSimplifiedModel();
// Create the region
typename storm::storage::ParameterRegion<storm::RationalFunction>::Valuation lowerBoundaries;
typename storm::storage::ParameterRegion<storm::RationalFunction>::Valuation upperBoundaries;
std::set<typename storm::storage::ParameterRegion<storm::RationalFunction>::VariableType> vars = storm::models::sparse::getProbabilityParameters(*dtmc);
for (auto var : vars) {
typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType lb = storm::utility::convertNumber<typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType>(0 + 0.000001);
typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType ub = storm::utility::convertNumber<typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType>(1 - 0.000001);
lowerBoundaries.emplace(std::make_pair(var, lb));
upperBoundaries.emplace(std::make_pair(var, ub));
auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
auto region=storm::api::parseRegion<storm::RationalFunction>("0.1<=p<=0.9", modelParameters);
std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
// For order extender
storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Model<storm::RationalFunction>> propositionalChecker(*model);
storm::storage::BitVector phiStates;
storm::storage::BitVector psiStates;
phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true);
storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula();
psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector();
// Get the maybeStates
std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates);
storm::storage::BitVector topStates = statesWithProbability01.second;
storm::storage::BitVector bottomStates = statesWithProbability01.first;
// OrderExtender
storm::storage::SparseMatrix<storm::RationalFunction> matrix = model->getTransitionMatrix();
auto orderExtender = storm::analysis::OrderExtender<storm::RationalFunction, double>(&topStates, &bottomStates, matrix);
// Order
auto res =orderExtender.extendOrder(nullptr, region);
auto order = std::get<0>(res);
ASSERT_TRUE(order->getDoneBuilding());
//monchecker
auto monChecker = new storm::analysis::MonotonicityChecker<storm::RationalFunction>(matrix);
//start testing
auto var = modelParameters.begin();
for (uint_fast64_t i = 0; i < 3; i++) {
EXPECT_EQ(storm::analysis::MonotonicityChecker<storm::RationalFunction>::Monotonicity::Incr, monChecker->checkLocalMonotonicity(order, i, *var, region));
}
auto region = storm::storage::ParameterRegion<storm::RationalFunction>(std::move(lowerBoundaries), std::move(upperBoundaries));
std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
ASSERT_EQ(dtmc->getNumberOfStates(), 99ull);
ASSERT_EQ(dtmc->getNumberOfTransitions(), 195ull);
storm::analysis::MonotonicityChecker<storm::RationalFunction> monotonicityChecker = storm::analysis::MonotonicityChecker<storm::RationalFunction>(dtmc, formulas, regions, true);
auto result = monotonicityChecker.checkMonotonicity(std::cout);
EXPECT_EQ(1ul, result.size());
EXPECT_EQ(2ul, result.begin()->second.size());
auto monotone = result.begin()->second.begin();
EXPECT_EQ(true, monotone->second.first);
EXPECT_EQ(false, monotone->second.second);
}
TEST(MonotonicityCheckerTest, Brp_with_bisimulation_samples) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2.pm";
std::string formulaAsString = "P=? [true U s=4 & i=N ]";
std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
TEST(MonotonicityCheckerTest, Casestudy2) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy2.pm";
std::string formulaAsString = "P=? [F s=4 ]";
std::string constantsAsString = "";
// Program and formula
// model
storm::prism::Program program = storm::api::parseProgram(programFile);
program = storm::utility::prism::preprocess(program, constantsAsString);
std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
@ -172,35 +172,92 @@ TEST(MonotonicityCheckerTest, Brp_with_bisimulation_samples) {
ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
model = simplifier.getSimplifiedModel();
// Apply bisimulation
storm::storage::BisimulationType bisimType = storm::storage::BisimulationType::Strong;
if (storm::settings::getModule<storm::settings::modules::BisimulationSettings>().isWeakBisimulationSet()) {
bisimType = storm::storage::BisimulationType::Weak;
// Create the region
auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
auto region=storm::api::parseRegion<storm::RationalFunction>("0.51<=p<=0.9", modelParameters);
std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
// For order extender
storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Model<storm::RationalFunction>> propositionalChecker(*model);
storm::storage::BitVector phiStates;
storm::storage::BitVector psiStates;
phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true);
storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula();
psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector();
// Get the maybeStates
std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates);
storm::storage::BitVector topStates = statesWithProbability01.second;
storm::storage::BitVector bottomStates = statesWithProbability01.first;
// OrderExtender
storm::storage::SparseMatrix<storm::RationalFunction> matrix = model->getTransitionMatrix();
auto orderExtender = storm::analysis::OrderExtender<storm::RationalFunction, double>(&topStates, &bottomStates, matrix);
// Order
auto res =orderExtender.extendOrder(nullptr, region);
auto order = std::get<0>(res);
order->addRelation(1,3);
order->addRelation(3,2);
//monchecker
auto monChecker = new storm::analysis::MonotonicityChecker<storm::RationalFunction>(matrix);
//start testing
auto var = modelParameters.begin();
for (uint_fast64_t i = 0; i < 3; i++) {
EXPECT_EQ(storm::analysis::MonotonicityChecker<storm::RationalFunction>::Monotonicity::Incr, monChecker->checkLocalMonotonicity(order, i, *var, region));
}
}
dtmc = storm::api::performBisimulationMinimization<storm::RationalFunction>(model, formulas, bisimType)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
TEST(MonotonicityCheckerTest, Casestudy3) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy3.pm";
std::string formulaAsString = "P=? [F s=3 ]";
std::string constantsAsString = "";
// model
storm::prism::Program program = storm::api::parseProgram(programFile);
program = storm::utility::prism::preprocess(program, constantsAsString);
std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*dtmc);
ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
model = simplifier.getSimplifiedModel();
// Create the region
typename storm::storage::ParameterRegion<storm::RationalFunction>::Valuation lowerBoundaries;
typename storm::storage::ParameterRegion<storm::RationalFunction>::Valuation upperBoundaries;
std::set<typename storm::storage::ParameterRegion<storm::RationalFunction>::VariableType> vars = storm::models::sparse::getProbabilityParameters(*dtmc);
for (auto var : vars) {
typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType lb = storm::utility::convertNumber<typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType>(0 + 0.000001);
typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType ub = storm::utility::convertNumber<typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType>(1 - 0.000001);
lowerBoundaries.emplace(std::make_pair(var, lb));
upperBoundaries.emplace(std::make_pair(var, ub));
}
auto region = storm::storage::ParameterRegion<storm::RationalFunction>(std::move(lowerBoundaries), std::move(upperBoundaries));
auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
auto region=storm::api::parseRegion<storm::RationalFunction>("0.1<=p<=0.9", modelParameters);
std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
ASSERT_EQ(dtmc->getNumberOfStates(), 99ull);
ASSERT_EQ(dtmc->getNumberOfTransitions(), 195ull);
auto monotonicityChecker = storm::analysis::MonotonicityChecker<storm::RationalFunction>(dtmc, formulas, regions, true, 50);
auto result = monotonicityChecker.checkMonotonicity(std::cout);
EXPECT_EQ(1ul, result.size());
EXPECT_EQ(2ul, result.begin()->second.size());
auto monotone = result.begin()->second.begin();
EXPECT_EQ(true, monotone->second.first);
EXPECT_EQ(false, monotone->second.second);
// For order extender
storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Model<storm::RationalFunction>> propositionalChecker(*model);
storm::storage::BitVector phiStates;
storm::storage::BitVector psiStates;
phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true);
storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula();
psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector();
// Get the maybeStates
std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates);
storm::storage::BitVector topStates = statesWithProbability01.second;
storm::storage::BitVector bottomStates = statesWithProbability01.first;
// OrderExtender
storm::storage::SparseMatrix<storm::RationalFunction> matrix = model->getTransitionMatrix();
auto orderExtender = storm::analysis::OrderExtender<storm::RationalFunction, double>(&topStates, &bottomStates, matrix);
// Order
auto res =orderExtender.extendOrder(nullptr, region);
auto order = std::get<0>(res);
ASSERT_TRUE(order->getDoneBuilding());
//monchecker
auto monChecker = new storm::analysis::MonotonicityChecker<storm::RationalFunction>(matrix);
//start testing
auto var = modelParameters.begin();
EXPECT_EQ(storm::analysis::MonotonicityChecker<storm::RationalFunction>::Monotonicity::Not, monChecker->checkLocalMonotonicity(order, 0, *var, region));
EXPECT_EQ(storm::analysis::MonotonicityChecker<storm::RationalFunction>::Monotonicity::Incr, monChecker->checkLocalMonotonicity(order, 1, *var, region));
EXPECT_EQ(storm::analysis::MonotonicityChecker<storm::RationalFunction>::Monotonicity::Incr, monChecker->checkLocalMonotonicity(order, 2, *var, region));
region=storm::api::parseRegion<storm::RationalFunction>("0.51<=p<=0.9", modelParameters);
EXPECT_EQ(storm::analysis::MonotonicityChecker<storm::RationalFunction>::Monotonicity::Decr, monChecker->checkLocalMonotonicity(order, 0, *var, region));
EXPECT_EQ(storm::analysis::MonotonicityChecker<storm::RationalFunction>::Monotonicity::Incr, monChecker->checkLocalMonotonicity(order, 1, *var, region));
EXPECT_EQ(storm::analysis::MonotonicityChecker<storm::RationalFunction>::Monotonicity::Incr, monChecker->checkLocalMonotonicity(order, 2, *var, region));
}

454
src/test/storm-pars/analysis/MonotonicityHelperTest.cpp

@ -0,0 +1,454 @@
#include "test/storm_gtest.h"
#include "storm-config.h"
#include "test/storm_gtest.h"
#include "storm/storage/expressions/BinaryRelationExpression.h"
#include "storm/storage/SparseMatrix.h"
#include "storm/adapters/RationalFunctionAdapter.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-parsers/parser/AutoParser.h"
#include "storm-parsers/parser/PrismParser.h"
#include "storm/storage/expressions/ExpressionManager.h"
#include "storm/api/builder.h"
#include "storm-pars/transformer/SparseParametricDtmcSimplifier.h"
#include "storm-pars/api/storm-pars.h"
#include "storm/api/storm.h"
#include "storm-parsers/api/storm-parsers.h"
TEST(MonotonicityHelperTest, Derivative_checker) {
// Create the region
typename storm::storage::ParameterRegion<storm::RationalFunction>::Valuation lowerBoundaries;
typename storm::storage::ParameterRegion<storm::RationalFunction>::Valuation upperBoundaries;
auto region = storm::storage::ParameterRegion<storm::RationalFunction>(std::move(lowerBoundaries), std::move(upperBoundaries));
// Derivative 0
auto constFunction = storm::RationalFunction(0);
auto constFunctionRes = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>::checkDerivative(constFunction, region);
EXPECT_TRUE(constFunctionRes.first);
EXPECT_TRUE(constFunctionRes.second);
// Derivative 5
constFunction = storm::RationalFunction(5);
constFunctionRes = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>::checkDerivative(constFunction, region);
EXPECT_TRUE(constFunctionRes.first);
EXPECT_FALSE(constFunctionRes.second);
// Derivative -4
constFunction = storm::RationalFunction(storm::RationalFunction(1)-constFunction);
constFunctionRes = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>::checkDerivative(constFunction, region);
EXPECT_FALSE(constFunctionRes.first);
EXPECT_TRUE(constFunctionRes.second);
std::shared_ptr<storm::RawPolynomialCache> cache = std::make_shared<storm::RawPolynomialCache>();
carl::StringParser parser;
parser.setVariables({"p", "q"});
// Create the region
auto functionP = storm::RationalFunction(storm::Polynomial(parser.template parseMultivariatePolynomial<storm::RationalFunctionCoefficient>("p"), cache));
auto functionQ = storm::RationalFunction(storm::Polynomial(parser.template parseMultivariatePolynomial<storm::RationalFunctionCoefficient>("q"), cache));
auto varsP = functionP.gatherVariables();
auto varsQ = functionQ.gatherVariables();
storm::utility::parametric::Valuation<storm::RationalFunction> lowerBoundaries2;
storm::utility::parametric::Valuation<storm::RationalFunction> upperBoundaries2;
for (auto var : varsP) {
typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType lb = storm::utility::convertNumber<typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType>(0 + 0.000001);
typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType ub = storm::utility::convertNumber<typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType>(1 - 0.000001);
lowerBoundaries2.emplace(std::make_pair(var, lb));
upperBoundaries2.emplace(std::make_pair(var, ub));
}
for (auto var : varsQ) {
typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType lb = storm::utility::convertNumber<typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType>(0 + 0.000001);
typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType ub = storm::utility::convertNumber<typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType>(1 - 0.000001);
lowerBoundaries2.emplace(std::make_pair(var, lb));
upperBoundaries2.emplace(std::make_pair(var, ub));
}
region = storm::storage::ParameterRegion<storm::RationalFunction>(std::move(lowerBoundaries2), std::move(upperBoundaries2));
// Derivative p
auto function = functionP;
auto functionRes = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>::checkDerivative(function, region);
EXPECT_TRUE(functionRes.first);
EXPECT_FALSE(functionRes.second);
// Derivative 1-p
auto functionDecr = storm::RationalFunction(storm::RationalFunction(1)-function);
auto functionDecrRes = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>::checkDerivative(functionDecr, region);
EXPECT_TRUE(functionDecrRes.first);
EXPECT_FALSE(functionDecrRes.second);
// Derivative 1-2p
auto functionNonMonotonic = storm::RationalFunction(storm::RationalFunction(1)-storm::RationalFunction(2)*function);
auto functionNonMonotonicRes = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>::checkDerivative(functionNonMonotonic, region);
EXPECT_FALSE(functionNonMonotonicRes.first);
EXPECT_FALSE(functionNonMonotonicRes.second);
// Derivative -p
functionDecr = storm::RationalFunction(storm::RationalFunction(0)-function);
functionDecrRes = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>::checkDerivative(functionDecr, region);
EXPECT_FALSE(functionDecrRes.first);
EXPECT_TRUE(functionDecrRes.second);
// Derivative p*q
function = functionP * functionQ ;
functionRes = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>::checkDerivative(function, region);
EXPECT_TRUE(functionRes.first);
EXPECT_FALSE(functionRes.second);
}
TEST(MonotonicityHelperTest, Brp_with_bisimulation_no_samples) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2.pm";
std::string formulaAsString = "P=? [true U s=4 & i=N ]";
std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
// Program and formula
storm::prism::Program program = storm::api::parseProgram(programFile);
program = storm::utility::prism::preprocess(program, constantsAsString);
std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
model = simplifier.getSimplifiedModel();
// Apply bisimulation
storm::storage::BisimulationType bisimType = storm::storage::BisimulationType::Strong;
if (storm::settings::getModule<storm::settings::modules::BisimulationSettings>().isWeakBisimulationSet()) {
bisimType = storm::storage::BisimulationType::Weak;
}
model = storm::api::performBisimulationMinimization<storm::RationalFunction>(model, formulas, bisimType)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
ASSERT_EQ(model->getNumberOfStates(), 99);
ASSERT_EQ(model->getNumberOfTransitions(), 195);
// Create the region
auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
auto region=storm::api::parseRegion<storm::RationalFunction>("0.1<=pL<=0.9, 0.1<=pK<=0.9", modelParameters);
std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
// Start testing
storm::analysis::MonotonicityHelper<storm::RationalFunction, double> MonotonicityHelper = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>(model, formulas, regions, true);
// Check if correct result size
auto result = MonotonicityHelper.checkMonotonicityInBuild(std::cout, false);
EXPECT_EQ(1, result.size());
// Check if the order and general monotonicity result is correct.
auto order = result.begin()->first;
auto monotonicityResult = result.begin()->second.first;
EXPECT_TRUE(monotonicityResult->isDone());
EXPECT_TRUE(monotonicityResult->existsMonotonicity());
EXPECT_TRUE(monotonicityResult->isAllMonotonicity());
auto assumptions = result.begin()->second.second;
EXPECT_EQ(0, assumptions.size());
// Check if result for each variable is correct
auto monRes = monotonicityResult->getMonotonicityResult();
for (auto entry : monRes) {
EXPECT_EQ(storm::analysis::MonotonicityResult<storm::RationalFunctionVariable>::Monotonicity::Incr, entry.second);
}
}
TEST(MonotonicityHelperTest, Brp_with_bisimulation_samples) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2.pm";
std::string formulaAsString = "P=? [true U s=4 & i=N ]";
std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
// Program and formula
storm::prism::Program program = storm::api::parseProgram(programFile);
program = storm::utility::prism::preprocess(program, constantsAsString);
std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
model = simplifier.getSimplifiedModel();
// Apply bisimulation
storm::storage::BisimulationType bisimType = storm::storage::BisimulationType::Strong;
if (storm::settings::getModule<storm::settings::modules::BisimulationSettings>().isWeakBisimulationSet()) {
bisimType = storm::storage::BisimulationType::Weak;
}
model = storm::api::performBisimulationMinimization<storm::RationalFunction>(model, formulas, bisimType)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
ASSERT_EQ(model->getNumberOfStates(), 99);
ASSERT_EQ(model->getNumberOfTransitions(), 195);
// Create the region
auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
auto region=storm::api::parseRegion<storm::RationalFunction>("0.1<=pL<=0.9, 0.1<=pK<=0.9", modelParameters);
std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
// Start testing
storm::analysis::MonotonicityHelper<storm::RationalFunction, double> MonotonicityHelper = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>(model, formulas, regions, true, 50);
// Check if correct result size
auto result = MonotonicityHelper.checkMonotonicityInBuild(std::cout, false);
EXPECT_EQ(1, result.size());
// Check if the order and general monotonicity result is correct.
auto order = result.begin()->first;
auto monotonicityResult = result.begin()->second.first;
EXPECT_TRUE(monotonicityResult->isDone());
EXPECT_TRUE(monotonicityResult->existsMonotonicity());
EXPECT_TRUE(monotonicityResult->isAllMonotonicity());
auto assumptions = result.begin()->second.second;
EXPECT_EQ(0, assumptions.size());
// Check if result for each variable is correct
auto monRes = monotonicityResult->getMonotonicityResult();
for (auto entry : monRes) {
EXPECT_EQ(storm::analysis::MonotonicityResult<storm::RationalFunctionVariable>::Monotonicity::Incr, entry.second);
}
}
TEST(MonotonicityHelperTest, zeroconf) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/zeroconf4.pm";
std::string formulaAsString = "P > 0.5 [ F s=5 ]";
std::string constantsAsString = "n = 4"; //e.g. pL=0.9,TOACK=0.5
// Program and formula
storm::prism::Program program = storm::api::parseProgram(programFile);
program = storm::utility::prism::preprocess(program, constantsAsString);
std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
model = simplifier.getSimplifiedModel();
storm::storage::BisimulationType bisimType = storm::storage::BisimulationType::Strong;
if (storm::settings::getModule<storm::settings::modules::BisimulationSettings>().isWeakBisimulationSet()) {
bisimType = storm::storage::BisimulationType::Weak;
}
model = storm::api::performBisimulationMinimization<storm::RationalFunction>(model, formulas, bisimType)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
ASSERT_EQ(model->getNumberOfStates(), 7);
ASSERT_EQ(model->getNumberOfTransitions(), 12);
// Create region
auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
auto region=storm::api::parseRegion<storm::RationalFunction>("0.1<=pL<=0.9, 0.1<=pK<=0.9", modelParameters);
std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
// Start testing
auto MonotonicityHelper = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>(model, formulas, regions, 50);
// Check if correct result size
auto result = MonotonicityHelper.checkMonotonicityInBuild(std::cout, false);
EXPECT_EQ(1, result.size());
// Check if the order and general monotonicity result is correct.
auto order = result.begin()->first;
auto monotonicityResult = result.begin()->second.first;
EXPECT_TRUE(monotonicityResult->isDone());
EXPECT_TRUE(monotonicityResult->existsMonotonicity());
EXPECT_TRUE(monotonicityResult->isAllMonotonicity());
// TODO @Jip we have 1 assumption instead of 0 here
auto assumptions = result.begin()->second.second;
EXPECT_EQ(0, assumptions.size());
// Check if result for each variable is correct
auto monRes = monotonicityResult->getMonotonicityResult();
for (auto entry : monRes) {
EXPECT_EQ(storm::analysis::MonotonicityResult<storm::RationalFunctionVariable>::Monotonicity::Incr, entry.second);
}
}
TEST(MonotonicityHelperTest, Simple1) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/simple1.pm";
std::string formulaAsString = "P > 0.5 [ F s=3 ]";
std::string constantsAsString = "";
// Program and formula
storm::prism::Program program = storm::api::parseProgram(programFile);
program = storm::utility::prism::preprocess(program, constantsAsString);
std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
model = simplifier.getSimplifiedModel();
ASSERT_EQ(model->getNumberOfStates(), 5);
ASSERT_EQ(model->getNumberOfTransitions(), 8);
// Create region
auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
auto region=storm::api::parseRegion<storm::RationalFunction>("0.1<=p<=0.49", modelParameters);
std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
// Start testing
auto MonotonicityHelper = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>(model, formulas, regions, 10);
// Check if correct result size
auto result = MonotonicityHelper.checkMonotonicityInBuild(std::cout, false);
EXPECT_EQ(1, result.size());
// Check if the order and general monotonicity result is correct.
auto order = result.begin()->first;
auto monotonicityResult = result.begin()->second.first;
EXPECT_TRUE(monotonicityResult->isDone());
EXPECT_FALSE(monotonicityResult->existsMonotonicity());
EXPECT_FALSE(monotonicityResult->isAllMonotonicity());
auto assumptions = result.begin()->second.second;
EXPECT_EQ(0, assumptions.size());
// Check if result for each variable is correct
auto monRes = monotonicityResult->getMonotonicityResult();
for (auto entry : monRes) {
EXPECT_EQ(storm::analysis::MonotonicityResult<storm::RationalFunctionVariable>::Monotonicity::Unknown, entry.second);
}
}
TEST(MonotonicityHelperTest, Casestudy1) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy1.pm";
std::string formulaAsString = "P > 0.5 [ F s=3 ]";
std::string constantsAsString = "";
// Program and formula
storm::prism::Program program = storm::api::parseProgram(programFile);
program = storm::utility::prism::preprocess(program, constantsAsString);
std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
model = simplifier.getSimplifiedModel();
// Create region
auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
auto region=storm::api::parseRegion<storm::RationalFunction>("0.1<=p<=0.9", modelParameters);
std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
ASSERT_EQ(model->getNumberOfStates(), 5);
ASSERT_EQ(model->getNumberOfTransitions(), 8);
auto MonotonicityHelper = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>(model, formulas, regions, 10);
auto result = MonotonicityHelper.checkMonotonicityInBuild(std::cout, false);
ASSERT_EQ(1, result.size());
auto order = result.begin()->first;
auto monotonicityResult = result.begin()->second.first;
EXPECT_TRUE(monotonicityResult->isDone());
EXPECT_TRUE(monotonicityResult->existsMonotonicity());
EXPECT_TRUE(monotonicityResult->isAllMonotonicity());
auto assumptions = result.begin()->second.second;
EXPECT_EQ(0, assumptions.size());
auto monRes = monotonicityResult->getMonotonicityResult();
for (auto entry : monRes) {
EXPECT_EQ(storm::analysis::MonotonicityResult<storm::RationalFunctionVariable>::Monotonicity::Incr, entry.second);
}
}
TEST(MonotonicityHelperTest, CaseStudy2) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy2.pm";
std::string formulaAsString = "P > 0.5 [ F s=4 ]";
std::string constantsAsString = "";
// Program and formula
storm::prism::Program program = storm::api::parseProgram(programFile);
program = storm::utility::prism::preprocess(program, constantsAsString);
std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
model = simplifier.getSimplifiedModel();
// Create region
auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
auto region=storm::api::parseRegion<storm::RationalFunction>("0.1<=p<=0.9", modelParameters);
std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
ASSERT_EQ(model->getNumberOfStates(), 6);
ASSERT_EQ(model->getNumberOfTransitions(), 12);
// Start testing
auto monotonicityHelper = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>(model, formulas, regions, 10);
// Check if correct result size
auto result = monotonicityHelper.checkMonotonicityInBuild(std::cout, false);
EXPECT_EQ(1, result.size());
EXPECT_FALSE(result.begin()->first->getDoneBuilding());
}
TEST(MonotonicityHelperTest, Casestudy3_not_monotone) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy3.pm";
std::string formulaAsString = "P > 0.5 [ F s=3 ]";
std::string constantsAsString = "";
// Program and formula
storm::prism::Program program = storm::api::parseProgram(programFile);
program = storm::utility::prism::preprocess(program, constantsAsString);
std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
model = simplifier.getSimplifiedModel();
// Create region
auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
auto region=storm::api::parseRegion<storm::RationalFunction>("0.1<=p<=0.9", modelParameters);
std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
ASSERT_EQ(model->getNumberOfStates(), 5);
ASSERT_EQ(model->getNumberOfTransitions(), 8);
auto MonotonicityHelper = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>(model, formulas, regions, 10);
auto result = MonotonicityHelper.checkMonotonicityInBuild(std::cout, false);
ASSERT_EQ(1, result.size());
auto order = result.begin()->first;
auto monotonicityResult = result.begin()->second.first;
EXPECT_TRUE(monotonicityResult->isDone());
EXPECT_FALSE(monotonicityResult->existsMonotonicity());
EXPECT_FALSE(monotonicityResult->isAllMonotonicity());
auto assumptions = result.begin()->second.second;
EXPECT_EQ(0, assumptions.size());
auto monRes = monotonicityResult->getMonotonicityResult();
for (auto entry : monRes) {
EXPECT_EQ(storm::analysis::MonotonicityResult<storm::RationalFunctionVariable>::Monotonicity::Unknown, entry.second);
}
}
TEST(MonotonicityHelperTest, Casestudy3_monotone) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy3.pm";
std::string formulaAsString = "P > 0.5 [ F s=3 ]";
std::string constantsAsString = "";
// Program and formula
storm::prism::Program program = storm::api::parseProgram(programFile);
program = storm::utility::prism::preprocess(program, constantsAsString);
std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
model = simplifier.getSimplifiedModel();
// Create region
auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
auto region=storm::api::parseRegion<storm::RationalFunction>("0.1<=p<=0.49", modelParameters);
std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
ASSERT_EQ(model->getNumberOfStates(), 5);
ASSERT_EQ(model->getNumberOfTransitions(), 8);
auto MonotonicityHelper = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>(model, formulas, regions, 10);
auto result = MonotonicityHelper.checkMonotonicityInBuild(std::cout, false);
ASSERT_EQ(1, result.size());
auto order = result.begin()->first;
auto monotonicityResult = result.begin()->second.first;
EXPECT_TRUE(monotonicityResult->isDone());
EXPECT_TRUE(monotonicityResult->existsMonotonicity());
EXPECT_TRUE(monotonicityResult->isAllMonotonicity());
auto assumptions = result.begin()->second.second;
EXPECT_EQ(0, assumptions.size());
auto monRes = monotonicityResult->getMonotonicityResult();
for (auto entry : monRes) {
EXPECT_EQ(storm::analysis::MonotonicityResult<storm::RationalFunctionVariable>::Monotonicity::Incr, entry.second);
}
}

364
src/test/storm-pars/analysis/OrderExtenderTest.cpp

@ -1,27 +1,22 @@
#include "test/storm_gtest.h"
#include "storm-config.h"
#include "test/storm_gtest.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-pars/api/storm-pars.h"
#include "storm-pars/transformer/SparseParametricDtmcSimplifier.h"
#include "storm-parsers/api/storm-parsers.h"
#include "storm-parsers/parser/AutoParser.h"
#include "storm-parsers/parser/PrismParser.h"
#include "storm/storage/expressions/ExpressionManager.h"
#include "storm/api/builder.h"
#include "storm-pars/analysis/Order.h"
#include "storm-pars/analysis/OrderExtender.h"
#include "storm-pars/transformer/SparseParametricDtmcSimplifier.h"
#include "storm-pars/api/storm-pars.h"
#include "storm/api/builder.h"
#include "storm/api/storm.h"
#include "storm/logic/Formulas.h"
#include "storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h"
#include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h"
#include "storm/models/sparse/StandardRewardModel.h"
#include "storm-parsers/api/storm-parsers.h"
#include "test/storm_gtest.h"
TEST(OrderExtenderTest, Brp_with_bisimulation) {
TEST(OrderExtenderTest, Brp_with_bisimulation_on_model) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2.pm";
std::string formulaAsString = "P=? [F s=4 & i=N ]";
std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
@ -31,8 +26,7 @@ TEST(OrderExtenderTest, Brp_with_bisimulation) {
program = storm::utility::prism::preprocess(program, constantsAsString);
std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*dtmc);
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
model = simplifier.getSimplifiedModel();
@ -42,19 +36,23 @@ TEST(OrderExtenderTest, Brp_with_bisimulation) {
bisimType = storm::storage::BisimulationType::Weak;
}
dtmc = storm::api::performBisimulationMinimization<storm::RationalFunction>(model, formulas, bisimType)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
model = storm::api::performBisimulationMinimization<storm::RationalFunction>(model, formulas, bisimType)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
ASSERT_EQ(dtmc->getNumberOfStates(), 99ull);
ASSERT_EQ(dtmc->getNumberOfTransitions(), 195ull);
ASSERT_EQ(model->getNumberOfStates(), 99);
ASSERT_EQ(model->getNumberOfTransitions(), 195);
auto *extender = new storm::analysis::OrderExtender<storm::RationalFunction>(dtmc);
auto criticalTuple = extender->toOrder(formulas);
EXPECT_EQ(dtmc->getNumberOfStates(), std::get<1>(criticalTuple));
EXPECT_EQ(dtmc->getNumberOfStates(), std::get<2>(criticalTuple));
auto vars = storm::models::sparse::getProbabilityParameters(*model);
auto region = storm::api::parseRegion<storm::RationalFunction>("0.00001 <= pK <= 0.999999, 0.00001 <= pL <= 0.999999", vars);
auto extender = storm::analysis::OrderExtender<storm::RationalFunction, double>(model, formulas[0]);
auto monRes = new storm::analysis::MonotonicityResult<typename storm::analysis::OrderExtender<storm::RationalFunction, double>::VariableType>;
auto criticalTuple = extender.toOrder(region, make_shared<storm::analysis::MonotonicityResult<typename storm::analysis::OrderExtender<storm::RationalFunction, double>::VariableType>>(*monRes));
EXPECT_EQ(model->getNumberOfStates(), std::get<1>(criticalTuple));
EXPECT_EQ(model->getNumberOfStates(), std::get<2>(criticalTuple));
auto order = std::get<0>(criticalTuple);
for (uint_fast64_t i = 0; i < dtmc->getNumberOfStates(); ++i) {
EXPECT_TRUE((*order->getAddedStates())[i]);
for (uint_fast64_t i = 0; i < model->getNumberOfStates(); ++i) {
EXPECT_TRUE((order->contains(i)));
}
// Check on some nodes
@ -65,7 +63,7 @@ TEST(OrderExtenderTest, Brp_with_bisimulation) {
EXPECT_EQ(storm::analysis::Order::NodeComparison::UNKNOWN, order->compare(7,13));
}
TEST(OrderExtenderTest, Brp_without_bisimulation) {
TEST(OrderExtenderTest, Brp_without_bisimulation_on_model) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2.pm";
std::string formulaAsString = "P=? [F s=4 & i=N ]";
std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
@ -75,19 +73,315 @@ TEST(OrderExtenderTest, Brp_without_bisimulation) {
program = storm::utility::prism::preprocess(program, constantsAsString);
std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*dtmc);
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
model = simplifier.getSimplifiedModel();
dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
ASSERT_EQ(dtmc->getNumberOfStates(), 193ull);
ASSERT_EQ(dtmc->getNumberOfTransitions(), 383ull);
ASSERT_EQ(model->getNumberOfStates(), 193);
ASSERT_EQ(model->getNumberOfTransitions(), 383);
auto vars = storm::models::sparse::getProbabilityParameters(*model);
auto region = storm::api::parseRegion<storm::RationalFunction>("0.00001 <= pK <= 0.999999, 0.00001 <= pL <= 0.999999", vars);
auto *extender = new storm::analysis::OrderExtender<storm::RationalFunction>(dtmc);
auto criticalTuple = extender->toOrder(formulas);
auto extender = storm::analysis::OrderExtender<storm::RationalFunction, double>(model, formulas[0]);
auto monRes = new storm::analysis::MonotonicityResult<typename storm::analysis::OrderExtender<storm::RationalFunction, double>::VariableType>;
auto criticalTuple = extender.toOrder(region, make_shared<storm::analysis::MonotonicityResult<typename storm::analysis::OrderExtender<storm::RationalFunction, double>::VariableType>>(*monRes));
EXPECT_EQ(183ul, std::get<1>(criticalTuple));
EXPECT_EQ(186ul, std::get<2>(criticalTuple));
}
TEST(OrderExtenderTest, Brp_with_bisimulation_on_matrix) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2.pm";
std::string formulaAsString = "P=? [F s=4 & i=N ]";
std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
// Program and formula
storm::prism::Program program = storm::api::parseProgram(programFile);
program = storm::utility::prism::preprocess(program, constantsAsString);
std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
model = simplifier.getSimplifiedModel();
// Apply bisimulation
storm::storage::BisimulationType bisimType = storm::storage::BisimulationType::Strong;
if (storm::settings::getModule<storm::settings::modules::BisimulationSettings>().isWeakBisimulationSet()) {
bisimType = storm::storage::BisimulationType::Weak;
}
model = storm::api::performBisimulationMinimization<storm::RationalFunction>(model, formulas, bisimType)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
ASSERT_EQ(model->getNumberOfStates(), 99);
ASSERT_EQ(model->getNumberOfTransitions(), 195);
auto vars = storm::models::sparse::getProbabilityParameters(*model);
auto region = storm::api::parseRegion<storm::RationalFunction>("0.00001 <= pK <= 0.999999, 0.00001 <= pL <= 0.999999", vars);
storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Model<storm::RationalFunction>> propositionalChecker(*model);
storm::storage::BitVector phiStates;
storm::storage::BitVector psiStates;
phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true);
storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula();
psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector();
// Get the maybeStates
std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates);
storm::storage::BitVector topStates = statesWithProbability01.second;
storm::storage::BitVector bottomStates = statesWithProbability01.first;
auto extender = storm::analysis::OrderExtender<storm::RationalFunction, double>(&topStates, &bottomStates, model->getTransitionMatrix());
auto res = extender.extendOrder(nullptr, region);
auto order = std::get<0>(res);
EXPECT_EQ(order->getNumberOfAddedStates(), model->getNumberOfStates());
EXPECT_TRUE(order->getDoneBuilding());
// Check on some nodes
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,0));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,5));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(5,0));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(94,5));
EXPECT_EQ(storm::analysis::Order::NodeComparison::UNKNOWN, order->compare(7,13));
}
TEST(OrderExtenderTest, Brp_without_bisimulation_on_matrix) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2.pm";
std::string formulaAsString = "P=? [F s=4 & i=N ]";
std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
// Program and formula
storm::prism::Program program = storm::api::parseProgram(programFile);
program = storm::utility::prism::preprocess(program, constantsAsString);
std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
model = simplifier.getSimplifiedModel();
ASSERT_EQ(model->getNumberOfStates(), 193);
ASSERT_EQ(model->getNumberOfTransitions(), 383);
auto vars = storm::models::sparse::getProbabilityParameters(*model);
auto region = storm::api::parseRegion<storm::RationalFunction>("0.00001 <= pK <= 0.999999, 0.00001 <= pL <= 0.999999", vars);
storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Model<storm::RationalFunction>> propositionalChecker(*model);
storm::storage::BitVector phiStates;
storm::storage::BitVector psiStates;
phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true);
storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula();
psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector();
// Get the maybeStates
std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates);
storm::storage::BitVector topStates = statesWithProbability01.second;
storm::storage::BitVector bottomStates = statesWithProbability01.first;
auto extender = storm::analysis::OrderExtender<storm::RationalFunction, double>(&topStates, &bottomStates, model->getTransitionMatrix());
auto res = extender.extendOrder(nullptr, region);
auto order = std::get<0>(res);
EXPECT_FALSE(order->getDoneBuilding());
}
TEST(OrderExtenderTest, simple1_on_model) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/simple1.pm";
std::string formulaAsString = "P=? [F s=3 ]";
std::string constantsAsString = "";
// model
storm::prism::Program program = storm::api::parseProgram(programFile);
program = storm::utility::prism::preprocess(program, constantsAsString);
std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
model = simplifier.getSimplifiedModel();
// Create the region
auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
auto region=storm::api::parseRegion<storm::RationalFunction>("0.51<=p<=0.9", modelParameters);
auto extender = storm::analysis::OrderExtender<storm::RationalFunction, double>(model, formulas[0]);
auto order = std::get<0>(extender.toOrder(region));
EXPECT_EQ(order->getNumberOfAddedStates(), 5);
EXPECT_TRUE(order->getDoneBuilding());
// Check on all states
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,0));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,1));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,2));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,4));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,0));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,2));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,4));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(0,2));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(0,4));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(2,4));
}
TEST(OrderExtenderTest, simple1_on_matrix) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/simple1.pm";
std::string formulaAsString = "P=? [F s=3 ]";
std::string constantsAsString = "";
// model
storm::prism::Program program = storm::api::parseProgram(programFile);
program = storm::utility::prism::preprocess(program, constantsAsString);
std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
model = simplifier.getSimplifiedModel();
// Create the region
auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
auto region=storm::api::parseRegion<storm::RationalFunction>("0.51 <= p <= 0.9", modelParameters);
// For order extender
storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Model<storm::RationalFunction>> propositionalChecker(*model);
storm::storage::BitVector phiStates;
storm::storage::BitVector psiStates;
phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true);
storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula();
psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector();
// Get the maybeStates
std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates);
storm::storage::BitVector topStates = statesWithProbability01.second;
storm::storage::BitVector bottomStates = statesWithProbability01.first;
// OrderExtender
auto extender = storm::analysis::OrderExtender<storm::RationalFunction, double>(&topStates, &bottomStates, model->getTransitionMatrix());
auto res = extender.extendOrder(nullptr, region);
auto order = std::get<0>(res);
EXPECT_EQ(order->getNumberOfAddedStates(), model->getNumberOfStates());
EXPECT_TRUE(order->getDoneBuilding());
// Check on all states, as this one automatically handles assumptions (if there is one valid) all are ABOVE
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,0));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,1));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,2));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,4));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,0));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,2));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,4));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(0,2));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(0,4));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(2,4));
}
TEST(OrderExtenderTest, casestudy1_on_model) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/simple1.pm";
std::string formulaAsString = "P=? [F s=3 ]";
std::string constantsAsString = "";
// model
storm::prism::Program program = storm::api::parseProgram(programFile);
program = storm::utility::prism::preprocess(program, constantsAsString);
std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
model = simplifier.getSimplifiedModel();
// Create the region
auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
auto region=storm::api::parseRegion<storm::RationalFunction>("0.51<=p<=0.9", modelParameters);
auto extender = storm::analysis::OrderExtender<storm::RationalFunction, double>(model, formulas[0]);
auto order = std::get<0>(extender.toOrder(region));
EXPECT_EQ(order->getNumberOfAddedStates(), 5);
EXPECT_TRUE(order->getDoneBuilding());
// Check on all states
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,0));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,1));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,2));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,4));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,0));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,2));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,4));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(0,2));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(0,4));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(2,4));
}
TEST(OrderExtenderTest, casestudy1_on_matrix) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy1.pm";
std::string formulaAsString = "P=? [F s=3 ]";
std::string constantsAsString = "";
// model
storm::prism::Program program = storm::api::parseProgram(programFile);
program = storm::utility::prism::preprocess(program, constantsAsString);
std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
model = simplifier.getSimplifiedModel();
// Create the region
auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
auto region=storm::api::parseRegion<storm::RationalFunction>("0.51 <= p <= 0.9", modelParameters);
// For order extender
storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Model<storm::RationalFunction>> propositionalChecker(*model);
storm::storage::BitVector phiStates;
storm::storage::BitVector psiStates;
phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true);
storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula();
psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector();
// Get the maybeStates
std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates);
storm::storage::BitVector topStates = statesWithProbability01.second;
storm::storage::BitVector bottomStates = statesWithProbability01.first;
// OrderExtender
auto extender = storm::analysis::OrderExtender<storm::RationalFunction, double>(&topStates, &bottomStates, model->getTransitionMatrix());
auto res = extender.extendOrder(nullptr, region);
auto order = std::get<0>(res);
EXPECT_EQ(order->getNumberOfAddedStates(), model->getNumberOfStates());
EXPECT_TRUE(order->getDoneBuilding());
// Check on all states
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,0));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,1));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,2));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,4));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,0));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,2));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,4));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(0,2));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(0,4));
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(2,4));
}
TEST(OrderExtenderTest, casestudy2_on_matrix) {
std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy2.pm";
std::string formulaAsString = "P=? [F s=4 ]";
std::string constantsAsString = "";
// model
storm::prism::Program program = storm::api::parseProgram(programFile);
program = storm::utility::prism::preprocess(program, constantsAsString);
std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
model = simplifier.getSimplifiedModel();
// Create the region
auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
auto region=storm::api::parseRegion<storm::RationalFunction>("0.1 <= p <= 0.2", modelParameters);
// For order extender
storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Model<storm::RationalFunction>> propositionalChecker(*model);
storm::storage::BitVector phiStates;
storm::storage::BitVector psiStates;
phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true);
storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula();
psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector();
// Get the maybeStates
std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates);
storm::storage::BitVector topStates = statesWithProbability01.second;
storm::storage::BitVector bottomStates = statesWithProbability01.first;
// OrderExtender
auto extender = storm::analysis::OrderExtender<storm::RationalFunction, double>(&topStates, &bottomStates, model->getTransitionMatrix());
auto res = extender.extendOrder(nullptr, region);
EXPECT_TRUE(std::get<0>(res)->getDoneBuilding());
}

97
src/test/storm-pars/analysis/OrderTest.cpp

@ -1,7 +1,7 @@
#include <storm/storage/StronglyConnectedComponentDecomposition.h>
#include "test/storm_gtest.h"
#include "storm-config.h"
#include "test/storm_gtest.h"
#include "storm-pars/analysis/Order.h"
#include "storm-pars/api/analysis.h"
#include "storm/storage/BitVector.h"
TEST(OrderTest, Simple) {
@ -10,10 +10,15 @@ TEST(OrderTest, Simple) {
above.set(0);
auto below = storm::storage::BitVector(numberOfStates);
below.set(1);
auto initialMiddle = storm::storage::BitVector(numberOfStates);
std::vector<uint_fast64_t> statesSorted;
auto order = storm::analysis::Order(&above, &below, &initialMiddle, numberOfStates, &statesSorted);
storm::storage::SparseMatrixBuilder<storm::RationalFunction> matrixBuilder(2,2,2);
matrixBuilder.addNextValue(0,0,storm::RationalFunction(1));
matrixBuilder.addNextValue(1,1,storm::RationalFunction(1));
storm::storage::StronglyConnectedComponentDecompositionOptions options;
options.forceTopologicalSort();
auto matrix= matrixBuilder.build();
auto decomposition = storm::storage::StronglyConnectedComponentDecomposition<storm::RationalFunction>(matrix, options);
auto statesSorted = storm::utility::graph::getTopologicalSort(matrix);
auto order = storm::analysis::Order(&above, &below, numberOfStates, decomposition, statesSorted);
EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order.compare(0,1));
EXPECT_EQ(storm::analysis::Order::NodeComparison::BELOW, order.compare(1,0));
EXPECT_EQ(nullptr, order.getNode(2));
@ -77,10 +82,15 @@ TEST(OrderTest, copy_order) {
above.set(0);
auto below = storm::storage::BitVector(numberOfStates);
below.set(1);
auto initialMiddle = storm::storage::BitVector(numberOfStates);
std::vector<uint_fast64_t> statesSorted;
auto order = storm::analysis::Order(&above, &below, &initialMiddle, numberOfStates, &statesSorted);
storm::storage::SparseMatrixBuilder<storm::RationalFunction> matrixBuilder(2,2,2);
matrixBuilder.addNextValue(0,0,storm::RationalFunction(1));
matrixBuilder.addNextValue(1,1,storm::RationalFunction(1));
storm::storage::StronglyConnectedComponentDecompositionOptions options;
options.forceTopologicalSort();
auto matrix= matrixBuilder.build();
auto decomposition = storm::storage::StronglyConnectedComponentDecomposition<storm::RationalFunction>(matrix, options);
auto statesSorted = storm::utility::graph::getTopologicalSort(matrix);
auto order = storm::analysis::Order(&above, &below, numberOfStates, decomposition, statesSorted);
order.add(2);
order.add(3);
order.addToNode(4, order.getNode(2));
@ -143,10 +153,15 @@ TEST(OrderTest, merge_nodes) {
above.set(0);
auto below = storm::storage::BitVector(numberOfStates);
below.set(1);
auto initialMiddle = storm::storage::BitVector(numberOfStates);
std::vector<uint_fast64_t> statesSorted;
auto order = storm::analysis::Order(&above, &below, &initialMiddle, numberOfStates, &statesSorted);
storm::storage::SparseMatrixBuilder<storm::RationalFunction> matrixBuilder(2,2,2);
matrixBuilder.addNextValue(0,0,storm::RationalFunction(1));
matrixBuilder.addNextValue(1,1,storm::RationalFunction(1));
storm::storage::StronglyConnectedComponentDecompositionOptions options;
options.forceTopologicalSort();
auto matrix= matrixBuilder.build();
auto decomposition = storm::storage::StronglyConnectedComponentDecomposition<storm::RationalFunction>(matrix, options);
auto statesSorted = storm::utility::graph::getTopologicalSort(matrix);
auto order = storm::analysis::Order(&above, &below, numberOfStates, decomposition, statesSorted);
order.add(2);
order.add(3);
order.addToNode(4, order.getNode(2));
@ -171,3 +186,57 @@ TEST(OrderTest, merge_nodes) {
EXPECT_EQ(storm::analysis::Order::NodeComparison::BELOW, order.compare(1,4));
EXPECT_EQ(storm::analysis::Order::NodeComparison::BELOW, order.compare(1,5));
}
TEST(OrderTest, sort_states) {
auto numberOfStates = 7;
auto above = storm::storage::BitVector(numberOfStates);
above.set(0);
auto below = storm::storage::BitVector(numberOfStates);
below.set(1);
storm::storage::SparseMatrixBuilder<storm::RationalFunction> matrixBuilder(2,2,2);
matrixBuilder.addNextValue(0,0,storm::RationalFunction(1));
matrixBuilder.addNextValue(1,1,storm::RationalFunction(1));
storm::storage::StronglyConnectedComponentDecompositionOptions options;
options.forceTopologicalSort();
auto matrix= matrixBuilder.build();
auto decomposition = storm::storage::StronglyConnectedComponentDecomposition<storm::RationalFunction>(matrix, options);
auto statesSorted = storm::utility::graph::getTopologicalSort(matrix);
auto order = storm::analysis::Order(&above, &below, numberOfStates, decomposition, statesSorted);
order.add(2);
order.add(3);
order.addToNode(4, order.getNode(2));
order.addBetween(5, order.getNode(0), order.getNode(3));
order.addBetween(6, order.getNode(5), order.getNode(3));
std::vector<uint_fast64_t> statesToSort = std::vector<uint_fast64_t> {0,1,5,6};
auto sortedStates = order.sortStates(&statesToSort);
EXPECT_EQ(sortedStates.size(), 4);
auto itr = sortedStates.begin();
EXPECT_EQ(*itr, 0);
EXPECT_EQ(*(++itr), 5);
EXPECT_EQ(*(++itr), 6);
EXPECT_EQ(*(++itr), 1);
statesToSort = std::vector<uint_fast64_t> {0,1,5,6,2};
sortedStates = order.sortStates(&statesToSort);
EXPECT_EQ(sortedStates.size(), 5);
itr = sortedStates.begin();
EXPECT_EQ(*itr, 0);
EXPECT_EQ(*(++itr), 5);
EXPECT_EQ(*(++itr), 6);
EXPECT_EQ(*(++itr), 1);
EXPECT_EQ(*(++itr), 7);
statesToSort = std::vector<uint_fast64_t> {0,2,1,5,6};
sortedStates = order.sortStates(&statesToSort);
EXPECT_EQ(sortedStates.size(), 5);
itr = sortedStates.begin();
EXPECT_EQ(*itr, 0);
EXPECT_EQ(*(++itr), 2);
EXPECT_EQ(*(++itr), 1);
EXPECT_EQ(*(++itr), 7);
EXPECT_EQ(*(++itr), 7);
}

732
src/test/storm-pars/modelchecker/SparseDtmcParameterLiftingTest.cpp
File diff suppressed because it is too large
View File

|||||||
100:0
Loading…
Cancel
Save