77 changed files with 7871 additions and 3098 deletions
-
14resources/3rdparty/include_cudd.cmake
-
148resources/examples/testfiles/pdtmc/brp16_2_mon_incr.pm
-
34resources/examples/testfiles/pdtmc/casestudy1.pm
-
39resources/examples/testfiles/pdtmc/casestudy2.pm
-
33resources/examples/testfiles/pdtmc/casestudy3.pm
-
35resources/examples/testfiles/pdtmc/parametric_die_2.pm
-
16resources/examples/testfiles/pdtmc/simple1.pm
-
18resources/examples/testfiles/pdtmc/simple3.pm
-
17resources/examples/testfiles/pdtmc/simple4.pm
-
3resources/examples/testfiles/pdtmc/simple5.pm
-
26resources/examples/testfiles/pdtmc/zeroconf4.pm
-
421src/storm-pars-cli/storm-pars.cpp
-
389src/storm-pars/analysis/AssumptionChecker.cpp
-
58src/storm-pars/analysis/AssumptionChecker.h
-
116src/storm-pars/analysis/AssumptionMaker.cpp
-
56src/storm-pars/analysis/AssumptionMaker.h
-
175src/storm-pars/analysis/LocalMonotonicityResult.cpp
-
113src/storm-pars/analysis/LocalMonotonicityResult.h
-
757src/storm-pars/analysis/MonotonicityChecker.cpp
-
148src/storm-pars/analysis/MonotonicityChecker.h
-
325src/storm-pars/analysis/MonotonicityHelper.cpp
-
171src/storm-pars/analysis/MonotonicityHelper.h
-
231src/storm-pars/analysis/MonotonicityResult.cpp
-
130src/storm-pars/analysis/MonotonicityResult.h
-
912src/storm-pars/analysis/Order.cpp
-
324src/storm-pars/analysis/Order.h
-
1104src/storm-pars/analysis/OrderExtender.cpp
-
129src/storm-pars/analysis/OrderExtender.h
-
10src/storm-pars/api/analysis.h
-
113src/storm-pars/api/region.h
-
3src/storm-pars/api/storm-pars.h
-
263src/storm-pars/modelchecker/region/RegionModelChecker.cpp
-
46src/storm-pars/modelchecker/region/RegionModelChecker.h
-
347src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.cpp
-
51src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.h
-
10src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.cpp
-
2src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.h
-
482src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.cpp
-
35src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.h
-
8src/storm-pars/modelchecker/region/ValidatingSparseDtmcParameterLiftingModelChecker.cpp
-
2src/storm-pars/modelchecker/region/ValidatingSparseDtmcParameterLiftingModelChecker.h
-
10src/storm-pars/modelchecker/region/ValidatingSparseMdpParameterLiftingModelChecker.cpp
-
2src/storm-pars/modelchecker/region/ValidatingSparseMdpParameterLiftingModelChecker.h
-
4src/storm-pars/modelchecker/region/ValidatingSparseParameterLiftingModelChecker.cpp
-
2src/storm-pars/modelchecker/region/ValidatingSparseParameterLiftingModelChecker.h
-
73src/storm-pars/parser/MonotonicityParser.cpp
-
16src/storm-pars/parser/MonotonicityParser.h
-
34src/storm-pars/parser/ParameterRegionParser.cpp
-
7src/storm-pars/parser/ParameterRegionParser.h
-
76src/storm-pars/settings/modules/MonotonicitySettings.cpp
-
45src/storm-pars/settings/modules/MonotonicitySettings.h
-
11src/storm-pars/settings/modules/ParametricSettings.cpp
-
10src/storm-pars/settings/modules/ParametricSettings.h
-
57src/storm-pars/settings/modules/RegionSettings.cpp
-
22src/storm-pars/settings/modules/RegionSettings.h
-
145src/storm-pars/storage/ParameterRegion.cpp
-
25src/storm-pars/storage/ParameterRegion.h
-
106src/storm-pars/transformer/ParameterLifter.cpp
-
58src/storm-pars/transformer/ParameterLifter.h
-
9src/storm/settings/modules/GeneralSettings.cpp
-
6src/storm/settings/modules/GeneralSettings.h
-
14src/storm/settings/modules/IOSettings.cpp
-
15src/storm/settings/modules/IOSettings.h
-
61src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp
-
17src/storm/solver/MinMaxLinearEquationSolver.cpp
-
9src/storm/solver/MinMaxLinearEquationSolver.h
-
128src/storm/solver/TopologicalMinMaxLinearEquationSolver.cpp
-
40src/storm/utility/graph.cpp
-
4src/storm/utility/graph.h
-
2src/test/storm-pars/CMakeLists.txt
-
284src/test/storm-pars/analysis/AssumptionCheckerTest.cpp
-
341src/test/storm-pars/analysis/AssumptionMakerTest.cpp
-
375src/test/storm-pars/analysis/MonotonicityCheckerTest.cpp
-
454src/test/storm-pars/analysis/MonotonicityHelperTest.cpp
-
364src/test/storm-pars/analysis/OrderExtenderTest.cpp
-
97src/test/storm-pars/analysis/OrderTest.cpp
-
732src/test/storm-pars/modelchecker/SparseDtmcParameterLiftingTest.cpp
@ -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 |
|||
|
|||
|
@ -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" ]; |
|||
//} |
|||
|
@ -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" ]; |
|||
//} |
@ -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" ]; |
|||
//} |
@ -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; |
@ -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 |
|||
|
@ -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 |
|||
|
@ -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 |
@ -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>; |
|||
} |
|||
} |
@ -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 |
@ -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>; |
|||
} |
|||
} |
@ -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>; |
|||
} |
|||
} |
@ -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 |
@ -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>; |
|||
} |
|||
} |
@ -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
File diff suppressed because it is too large
View File
1104
src/storm-pars/analysis/OrderExtender.cpp
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -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" |
|||
|
@ -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" |
@ -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>; |
|||
} |
|||
} |
@ -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); |
|||
}; |
|||
} |
|||
} |
|||
|
|||
|
@ -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); |
|||
} |
|||
} |
732
src/test/storm-pars/modelchecker/SparseDtmcParameterLiftingTest.cpp
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Reference in new issue
xxxxxxxxxx