18 changed files with 621 additions and 1288 deletions
-
160examples/multi-objective/mdp/power/power-timed.nm
-
11examples/multi-objective/mdp/power/power-timed.pctl
-
169examples/multi-objective/mdp/zeroconf_time/zeroconf_host_multi2_time.nm
-
174examples/multi-objective/mdp/zeroconf_time/zeroconf_host_multi4_time.nm
-
13examples/multi-objective/mdp/zeroconf_time/zeroconf_host_multi_time.pctl
-
18src/modelchecker/multiobjective/SparseMdpMultiObjectiveModelChecker.cpp
-
71src/modelchecker/multiobjective/helper/SparseMdpMultiObjectivePreprocessingHelper.h
-
327src/modelchecker/multiobjective/helper/SparseMultiObjectiveHelper.cpp
-
67src/modelchecker/multiobjective/helper/SparseMultiObjectiveHelper.h
-
56src/modelchecker/multiobjective/helper/SparseMultiObjectiveHelperReturnType.h
-
490src/modelchecker/multiobjective/helper/SparseMultiObjectiveModelCheckerHelper.cpp
-
80src/modelchecker/multiobjective/helper/SparseMultiObjectiveModelCheckerHelper.h
-
154src/modelchecker/multiobjective/helper/SparseMultiObjectivePreprocessor.cpp
-
67src/modelchecker/multiobjective/helper/SparseMultiObjectivePreprocessor.h
-
26src/modelchecker/multiobjective/helper/SparseMultiObjectivePreprocessorReturnType.h
-
2src/modelchecker/multiobjective/helper/SparseMultiObjectiveWeightVectorChecker.cpp
-
12src/modelchecker/multiobjective/helper/SparseMultiObjectiveWeightVectorChecker.h
-
12src/utility/vector.h
@ -1,160 +0,0 @@ |
|||
// power manager example |
|||
mdp |
|||
|
|||
const int QMAX; // max queue size |
|||
|
|||
// to model the pm making a choice and then a move being made we need |
|||
// two clock ticks for each transition |
|||
// first the pm decides tick1 and then the system moves tick2 |
|||
|
|||
module timer |
|||
|
|||
c : [0..1]; |
|||
|
|||
[tick1] c=0 -> (c'=1); |
|||
[tick2] c=1 -> (c'=0); |
|||
|
|||
endmodule |
|||
|
|||
//------------------------------------------------------------------------- |
|||
|
|||
// POWER MANAGER |
|||
module PM |
|||
|
|||
pm : [0..4] init 4; |
|||
// 0 - go to active |
|||
// 1 - go to idle |
|||
// 2 - go to idlelp |
|||
// 3 - go to stby |
|||
// 4 - go to sleep |
|||
|
|||
[tick1] true -> (pm'=0); |
|||
[tick1] true -> (pm'=1); |
|||
[tick1] true -> (pm'=2); |
|||
[tick1] true -> (pm'=3); |
|||
[tick1] true -> (pm'=4); |
|||
|
|||
endmodule |
|||
|
|||
|
|||
//------------------------------------------------------------------------- |
|||
|
|||
// SERVICE REQUESTER |
|||
module SR |
|||
|
|||
sr : [0..1] init 0; |
|||
// 0 idle |
|||
// 1 1req |
|||
|
|||
[tick2] sr=0 -> 0.898: (sr'=0) + 0.102: (sr'=1); |
|||
[tick2] sr=1 -> 0.454: (sr'=0) + 0.546: (sr'=1); |
|||
|
|||
endmodule |
|||
|
|||
//------------------------------------------------------------------------- |
|||
|
|||
// SERVICE PROVIDER |
|||
|
|||
module SP |
|||
|
|||
sp : [0..10] init 9; |
|||
// 0 active |
|||
// 1 idle |
|||
// 2 active_idlelp |
|||
// 3 idlelp |
|||
// 4 idlelp_active |
|||
// 5 active_stby |
|||
// 6 stby |
|||
// 7 stby_active |
|||
// 8 active_sleep |
|||
// 9 sleep |
|||
// 10 sleep_active |
|||
|
|||
// states where PM has no control (transient states) |
|||
[tick2] sp=2 -> 0.75 : (sp'=2) + 0.25 : (sp'=3); // active_idlelp |
|||
[tick2] sp=4 -> 0.25 : (sp'=0) + 0.75 : (sp'=4); // idlelp_active |
|||
[tick2] sp=5 -> 0.995 : (sp'=5) + 0.005 : (sp'=6); // active_stby |
|||
[tick2] sp=7 -> 0.005 : (sp'=0) + 0.995 : (sp'=7); // stby_active |
|||
[tick2] sp=8 -> 0.9983 : (sp'=8) + 0.0017 : (sp'=9); // active_sleep |
|||
[tick2] sp=10 -> 0.0017 : (sp'=0) + 0.9983 : (sp'=10); // sleep_active |
|||
|
|||
// states where PM has control |
|||
// goto_active |
|||
[tick2] sp=0 & pm=0 -> (sp'=0); // active |
|||
[tick2] sp=1 & pm=0 -> (sp'=0); // idle |
|||
[tick2] sp=3 & pm=0 -> (sp'=4); // idlelp |
|||
[tick2] sp=6 & pm=0 -> (sp'=7); // stby |
|||
[tick2] sp=9 & pm=0 -> (sp'=10); // sleep |
|||
// goto_idle |
|||
[tick2] sp=0 & pm=1 -> (sp'=1); // active |
|||
[tick2] sp=1 & pm=1 -> (sp'=1); // idle |
|||
[tick2] sp=3 & pm=1 -> (sp'=3); // idlelp |
|||
[tick2] sp=6 & pm=1 -> (sp'=6); // stby |
|||
[tick2] sp=9 & pm=1 -> (sp'=9); // sleep |
|||
// goto_idlelp |
|||
[tick2] sp=0 & pm=2 -> (sp'=2); // active |
|||
[tick2] sp=1 & pm=2 -> (sp'=2); // idle |
|||
[tick2] sp=3 & pm=2 -> (sp'=3); // idlelp |
|||
[tick2] sp=6 & pm=2 -> (sp'=6); // stby |
|||
[tick2] sp=9 & pm=2 -> (sp'=9); // sleep |
|||
// goto_stby |
|||
[tick2] sp=0 & pm=3 -> (sp'=5); // active |
|||
[tick2] sp=1 & pm=3 -> (sp'=5); // idle |
|||
[tick2] sp=3 & pm=3 -> (sp'=5); // idlelp |
|||
[tick2] sp=6 & pm=3 -> (sp'=6); // stby |
|||
[tick2] sp=9 & pm=3 -> (sp'=9); // sleep |
|||
// goto_sleep |
|||
[tick2] sp=0 & pm=4 -> (sp'=8); // active |
|||
[tick2] sp=1 & pm=4 -> (sp'=8); // idle |
|||
[tick2] sp=3 & pm=4 -> (sp'=8); // idlelp |
|||
[tick2] sp=6 & pm=4 -> (sp'=8); // stby |
|||
[tick2] sp=9 & pm=4 -> (sp'=9); // sleep |
|||
|
|||
endmodule |
|||
|
|||
|
|||
//------------------------------------------------------------------------- |
|||
|
|||
// SQ |
|||
module SQ |
|||
|
|||
q : [0..QMAX] init 0; |
|||
|
|||
// serve if busy |
|||
[tick2] sr=0 & sp=0 -> (q'=max(q-1,0)); |
|||
[tick2] sr=1 & sp=0 -> (q'=q); |
|||
|
|||
// otherwise do nothing |
|||
[tick2] sr=0 & sp>0 -> (q'=q); |
|||
[tick2] sr=1 & sp>0 -> (q'=min(q+1,QMAX)); |
|||
|
|||
endmodule |
|||
|
|||
//------------------------------------------------------------------------- |
|||
//rewards "time" |
|||
// [tick2] bat=1 : 1; |
|||
//endrewards |
|||
|
|||
rewards "power" |
|||
[tick2] sp=0 & c=1 : 2.5; |
|||
[tick2] sp=1 & c=1 : 1.5; |
|||
[tick2] sp=2 & c=1 : 2.5; |
|||
[tick2] sp=3 & c=1 : 0.8; |
|||
[tick2] sp=4 & c=1 : 2.5; |
|||
[tick2] sp=5 & c=1 : 2.5; |
|||
[tick2] sp=6 & c=1 : 0.3; |
|||
[tick2] sp=7 & c=1 : 2.5; |
|||
[tick2] sp=8 & c=1 : 2.5; |
|||
[tick2] sp=9 & c=1 : 0.1; |
|||
[tick2] sp=10 & c=1 : 2.5; |
|||
endrewards |
|||
|
|||
// is an instantaneous property but I suppose we can look at average size |
|||
// i.e. divide by the expected number of time steps |
|||
rewards "queue" |
|||
[tick2] c=1 : q; |
|||
endrewards |
|||
|
|||
rewards "lost" |
|||
[tick2] sr=1 & sp>0 & q=2 : 1; |
|||
endrewards |
@ -1,11 +0,0 @@ |
|||
// Average queue size |
|||
const double Q; |
|||
|
|||
// Time bound |
|||
const int k; |
|||
|
|||
// Minimum energy usage over k time-steps, such that average queue size remains below Q |
|||
"num_energy": multi(R{"power"}min=? [ C<=k ], R{"queue"}<=Q*k [ C<=k ]) |
|||
|
|||
// Pareto query: minimum energy usage vs minimum average queue size |
|||
"pareto": multi(R{"power"}min=? [ C<=k ], R{"queue"}min=? [ C<=k ]) |
@ -1,169 +0,0 @@ |
|||
// IPv4: PTA model with digitial clocks |
|||
// multi-objective model of the host |
|||
// gxn/dxp 28/09/09 |
|||
|
|||
mdp |
|||
|
|||
//------------------------------------------------------------- |
|||
// VARIABLES |
|||
const int N=20; // number of abstract hosts |
|||
const int K=2; // number of probes to send |
|||
|
|||
// PROBABILITIES |
|||
const double old = N/65024; // probability pick an ip address being used |
|||
//const double old = 1/2; // probability pick an ip address being used |
|||
const double new = (1-old); // probability pick a new ip address |
|||
|
|||
// TIMING CONSTANTS |
|||
const int CONSEC = 2; // time interval between sending consecutive probles |
|||
const int TRANSTIME = 1; // upper bound on transmission time delay |
|||
const int LONGWAIT = 60; // minimum time delay after a high number of address collisions |
|||
const int DEFEND = 10; |
|||
|
|||
const int TIME_MAX_X = 60; // max value of clock x |
|||
const int TIME_MAX_Y = 10; // max value of clock y |
|||
const int TIME_MAX_Z = 1; // max value of clock z |
|||
|
|||
// OTHER CONSTANTS |
|||
const int MAXCOLL = 10; // maximum number of collisions before long wait |
|||
|
|||
|
|||
//------------------------------------------------------------- |
|||
// CONCRETE HOST |
|||
module host0 |
|||
|
|||
x : [0..TIME_MAX_X]; // first clock of the host |
|||
y : [0..TIME_MAX_Y]; // second clock of the host |
|||
|
|||
coll : [0..MAXCOLL]; // number of address collisions |
|||
probes : [0..K]; // counter (number of probes sent) |
|||
mess : [0..1]; // need to send a message or not |
|||
defend : [0..1]; // defend (if =1, try to defend IP address) |
|||
|
|||
ip : [1..2]; // ip address (1 - in use & 2 - fresh) |
|||
|
|||
l : [0..4] init 1; // location |
|||
// 0 : RECONFIGURE |
|||
// 1 : RANDOM |
|||
// 2 : WAITSP |
|||
// 3 : WAITSG |
|||
// 4 : USE |
|||
|
|||
// RECONFIGURE |
|||
[reset] l=0 -> (l'=1); |
|||
|
|||
// RANDOM (choose IP address) |
|||
[rec0] (l=1) -> true; // get message (ignore since have no ip address) |
|||
[rec1] (l=1) -> true; // get message (ignore since have no ip address) |
|||
// small number of collisions (choose straight away) |
|||
[] l=1 & coll<MAXCOLL -> 1/3*old : (l'=2) & (ip'=1) & (x'=0) |
|||
+ 1/3*old : (l'=2) & (ip'=1) & (x'=1) |
|||
+ 1/3*old : (l'=2) & (ip'=1) & (x'=2) |
|||
+ 1/3*new : (l'=2) & (ip'=2) & (x'=0) |
|||
+ 1/3*new : (l'=2) & (ip'=2) & (x'=1) |
|||
+ 1/3*new : (l'=2) & (ip'=2) & (x'=2); |
|||
// large number of collisions: (wait for LONGWAIT) |
|||
[time] l=1 & coll=MAXCOLL & x<LONGWAIT -> (x'=min(x+1,TIME_MAX_X)); |
|||
[] l=1 & coll=MAXCOLL & x=LONGWAIT -> 1/3*old : (l'=2) & (ip'=1) & (x'=0) |
|||
+ 1/3*old : (l'=2) & (ip'=1) & (x'=1) |
|||
+ 1/3*old : (l'=2) & (ip'=1) & (x'=2) |
|||
+ 1/3*new : (l'=2) & (ip'=2) & (x'=0) |
|||
+ 1/3*new : (l'=2) & (ip'=2) & (x'=1) |
|||
+ 1/3*new : (l'=2) & (ip'=2) & (x'=2); |
|||
|
|||
// WAITSP |
|||
// let time pass |
|||
[time] l=2 & x<2 -> (x'=min(x+1,2)); |
|||
// send probe |
|||
[send1] l=2 & ip=1 & x=2 & probes<K -> (x'=0) & (probes'=probes+1); |
|||
[send2] l=2 & ip=2 & x=2 & probes<K -> (x'=0) & (probes'=probes+1); |
|||
// sent K probes and waited 2 seconds |
|||
[configured] l=2 & x=2 & probes=K -> (l'=3) & (probes'=0) & (coll'=0) & (x'=0); |
|||
// get message and ip does not match: ignore |
|||
[rec0] l=2 & ip!=0 -> (l'=l); |
|||
[rec1] l=2 & ip!=1 -> (l'=l); |
|||
// get a message with matching ip: reconfigure |
|||
[rec1] l=2 & ip=1 -> (l'=0) & (coll'=min(coll+1,MAXCOLL)) & (x'=0) & (probes'=0); |
|||
|
|||
// WAITSG (sends two gratuitious arp probes) |
|||
// time passage |
|||
[time] l=3 & mess=0 & defend=0 & x<CONSEC -> (x'=min(x+1,TIME_MAX_X)); |
|||
[time] l=3 & mess=0 & defend=1 & x<CONSEC -> (x'=min(x+1,TIME_MAX_X)) & (y'=min(y+1,DEFEND)); |
|||
|
|||
// receive message and same ip: defend |
|||
[rec1] l=3 & mess=0 & ip=1 & (defend=0 | y>=DEFEND) -> (defend'=1) & (mess'=1) & (y'=0); |
|||
// receive message and same ip: defer |
|||
[rec1] l=3 & mess=0 & ip=1 & (defend=0 | y<DEFEND) -> (l'=0) & (probes'=0) & (defend'=0) & (x'=0) & (y'=0); |
|||
// receive message and different ip |
|||
[rec0] l=3 & mess=0 & ip!=0 -> (l'=l); |
|||
[rec1] l=3 & mess=0 & ip!=1 -> (l'=l); |
|||
|
|||
|
|||
// send probe reply or message for defence |
|||
[send1] l=3 & ip=1 & mess=1 -> (mess'=0); |
|||
[send2] l=3 & ip=2 & mess=1 -> (mess'=0); |
|||
// send first gratuitous arp message |
|||
[send1] l=3 & ip=1 & mess=0 & x=CONSEC & probes<1 -> (x'=0) & (probes'=probes+1); |
|||
[send2] l=3 & ip=2 & mess=0 & x=CONSEC & probes<1 -> (x'=0) & (probes'=probes+1); |
|||
// send second gratuitous arp message (move to use) |
|||
[send1] l=3 & ip=1 & mess=0 & x=CONSEC & probes=1 -> (l'=4) & (x'=0) & (y'=0) & (probes'=0); |
|||
[send2] l=3 & ip=2 & mess=0 & x=CONSEC & probes=1 -> (l'=4) & (x'=0) & (y'=0) & (probes'=0); |
|||
|
|||
// USE (only interested in reaching this state so do not need to add anything here) |
|||
[] l=4 -> true; |
|||
|
|||
endmodule |
|||
|
|||
//------------------------------------------------------------- |
|||
// error automaton for the environment assumption |
|||
// do not get a reply when K probes are sent |
|||
const int M; // time between sending and receiving a message |
|||
|
|||
module env_error2 |
|||
|
|||
env : [0..1]; // 0 active and 1 done |
|||
k : [0..2]; // counts the number of messages sent |
|||
c1 : [0..M+1]; // time since first message |
|||
c2 : [0..M+1]; // time since second message |
|||
error : [0..1]; |
|||
|
|||
// message with new ip address arrives so done |
|||
[send2] error=0 & env=0 -> (env'=1); |
|||
// message with old ip address arrives so count |
|||
[send1] error=0 & env=0 -> (k'=min(k+1,K)); |
|||
// time passgae so update relevant clocks |
|||
[time] error=0 & env=0 & k=0 -> true; |
|||
[time] error=0 & env=0 & k=1 & min(c1,c2)<M -> (c1'=min(c1+1,M+1)); |
|||
[time] error=0 & env=0 & k=2 & min(c1,c2)<M -> (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)); |
|||
// all clocks reached their bound so an error |
|||
[time] error=0 & env=0 & min(c1,c2)=M -> (error'=1); |
|||
// send a reply (then done) |
|||
[rec1] error=0 & env=0 & k>0 & min(c1,c2)<=M -> (env'=1); |
|||
// finished so any action can be performed |
|||
[time] error=1 | env=1 -> true; |
|||
[send1] error=1 | env=1 -> true; |
|||
[send2] error=1 | env=1 -> true; |
|||
[rec1] error=1 | env=1 -> true; |
|||
|
|||
endmodule |
|||
|
|||
//------------------------------------------------------------- |
|||
// error automaton for the time bounded assumption |
|||
// host does not send configured signal within T seconds |
|||
const int T; |
|||
|
|||
module time_error |
|||
|
|||
time_error : [0..1]; |
|||
done : [0..1]; |
|||
t : [0..T]; |
|||
|
|||
[time] t<T-1 & done=0 & time_error=0 -> (t'=t+1); // time passes and bound not reached |
|||
[time] t=T-1 & done=0 & time_error=0 -> (time_error'=1); // bound reached so error |
|||
[configured] time_error=0 -> (done'=1); // configured within the time bound |
|||
|
|||
// when in error or done state can loop with either action |
|||
[configured] time_error=1 | done=1 -> true; |
|||
[time] time_error=1 | done=1 -> true; |
|||
|
|||
endmodule |
@ -1,174 +0,0 @@ |
|||
// IPv4: PTA model with digitial clocks |
|||
// multi-objective model of the host |
|||
// gxn/dxp 28/09/09 |
|||
|
|||
mdp |
|||
|
|||
//------------------------------------------------------------- |
|||
// VARIABLES |
|||
const int N=20; // number of abstract hosts |
|||
const int K=4; // number of probes to send |
|||
|
|||
// PROBABILITIES |
|||
const double old = N/65024; // probability pick an ip address being used |
|||
//const double old = 0.5; // probability pick an ip address being used |
|||
const double new = (1-old); // probability pick a new ip address |
|||
|
|||
// TIMING CONSTANTS |
|||
const int CONSEC = 2; // time interval between sending consecutive probles |
|||
const int TRANSTIME = 1; // upper bound on transmission time delay |
|||
const int LONGWAIT = 60; // minimum time delay after a high number of address collisions |
|||
const int DEFEND = 10; |
|||
|
|||
const int TIME_MAX_X = 60; // max value of clock x |
|||
const int TIME_MAX_Y = 10; // max value of clock y |
|||
const int TIME_MAX_Z = 1; // max value of clock z |
|||
|
|||
// OTHER CONSTANTS |
|||
const int MAXCOLL = 10; // maximum number of collisions before long wait |
|||
|
|||
|
|||
//------------------------------------------------------------- |
|||
// CONCRETE HOST |
|||
module host0 |
|||
|
|||
x : [0..TIME_MAX_X]; // first clock of the host |
|||
y : [0..TIME_MAX_Y]; // second clock of the host |
|||
|
|||
coll : [0..MAXCOLL]; // number of address collisions |
|||
probes : [0..K]; // counter (number of probes sent) |
|||
mess : [0..1]; // need to send a message or not |
|||
defend : [0..1]; // defend (if =1, try to defend IP address) |
|||
|
|||
ip : [1..2]; // ip address (1 - in use & 2 - fresh) |
|||
|
|||
l : [0..4] init 1; // location |
|||
// 0 : RECONFIGURE |
|||
// 1 : RANDOM |
|||
// 2 : WAITSP |
|||
// 3 : WAITSG |
|||
// 4 : USE |
|||
|
|||
// RECONFIGURE |
|||
[reset] l=0 -> (l'=1); |
|||
|
|||
// RANDOM (choose IP address) |
|||
[rec0] (l=1) -> true; // get message (ignore since have no ip address) |
|||
[rec1] (l=1) -> true; // get message (ignore since have no ip address) |
|||
// small number of collisions (choose straight away) |
|||
[] l=1 & coll<MAXCOLL -> 1/3*old : (l'=2) & (ip'=1) & (x'=0) |
|||
+ 1/3*old : (l'=2) & (ip'=1) & (x'=1) |
|||
+ 1/3*old : (l'=2) & (ip'=1) & (x'=2) |
|||
+ 1/3*new : (l'=2) & (ip'=2) & (x'=0) |
|||
+ 1/3*new : (l'=2) & (ip'=2) & (x'=1) |
|||
+ 1/3*new : (l'=2) & (ip'=2) & (x'=2); |
|||
// large number of collisions: (wait for LONGWAIT) |
|||
[time] l=1 & coll=MAXCOLL & x<LONGWAIT -> (x'=min(x+1,TIME_MAX_X)); |
|||
[] l=1 & coll=MAXCOLL & x=LONGWAIT -> 1/3*old : (l'=2) & (ip'=1) & (x'=0) |
|||
+ 1/3*old : (l'=2) & (ip'=1) & (x'=1) |
|||
+ 1/3*old : (l'=2) & (ip'=1) & (x'=2) |
|||
+ 1/3*new : (l'=2) & (ip'=2) & (x'=0) |
|||
+ 1/3*new : (l'=2) & (ip'=2) & (x'=1) |
|||
+ 1/3*new : (l'=2) & (ip'=2) & (x'=2); |
|||
|
|||
// WAITSP |
|||
// let time pass |
|||
[time] l=2 & x<2 -> (x'=min(x+1,2)); |
|||
// send probe |
|||
[send1] l=2 & ip=1 & x=2 & probes<K -> (x'=0) & (probes'=probes+1); |
|||
[send2] l=2 & ip=2 & x=2 & probes<K -> (x'=0) & (probes'=probes+1); |
|||
// sent K probes and waited 2 seconds |
|||
[configured] l=2 & x=2 & probes=K -> (l'=3) & (probes'=0) & (coll'=0) & (x'=0); |
|||
// get message and ip does not match: ignore |
|||
[rec0] l=2 & ip!=0 -> (l'=l); |
|||
[rec1] l=2 & ip!=1 -> (l'=l); |
|||
// get a message with matching ip: reconfigure |
|||
[rec1] l=2 & ip=1 -> (l'=0) & (coll'=min(coll+1,MAXCOLL)) & (x'=0) & (probes'=0); |
|||
|
|||
// WAITSG (sends two gratuitious arp probes) |
|||
// time passage |
|||
[time] l=3 & mess=0 & defend=0 & x<CONSEC -> (x'=min(x+1,TIME_MAX_X)); |
|||
[time] l=3 & mess=0 & defend=1 & x<CONSEC -> (x'=min(x+1,TIME_MAX_X)) & (y'=min(y+1,DEFEND)); |
|||
|
|||
// receive message and same ip: defend |
|||
[rec1] l=3 & mess=0 & ip=1 & (defend=0 | y>=DEFEND) -> (defend'=1) & (mess'=1) & (y'=0); |
|||
// receive message and same ip: defer |
|||
[rec1] l=3 & mess=0 & ip=1 & (defend=0 | y<DEFEND) -> (l'=0) & (probes'=0) & (defend'=0) & (x'=0) & (y'=0); |
|||
// receive message and different ip |
|||
[rec0] l=3 & mess=0 & ip!=0 -> (l'=l); |
|||
[rec1] l=3 & mess=0 & ip!=1 -> (l'=l); |
|||
|
|||
|
|||
// send probe reply or message for defence |
|||
[send1] l=3 & ip=1 & mess=1 -> (mess'=0); |
|||
[send2] l=3 & ip=2 & mess=1 -> (mess'=0); |
|||
// send first gratuitous arp message |
|||
[send1] l=3 & ip=1 & mess=0 & x=CONSEC & probes<1 -> (x'=0) & (probes'=probes+1); |
|||
[send2] l=3 & ip=2 & mess=0 & x=CONSEC & probes<1 -> (x'=0) & (probes'=probes+1); |
|||
// send second gratuitous arp message (move to use) |
|||
[send1] l=3 & ip=1 & mess=0 & x=CONSEC & probes=1 -> (l'=4) & (x'=0) & (y'=0) & (probes'=0); |
|||
[send2] l=3 & ip=2 & mess=0 & x=CONSEC & probes=1 -> (l'=4) & (x'=0) & (y'=0) & (probes'=0); |
|||
|
|||
// USE (only interested in reaching this state so do not need to add anything here) |
|||
[] l=4 -> true; |
|||
|
|||
endmodule |
|||
|
|||
//------------------------------------------------------------- |
|||
// error automaton for the environment assumption |
|||
// do not get a reply when K probes are sent |
|||
const int M; // time between sending and receiving a message |
|||
|
|||
module env_error4 |
|||
|
|||
env : [0..1]; // 0 active and 1 done |
|||
k : [0..4]; // counts the number of messages sent |
|||
c1 : [0..M+1]; // time since first message |
|||
c2 : [0..M+1]; // time since second message |
|||
c3 : [0..M+1]; // time since third message |
|||
c4 : [0..M+1]; // time since fourth message |
|||
error : [0..1]; |
|||
|
|||
// message with new ip address arrives so done |
|||
[send2] error=0 & env=0 -> (env'=1); |
|||
// message with old ip address arrives so count |
|||
[send1] error=0 & env=0 -> (k'=min(k+1,K)); |
|||
// time passgae so update relevant clocks |
|||
[time] error=0 & env=0 & k=0 -> true; |
|||
[time] error=0 & env=0 & k=1 & min(c1,c2,c3,c4)<M -> (c1'=min(c1+1,M+1)); |
|||
[time] error=0 & env=0 & k=2 & min(c1,c2,c3,c4)<M -> (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)); |
|||
[time] error=0 & env=0 & k=3 & min(c1,c2,c3,c4)<M -> (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)) & (c3'=min(c3+1,M+1)); |
|||
[time] error=0 & env=0 & k=4 & min(c1,c2,c3,c4)<M -> (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)) & (c3'=min(c3+1,M+1)) & (c4'=min(c4+1,M+1)); |
|||
// all clocks reached their bound so an error |
|||
[time] error=0 & env=0 & min(c1,c2,c3,c4)=M -> (error'=1); |
|||
// send a reply (then done) |
|||
[rec1] error=0 & env=0 & k>0 & min(c1,c2,c3,c4)<=M -> (env'=1); |
|||
// finished so any action can be performed |
|||
[time] error=1 | env=1 -> true; |
|||
[send1] error=1 | env=1 -> true; |
|||
[send2] error=1 | env=1 -> true; |
|||
[send2] error=1 | env=1 -> true; |
|||
[rec1] error=1 | env=1 -> true; |
|||
|
|||
endmodule |
|||
|
|||
//------------------------------------------------------------- |
|||
// error automaton for the time bounded assumption |
|||
// host does not send configured signal within T seconds |
|||
const int T; |
|||
|
|||
module time_error |
|||
|
|||
time_error : [0..1]; |
|||
done : [0..1]; |
|||
t : [0..T]; |
|||
|
|||
[time] t<T-1 & done=0 & time_error=0 -> (t'=t+1); // time passes and bound not reached |
|||
[time] t=T-1 & done=0 & time_error=0 -> (time_error'=1); // bound reached so error |
|||
[configured] time_error=0 -> (done'=1); // configured within the time bound |
|||
|
|||
// when in error or done state can loop with either action |
|||
[configured] time_error=1 | done=1 -> true; |
|||
[time] time_error=1 | done=1 -> true; |
|||
|
|||
endmodule |
@ -1,13 +0,0 @@ |
|||
// Max probability of component violating assumption property (checked separately) |
|||
const double p_fail = |
|||
K=2 ? 0.19 : |
|||
K=4 ? 0.006859000000000001 : |
|||
K=6 ? 2.476099000000001E-4 : |
|||
K=8 ? 8.938717390000006E-6 : |
|||
0; |
|||
|
|||
// Assume-guarantee check via multi-objective |
|||
"num_ag": multi(Pmax=? [ F time_error=1 ] , P>=1-p_fail [ G (error=0) ]) |
|||
|
|||
// Pareto query for assume-guarantee check |
|||
"pareto": multi(Pmax=? [ F time_error=1 ] , Pmax=? [ G (error=0) ]) |
@ -1,71 +0,0 @@ |
|||
#ifndef STORM_MODELCHECKER_MULTIOBJECTIVE_HELPER_SPARSEMDPMULTIOBJECTIVEPREPROCESSINGHELPER_H_ |
|||
#define STORM_MODELCHECKER_MULTIOBJECTIVE_HELPER_SPARSEMDPMULTIOBJECTIVEPREPROCESSINGHELPER_H_ |
|||
|
|||
#include "src/logic/Formulas.h" |
|||
#include "src/modelchecker/multiobjective/helper/SparseMultiObjectiveModelCheckerInformation.h" |
|||
|
|||
namespace storm { |
|||
namespace modelchecker { |
|||
|
|||
|
|||
namespace helper { |
|||
|
|||
/* |
|||
* Helper Class to invoke the necessary preprocessing for multi objective model checking |
|||
*/ |
|||
template <class SparseMdpModelType> |
|||
class SparseMdpMultiObjectivePreprocessingHelper { |
|||
public: |
|||
typedef typename SparseMdpModelType::ValueType ValueType; |
|||
typedef typename SparseMdpModelType::RewardModelType RewardModelType; |
|||
typedef SparseMultiObjectiveModelCheckerInformation<SparseMdpModelType> Information; |
|||
|
|||
/*! |
|||
* Preprocesses the given model w.r.t. the given formulas. |
|||
* @param originalModel The considered model |
|||
* @param originalFormula the considered formula. The subformulas should only contain one OperatorFormula at top level, i.e., the formula is simple. |
|||
*/ |
|||
static Information preprocess(storm::logic::MultiObjectiveFormula const& originalFormula, SparseMdpModelType const& originalModel); |
|||
|
|||
private: |
|||
|
|||
/*! |
|||
* Inserts the information regarding the given formula (i.e. objective) into info.objectives |
|||
* |
|||
* @param formula OperatorFormula representing the objective |
|||
* @param the information collected so far |
|||
*/ |
|||
static void addObjective(std::shared_ptr<storm::logic::Formula const> const& formula, Information& info); |
|||
|
|||
/*! |
|||
* Sets the timeBound for the given objective |
|||
*/ |
|||
static void setStepBoundOfObjective(typename Information::ObjectiveInformation& currentObjective); |
|||
|
|||
/*! |
|||
* Sets whether we should consider negated rewards |
|||
*/ |
|||
static void setWhetherNegatedRewardsAreConsidered(Information& info); |
|||
|
|||
/*! |
|||
* Apply the neccessary preprocessing for the given formula. |
|||
* @param formula the current (sub)formula |
|||
* @param info the information collected so far |
|||
* @param currentObjective the currently considered objective. The given formula should be a a (sub)formula of this objective |
|||
* @param optionalRewardModelName the reward model name that is considered for the formula (if available) |
|||
*/ |
|||
static void preprocessFormula(storm::logic::ProbabilityOperatorFormula const& formula, Information& info, typename Information::ObjectiveInformation& currentObjective); |
|||
static void preprocessFormula(storm::logic::RewardOperatorFormula const& formula, Information& info, typename Information::ObjectiveInformation& currentObjective); |
|||
static void preprocessFormula(storm::logic::UntilFormula const& formula, Information& info, typename Information::ObjectiveInformation& currentObjective); |
|||
static void preprocessFormula(storm::logic::BoundedUntilFormula const& formula, Information& info, typename Information::ObjectiveInformation& currentObjective); |
|||
static void preprocessFormula(storm::logic::GloballyFormula const& formula, Information& info, typename Information::ObjectiveInformation& currentObjective); |
|||
static void preprocessFormula(storm::logic::EventuallyFormula const& formula, Information& info, typename Information::ObjectiveInformation& currentObjective, boost::optional<std::string> const& optionalRewardModelName = boost::none); |
|||
static void preprocessFormula(storm::logic::CumulativeRewardFormula const& formula, Information& info, typename Information::ObjectiveInformation& currentObjective, boost::optional<std::string> const& optionalRewardModelName = boost::none); |
|||
|
|||
}; |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
#endif /* STORM_MODELCHECKER_MULTIOBJECTIVE_HELPER_SPARSEMDPMULTIOBJECTIVEPREPROCESSINGHELPER_H_ */ |
@ -0,0 +1,327 @@ |
|||
#include "src/modelchecker/multiobjective/helper/SparseMultiObjectiveHelper.h"
|
|||
|
|||
#include "src/adapters/CarlAdapter.h"
|
|||
#include "src/models/sparse/Mdp.h"
|
|||
#include "src/models/sparse/StandardRewardModel.h"
|
|||
#include "src/utility/constants.h"
|
|||
#include "src/utility/vector.h"
|
|||
#include "src/settings//SettingsManager.h"
|
|||
#include "src/settings/modules/MultiObjectiveSettings.h"
|
|||
|
|||
#include "src/exceptions/UnexpectedException.h"
|
|||
|
|||
namespace storm { |
|||
namespace modelchecker { |
|||
namespace helper { |
|||
|
|||
|
|||
template <class SparseModelType, typename RationalNumberType> |
|||
typename SparseMultiObjectiveHelper<SparseModelType, RationalNumberType>::ReturnType SparseMultiObjectiveHelper<SparseModelType, RationalNumberType>::check(PreprocessorData const& data) { |
|||
ReturnType result; |
|||
result.overApproximation = storm::storage::geometry::Polytope<RationalNumberType>::createUniversalPolytope(); |
|||
result.underApproximation = storm::storage::geometry::Polytope<RationalNumberType>::createEmptyPolytope(); |
|||
|
|||
uint_fast64_t numOfObjectivesWithoutThreshold = 0; |
|||
for(auto const& obj : data.objectives) { |
|||
if(!obj.threshold) { |
|||
++numOfObjectivesWithoutThreshold; |
|||
} |
|||
} |
|||
if(numOfObjectivesWithoutThreshold == 0) { |
|||
achievabilityQuery(data, result); |
|||
} else if (numOfObjectivesWithoutThreshold == 1) { |
|||
numericalQuery(data, result); |
|||
} else if (numOfObjectivesWithoutThreshold == data.objectives.size()) { |
|||
paretoQuery(data, result); |
|||
} else { |
|||
STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "The number of objecties without threshold is not valid. It should be either 0 (achievabilityQuery), 1 (numericalQuery), or " << data.objectives.size() << " (paretoQuery). Got " << numOfObjectivesWithoutThreshold << " instead."); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
template <class SparseModelType, typename RationalNumberType> |
|||
void SparseMultiObjectiveHelper<SparseModelType, RationalNumberType>::achievabilityQuery(PreprocessorData const& data, ReturnType& result) { |
|||
Point thresholds; |
|||
thresholds.reserve(data.objectives.size()); |
|||
storm::storage::BitVector strictThresholds(data.objectives.size()); |
|||
for(uint_fast64_t objIndex = 0; objIndex < data.objectives.size(); ++objIndex) { |
|||
thresholds.push_back(storm::utility::convertNumber<RationalNumberType>(*data.objectives[objIndex].threshold)); |
|||
strictThresholds.set(objIndex, data.objectives[objIndex].thresholdIsStrict); |
|||
} |
|||
|
|||
storm::storage::BitVector individualObjectivesToBeChecked(data.objectives.size(), true); |
|||
bool converged=false; |
|||
SparseMultiObjectiveWeightVectorChecker<SparseModelType> weightVectorChecker(data); |
|||
do { |
|||
WeightVector separatingVector = findSeparatingVector(thresholds, result.underApproximation, individualObjectivesToBeChecked); |
|||
refineResult(separatingVector, weightVectorChecker, result); |
|||
// Check for convergence
|
|||
if(!checkIfThresholdsAreSatisfied(overApproximation, thresholds, strictThresholds)){ |
|||
result.thresholdsAreAchievable = false; |
|||
converged=true; |
|||
} |
|||
if(checkIfThresholdsAreSatisfied(underApproximation, thresholds, strictThresholds)){ |
|||
result.thresholdsAreAchievable = true; |
|||
converged=true; |
|||
} |
|||
converged |= (storm::settings::getModule<storm::settings::modules::MultiObjectiveSettings>().isMaxIterationsSet() && result.iterations.size() >= storm::settings::getModule<storm::settings::modules::MultiObjectiveSettings>().getMaxIterations()); |
|||
} while(!converged); |
|||
} |
|||
|
|||
template <class SparseModelType, typename RationalNumberType> |
|||
void SparseMultiObjectiveHelper<SparseModelType, RationalNumberType>::numericalQuery(PreprocessorData const& data, ReturnType& result) { |
|||
Point thresholds; |
|||
thresholds.reserve(data.objectives.size()); |
|||
storm::storage::BitVector strictThresholds(data.objectives.size()); |
|||
std::vector<storm::storage::geometry::Halfspace<RationalNumberType>> thresholdConstraints; |
|||
thresholdConstraints.reserve(data.objectives.size()-1); |
|||
uint_fast64_t optimizingObjIndex = data.objectives.size(); // init with invalid value...
|
|||
for(uint_fast64_t objIndex = 0; objIndex < data.objectives.size(); ++objIndex) { |
|||
if(data.objectives[objIndex].threshold) { |
|||
thresholds.push_back(storm::utility::convertNumber<RationalNumberType>(*data.objectives[objIndex].threshold)); |
|||
WeightVector normalVector(data.objectives.size(), storm::utility::zero<RationalNumberType>()); |
|||
normalVector[objIndex] = -storm::utility::one<RationalNumberType>(); |
|||
thresholdConstraints.emplace_back(std::move(normalVector), -thresholds.back()); |
|||
strictThresholds.set(objIndex, data.objectives[objIndex].thresholdIsStrict); |
|||
} else { |
|||
optimizingObjIndex = objIndex; |
|||
thresholds.push_back(storm::utility::zero<RationalNumberType>()); |
|||
} |
|||
} |
|||
auto thresholdsAsPolytope = storm::storage::geometry::Polytope<RationalNumberType>::create(thresholdConstraints); |
|||
WeightVector directionOfOptimizingObjective(data.objectives.size(), storm::utility::zero<RationalNumberType>()); |
|||
directionOfOptimizingObjective[optimizingObjIndex] = storm::utility::one<RationalNumberType>(); |
|||
|
|||
// Try to find one valid solution
|
|||
storm::storage::BitVector individualObjectivesToBeChecked(data.objectives.size(), true); |
|||
individualObjectivesToBeChecked.set(optimizingObjIndex, false); |
|||
SparseMultiObjectiveWeightVectorChecker<SparseModelType> weightVectorChecker(data); |
|||
bool converged=false; |
|||
do { |
|||
WeightVector separatingVector = findSeparatingVector(thresholds, result.underApproximation, individualObjectivesToBeChecked); |
|||
refineResult(separatingVector, weightVectorChecker, result); |
|||
//Pick the threshold for the optimizing objective low enough so valid solutions are not excluded
|
|||
thresholds[optimizingObjIndex] = std::min(thresholds[optimizingObjIndex], iterations.back().point[optimizingObjIndex]); |
|||
if(!checkIfThresholdsAreSatisfied(overApproximation, thresholds, strictThresholds)){ |
|||
result.thresholdsAreAchievable = false; |
|||
converged=true; |
|||
} |
|||
if(checkIfThresholdsAreSatisfied(underApproximation, thresholds, strictThresholds)){ |
|||
result.thresholdsAreAchievable = true; |
|||
converged=true; |
|||
} |
|||
} while(!converged); |
|||
if(*result.thresholdsAreAchievable) { |
|||
STORM_LOG_DEBUG("Found a solution that satisfies the objective thresholds."); |
|||
individualObjectivesToBeChecked.clear(); |
|||
// Improve the found solution.
|
|||
// Note that we do not have to care whether a threshold is strict anymore, because the resulting optimum should be
|
|||
// the supremum over all strategies. Hence, one could combine a scheduler inducing the optimum value (but possibly violating strict
|
|||
// thresholds) and (with very low probability) a scheduler that satisfies all (possibly strict) thresholds.
|
|||
converged = false; |
|||
do { |
|||
std::pair<Point, bool> optimizationRes = underApproximation->intersection(thresholdsAsPolytope)->optimize(directionOfOptimizingObjective); |
|||
STORM_LOG_THROW(optimizationRes.second, storm::exceptions::UnexpectedException, "The underapproximation is either unbounded or empty."); |
|||
thresholds[optimizingObjIndex] = optimizationRes.first[optimizingObjIndex]; |
|||
STORM_LOG_DEBUG("Best solution found so far is " << storm::utility::convertNumber<double>(thresholds[optimizingObjIndex]) << "."); |
|||
//Compute an upper bound for the optimum and check for convergence
|
|||
optimizationRes = overApproximation->intersection(thresholdsAsPolytope)->optimize(directionOfOptimizingObjective); |
|||
if(optimizationRes.second) { |
|||
result.precisionOfResult = optimizationRes.first[optimizingObjIndex]; - thresholds[optimizingObjIndex]; |
|||
STORM_LOG_DEBUG("Solution can be improved by at most " << storm::utility::convertNumber<double>(*result.precisionOfResult)); |
|||
if(storm::utility::convertNumber<double>(*result.precisionOfResult) < storm::settings::getModule<storm::settings::modules::MultiObjectiveSettings>().getPrecision()) { |
|||
result.numericalResult = thresholds[optimizingObjIndex]; |
|||
result.optimumIsAchievable = checkIfThresholdsAreSatisfied(underApproximation, thresholds, strictThresholds); |
|||
converged=true; |
|||
} |
|||
} |
|||
converged |= (storm::settings::getModule<storm::settings::modules::MultiObjectiveSettings>().isMaxIterationsSet() && result.iterations.size() >= storm::settings::getModule<storm::settings::modules::MultiObjectiveSettings>().getMaxIterations()); |
|||
if(!converged) { |
|||
WeightVector separatingVector = findSeparatingVector(thresholds, result.underApproximation, individualObjectivesToBeChecked); |
|||
refineResult(separatingVector, weightVectorChecker, result); |
|||
} |
|||
} while(!converged); |
|||
} |
|||
} |
|||
|
|||
template <class SparseModelType, typename RationalNumberType> |
|||
void SparseMultiObjectiveHelper<SparseModelType, RationalNumberType>::paretoQuery(PreprocessorData const& data, ReturnType& result) { |
|||
//First consider the objectives individually
|
|||
for(uint_fast64_t objIndex = 0; objIndex < data.objectives.size(); ++objIndex) { |
|||
WeightVector direction(data.objectives.size(), storm::utility::zero<RationalNumberType>()); |
|||
direction[objIndex] = storm::utility::one<RationalNumberType>(); |
|||
refineResult(direction, weightVectorChecker, result); |
|||
} |
|||
|
|||
bool converged=false; |
|||
do { |
|||
// Get the halfspace of the underApproximation with maximal distance to a vertex of the overApproximation
|
|||
std::vector<storm::storage::geometry::Halfspace<RationalNumberType>> underApproxHalfspaces = result.underApproximation->getHalfspaces(); |
|||
std::vector<Point> overApproxVertices = result.overApproximation->getVertices(); |
|||
uint_fast64_t farestHalfspaceIndex = underApproxHalfspaces.size(); |
|||
RationalNumberType farestDistance = storm::utility::zero<RationalNumberType>(); |
|||
for(uint_fast64_t halfspaceIndex = 0; halfspaceIndex < underApproxHalfspaces.size(); ++halfspaceIndex) { |
|||
for(auto const& vertex : overApproxVertices) { |
|||
RationalNumberType distance = -underApproxHalfspaces[halfspaceIndex].euclideanDistance(vertex); |
|||
if(distance > farestDistance) { |
|||
farestHalfspaceIndex = halfspaceIndex; |
|||
farestDistance = distance; |
|||
} |
|||
} |
|||
} |
|||
STORM_LOG_DEBUG("Farest distance between under- and overApproximation is " << storm::utility::convertNumber<double>(farestDistance)); |
|||
if(storm::utility::convertNumber<double>(farestDistance) < storm::settings::getModule<storm::settings::modules::MultiObjectiveSettings>().getPrecision()) { |
|||
result.precisionOfResult = farestDistance; |
|||
converged = true; |
|||
} |
|||
converged |= (storm::settings::getModule<storm::settings::modules::MultiObjectiveSettings>().isMaxIterationsSet() && result.iterations.size() >= storm::settings::getModule<storm::settings::modules::MultiObjectiveSettings>().getMaxIterations()); |
|||
if(!converged) { |
|||
WeightVector direction = underApproxHalfspaces[farestHalfspaceIndex].normalVector(); |
|||
refineResult(direction, weightVectorChecker, result); |
|||
} |
|||
} while(!converged); |
|||
} |
|||
|
|||
|
|||
template <class SparseModelType, typename RationalNumberType> |
|||
void SparseMultiObjectiveHelper<SparseModelType, RationalNumberType>::refineResult(WeightVector const& direction, SparseWeightedObjectivesModelCheckerHelper<SparseModelType> const& weightedObjectivesChecker, ReturnType& result) { |
|||
weightedObjectivesChecker.check(storm::utility::vector::convertNumericVector<typename SparseModelType::ValueType>(direction)); |
|||
STORM_LOG_DEBUG("Result with weighted objectives is " << weightedObjectivesChecker.getInitialStateResultOfObjectives()); |
|||
result.iterations.emplace_back(direction, storm::utility::vector::convertNumericVector<RationalNumberType>(weightedObjectivesChecker.getInitialStateResultOfObjectives()), weightedObjectivesChecker.getScheduler()); |
|||
|
|||
updateOverApproximation(result.iterations.back().point, result.iterations.back().weightVector); |
|||
updateUnderApproximation(); |
|||
} |
|||
|
|||
template <class SparseModelType, typename RationalNumberType> |
|||
typename SparseMultiObjectiveHelper<SparseModelType, RationalNumberType>::WeightVector SparseMultiObjectiveHelper<SparseModelType, RationalNumberType>::findSeparatingVector(Point const& pointToBeSeparated, std::shared_ptr<storm::storage::geometry::Polytope<RationalNumberType>> const& underApproximation, storm::storage::BitVector& individualObjectivesToBeChecked) const { |
|||
STORM_LOG_DEBUG("Searching a weight vector to seperate the point given by " << storm::utility::vector::convertNumericVector<double>(pointToBeSeparated) << "."); |
|||
|
|||
if(underApproximation->isEmpty()) { |
|||
// In this case, every weight vector is separating
|
|||
uint_fast64_t objIndex = individualObjectivesToBeChecked.getNextSetIndex(0) % pointToBeSeparated.size(); |
|||
WeightVector result(pointToBeSeparated.size(), storm::utility::zero<RationalNumberType>()); |
|||
result[objIndex] = storm::utility::one<RationalNumberType>(); |
|||
individualObjectivesToBeChecked.set(objIndex, false); |
|||
return result; |
|||
} |
|||
|
|||
// Reaching this point means that the underApproximation contains halfspaces.
|
|||
// The seperating vector has to be the normal vector of one of these halfspaces.
|
|||
// We pick one with maximal distance to the given point. However, weight vectors that correspond to checking individual objectives take precedence.
|
|||
std::vector<storm::storage::geometry::Halfspace<RationalNumberType>> halfspaces = underApproximation->getHalfspaces(); |
|||
uint_fast64_t farestHalfspaceIndex = 0; |
|||
// Note that we are looking for a halfspace that does not contain the point. Thus, the returned distances are negative.
|
|||
RationalNumberType farestDistance = -halfspaces[farestHalfspaceIndex].euclideanDistance(pointToBeSeparated); |
|||
storm::storage::BitVector nonZeroVectorEntries = ~storm::utility::vector::filterZero<RationalNumberType>(halfspaces[farestHalfspaceIndex].normalVector()); |
|||
bool foundSeparatingSingleObjectiveVector = nonZeroVectorEntries.getNumberOfSetBits()==1 && individualObjectivesToBeChecked.get(nonZeroVectorEntries.getNextSetIndex(0)) && farestDistance>storm::utility::zero<RationalNumberType>(); |
|||
|
|||
for(uint_fast64_t halfspaceIndex = 1; halfspaceIndex < halfspaces.size(); ++halfspaceIndex) { |
|||
RationalNumberType distance = -halfspaces[halfspaceIndex].euclideanDistance(pointToBeSeparated); |
|||
nonZeroVectorEntries = ~storm::utility::vector::filterZero<RationalNumberType>(halfspaces[farestHalfspaceIndex].normalVector()); |
|||
bool isSeparatingSingleObjectiveVector = nonZeroVectorEntries.getNumberOfSetBits() == 1 && individualObjectivesToBeChecked.get(nonZeroVectorEntries.getNextSetIndex(0)) && distance>storm::utility::zero<RationalNumberType>(); |
|||
// Check if this halfspace is 'better' than the current one
|
|||
if((!foundSeparatingSingleObjectiveVector && isSeparatingSingleObjectiveVector ) || (foundSeparatingSingleObjectiveVector==isSeparatingSingleObjectiveVector && distance>farestDistance)) { |
|||
foundSeparatingSingleObjectiveVector |= isSeparatingSingleObjectiveVector; |
|||
farestHalfspaceIndex = halfspaceIndex; |
|||
farestDistance = distance; |
|||
} |
|||
} |
|||
if(foundSeparatingSingleObjectiveVector) { |
|||
nonZeroVectorEntries = ~storm::utility::vector::filterZero<RationalNumberType>(halfspaces[farestHalfspaceIndex].normalVector()); |
|||
individualObjectivesToBeChecked.set(nonZeroVectorEntries.getNextSetIndex(0), false); |
|||
} |
|||
|
|||
STORM_LOG_THROW(farestDistance>=storm::utility::zero<RationalNumberType>(), storm::exceptions::UnexpectedException, "There is no seperating vector."); |
|||
STORM_LOG_DEBUG("Found separating weight vector: " << storm::utility::vector::convertNumericVector<double>(halfspaces[farestHalfspaceIndex].normalVector()) << "."); |
|||
return halfspaces[farestHalfspaceIndex].normalVector(); |
|||
} |
|||
|
|||
template <class SparseModelType, typename RationalNumberType> |
|||
void SparseMultiObjectiveHelper<SparseModelType, RationalNumberType>::updateOverApproximation(std::vector<typename ReturnType::Iteration> const& iterations, std::shared_ptr<storm::storage::geometry::Polytope<RationalNumberType>>& overApproximation) { |
|||
storm::storage::geometry::Halfspace<RationalNumberType> h(iterations.back().weightVector, storm::utility::vector::dotProduct(iterations.back().weightVector, iterations.back().point)); |
|||
|
|||
// Due to numerical issues, it might be the case that the updated overapproximation does not contain the underapproximation,
|
|||
// e.g., when the new point is strictly contained in the underapproximation. Check if this is the case.
|
|||
RationalNumberType maximumOffset = h.offset(); |
|||
for(auto const& iteration : iterations){ |
|||
maximumOffset = std::max(maximumOffset, storm::utility::vector::dotProduct(h.normalVector(), iteration.point)); |
|||
} |
|||
if(maximumOffset > h.offset()){ |
|||
// We correct the issue by shifting the halfspace such that it contains the underapproximation
|
|||
h.offset() = maximumOffset; |
|||
STORM_LOG_WARN("Numerical issues: The overapproximation would not contain the underapproximation. Hence, a halfspace is shifted by " << storm::utility::convertNumber<double>(h.euclideanDistance(iterations.back().point)) << "."); |
|||
} |
|||
overApproximation = overApproximation->intersection(h); |
|||
STORM_LOG_DEBUG("Updated OverApproximation to " << overApproximation->toString(true)); |
|||
} |
|||
|
|||
template <class SparseModelType, typename RationalNumberType> |
|||
void SparseMultiObjectiveHelper<SparseModelType, RationalNumberType>::updateUnderApproximation(std::vector<typename ReturnType::Iteration> const& iterations, std::shared_ptr<storm::storage::geometry::Polytope<RationalNumberType>>& underApproximation) { |
|||
std::vector<Point> paretoPointsVec; |
|||
paretoPointsVec.reserve(iterations.size()); |
|||
for(auto const& iteration : iterations) { |
|||
paretoPointsVec.push_back(iterations.point); |
|||
} |
|||
|
|||
STORM_LOG_WARN("REMOVE ADDING ADDITIONAL VERTICES AS SOON AS HYPRO WORKS FOR DEGENERATED POLYTOPES"); |
|||
if(paretoPointsVec.front().size()==2) { |
|||
Point p1 = {-10000, -9999}; |
|||
Point p2 = {-9999, -10000}; |
|||
paretoPointsVec.push_back(p1); |
|||
paretoPointsVec.push_back(p2); |
|||
} else { |
|||
Point p1 = {-10000, -9999, -9999}; |
|||
Point p2 = {-9999, -10000, -9999}; |
|||
Point p3 = {-9999, -9999, -10000}; |
|||
paretoPointsVec.push_back(p1); |
|||
paretoPointsVec.push_back(p2); |
|||
paretoPointsVec.push_back(p3); |
|||
} |
|||
|
|||
boost::optional<Point> upperBounds; |
|||
if(!paretoPointsVec.empty()){ |
|||
//Get the pointwise maximum of the pareto points
|
|||
upperBounds = paretoPointsVec.front(); |
|||
for(auto paretoPointIt = paretoPointsVec.begin()+1; paretoPointIt != paretoPointsVec.end(); ++paretoPointIt){ |
|||
auto upperBoundIt = upperBounds->begin(); |
|||
for(auto const& paretoPointCoordinate : *paretoPointIt){ |
|||
if(paretoPointCoordinate>*upperBoundIt){ |
|||
*upperBoundIt = paretoPointCoordinate; |
|||
} |
|||
++upperBoundIt; |
|||
} |
|||
} |
|||
} |
|||
|
|||
underApproximation = storm::storage::geometry::Polytope<RationalNumberType>::create(paretoPointsVec)->downwardClosure(upperBounds); |
|||
STORM_LOG_DEBUG("Updated UnderApproximation to " << underApproximation->toString(true)); |
|||
} |
|||
|
|||
template <class SparseModelType, typename RationalNumberType> |
|||
bool SparseMultiObjectiveHelper<SparseModelType, RationalNumberType>::checkIfThresholdsAreSatisfied(std::shared_ptr<storm::storage::geometry::Polytope<RationalNumberType>> const& polytope, Point const& thresholds, storm::storage::BitVector const& strictThresholds) const { |
|||
std::vector<storm::storage::geometry::Halfspace<RationalNumberType>> halfspaces = polytope->getHalfspaces(); |
|||
for(auto const& h : halfspaces) { |
|||
RationalNumberType distance = h.distance(thresholds); |
|||
if(distance < storm::utility::zero<RationalNumberType>()) { |
|||
return false; |
|||
} |
|||
if(distance == storm::utility::zero<RationalNumberType>()) { |
|||
// In this case, the thresholds point is on the boundary of the polytope.
|
|||
// Check if this is problematic for the strict thresholds
|
|||
for(auto strictThreshold : strictThresholds) { |
|||
if(h.normalVector()[strictThreshold] > storm::utility::zero<RationalNumberType>()) { |
|||
return false; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
#ifdef STORM_HAVE_CARL
|
|||
template class SparseMultiObjectiveHelper<storm::models::sparse::Mdp<double>, storm::RationalNumber>; |
|||
#endif
|
|||
} |
|||
} |
|||
} |
@ -0,0 +1,67 @@ |
|||
#ifndef STORM_MODELCHECKER_MULTIOBJECTIVE_HELPER_SPARSEMULTIOBJECTIVEHELPER_H_ |
|||
#define STORM_MODELCHECKER_MULTIOBJECTIVE_HELPER_SPARSEMULTIOBJECTIVEHELPER_H_ |
|||
|
|||
#include "src/modelchecker/multiobjective/helper/SparseMultiObjectivePreprocessorReturnType.h" |
|||
#include "src/modelchecker/multiObjective/helper/SparseMultiObjectiveWeightVectorChecker.h" |
|||
#include "src/storage/geometry/Polytope.h" |
|||
#include "src/storage/TotalScheduler.h" |
|||
|
|||
|
|||
namespace storm { |
|||
namespace modelchecker { |
|||
namespace helper { |
|||
|
|||
template <class SparseModelType, typename RationalNumberType> |
|||
class SparseMultiObjectiveHelper { |
|||
public: |
|||
typedef SpaseMultiObjectivePreprocessorReturnType<SparseModelType> PreprocessorData; |
|||
typedef SpaseMultiObjectiveHelperReturnType<RationalNumberType> ReturnType; |
|||
|
|||
typedef std::vector<RationalNumberType> Point; |
|||
typedef std::vector<RationalNumberType> WeightVector; |
|||
|
|||
|
|||
static ReturnType check(PreprocessorData const& data); |
|||
|
|||
private: |
|||
|
|||
static void achievabilityQuery(PreprocessorData const& data, ReturnType& result); |
|||
static void numericalQuery(PreprocessorData const& data, ReturnType& result); |
|||
static void paretoQuery(PreprocessorData const& data, ReturnType& result); |
|||
|
|||
/* |
|||
* Refines the current result w.r.t. the given direction vector |
|||
*/ |
|||
static void refineResult(WeightVector const& direction, SparseWeightedObjectivesModelCheckerHelper<SparseModelType> const& weightedObjectivesChecker, ReturnType& result); |
|||
|
|||
/* |
|||
* Returns a weight vector w that separates the under approximation from the given point p, i.e., |
|||
* For each x in the under approximation, it holds that w*x <= w*p |
|||
* |
|||
* @param pointToBeSeparated the point that is to be seperated |
|||
* @param underapproximation the current under approximation |
|||
* @param objectivesToBeCheckedIndividually stores for each objective whether it still makes sense to check for this objective individually (i.e., with weight vector given by w_{objIndex} = 1 ) |
|||
*/ |
|||
static WeightVector findSeparatingVector(Point const& pointToBeSeparated, std::shared_ptr<storm::storage::geometry::Polytope<RationalNumberType>> const& underApproximation, storm::storage::BitVector& individualObjectivesToBeChecked) const; |
|||
|
|||
/* |
|||
* Updates the over- and under approximation, respectively |
|||
* |
|||
* @param iterations the iterations performed so far. iterations.back() should be the newest iteration, whose information is not yet included in the approximation. |
|||
* @param over/underApproximation the current over/underApproximation that will be updated |
|||
*/ |
|||
static void updateOverApproximation(std::vector<typename ReturnType::Iteration> const& iterations, std::shared_ptr<storm::storage::geometry::Polytope<RationalNumberType>>& overApproximation); |
|||
static void updateUnderApproximation(std::vector<typename ReturnType::Iteration> const& iterations, std::shared_ptr<storm::storage::geometry::Polytope<RationalNumberType>>& underApproximation); |
|||
|
|||
/* |
|||
* Returns true iff there is a point in the given polytope that satisfies the given thresholds. |
|||
* It is assumed that the given polytope contains the downward closure of its vertices. |
|||
*/ |
|||
static bool checkIfThresholdsAreSatisfied(std::shared_ptr<storm::storage::geometry::Polytope<RationalNumberType>> const& polytope, Point const& thresholds, storm::storage::BitVector const& strictThresholds) const; |
|||
}; |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
#endif /* STORM_MODELCHECKER_MULTIOBJECTIVE_HELPER_SPARSEMULTIOBJECTIVEHELPER_H_ */ |
@ -0,0 +1,56 @@ |
|||
#ifndef STORM_MODELCHECKER_MULTIOBJECTIVE_HELPER_SPARSEMULTIOBJECTIVEHELPERRETURNTYPE_H_ |
|||
#define STORM_MODELCHECKER_MULTIOBJECTIVE_HELPER_SPARSEMULTIOBJECTIVEHELPERRETURNTYPE_H_ |
|||
|
|||
#include <vector> |
|||
#include <memory> |
|||
#include <iomanip> |
|||
#include <boost/optional.hpp> |
|||
|
|||
#include "src/storage/geometry/Polytope.h" |
|||
#include "src/storage/TotalScheduler.h" |
|||
|
|||
namespace storm { |
|||
namespace modelchecker { |
|||
namespace helper { |
|||
|
|||
template <typename RationalNumberType> |
|||
struct SparseMultiObjectiveHelperReturnType { |
|||
|
|||
struct Iteration { |
|||
Iteration (std::vector<RationalNumberType> const& weightVector, std::vector<RationalNumberType> const& point, storm::storage::TotalScheduler const& scheduler) : weightVector(weightVector), point(point), scheduler(scheduler) { |
|||
//Intentionally left empty |
|||
} |
|||
|
|||
Iteration (std::vector<RationalNumberType>&& weightVector, std::vector<RationalNumberType>&& point, storm::storage::TotalScheduler&& scheduler) : weightVector(weightVector), point(point), scheduler(scheduler) { |
|||
//Intentionally left empty |
|||
} |
|||
|
|||
std::vector<RationalNumberType> weightVector; |
|||
std::vector<RationalNumberType> point; |
|||
storm::storage::TotalScheduler scheduler; |
|||
} |
|||
|
|||
//Stores the results for the individual iterations |
|||
std::vector<Iteration> iterations; |
|||
|
|||
//Stores an over/ under approximation of the set of achievable values |
|||
std::shared_ptr<storm::storage::geometry::Polytope<RationalNumberType>> overApproximation; |
|||
std::shared_ptr<storm::storage::geometry::Polytope<RationalNumberType>> underApproximation; |
|||
|
|||
// Stores the result of an achievability query (if applicable) |
|||
// For a numerical query, stores whether there is one feasible solution |
|||
boost::optional<bool> thresholdsAreAchievable; |
|||
|
|||
//Stores the result of a numerical query (if applicable) |
|||
boost::optional<RationalNumberType> numericalResult; |
|||
boost::optional<bool> optimumIsAchievable; //True if there is an actual scheduler that induces the computed supremum (i.e., supremum == maximum) |
|||
|
|||
//Stores the achieved precision for numerical and pareto queries |
|||
boost::optional<RationalNumberType> precisionOfResult; |
|||
|
|||
}; |
|||
} |
|||
} |
|||
} |
|||
|
|||
#endif /* STORM_MODELCHECKER_MULTIOBJECTIVE_HELPER_SPARSEMULTIOBJECTIVEHELPERRETURNTYPE_H_ */ |
@ -1,490 +0,0 @@ |
|||
#include "src/modelchecker/multiobjective/helper/SparseMultiObjectiveModelCheckerHelper.h"
|
|||
|
|||
#include "src/adapters/CarlAdapter.h"
|
|||
#include "src/models/sparse/Mdp.h"
|
|||
#include "src/models/sparse/StandardRewardModel.h"
|
|||
#include "src/utility/constants.h"
|
|||
#include "src/utility/vector.h"
|
|||
#include "src/settings//SettingsManager.h"
|
|||
#include "src/settings/modules/MultiObjectiveSettings.h"
|
|||
|
|||
|
|||
#include "src/exceptions/UnexpectedException.h"
|
|||
|
|||
namespace storm { |
|||
namespace modelchecker { |
|||
namespace helper { |
|||
|
|||
template <class SparseModelType, typename RationalNumberType> |
|||
SparseMultiObjectiveModelCheckerHelper<SparseModelType, RationalNumberType>::SparseMultiObjectiveModelCheckerHelper(Information& info) : info(info), weightedObjectivesChecker(info), numIterations(0) { |
|||
overApproximation = storm::storage::geometry::Polytope<RationalNumberType>::createUniversalPolytope(); |
|||
underApproximation = storm::storage::geometry::Polytope<RationalNumberType>::createEmptyPolytope(); |
|||
} |
|||
|
|||
template <class SparseModelType, typename RationalNumberType> |
|||
SparseMultiObjectiveModelCheckerHelper<SparseModelType, RationalNumberType>::~SparseMultiObjectiveModelCheckerHelper() { |
|||
// Intentionally left empty
|
|||
} |
|||
|
|||
template <class SparseModelType, typename RationalNumberType> |
|||
void SparseMultiObjectiveModelCheckerHelper<SparseModelType, RationalNumberType>::check(Information & info) { |
|||
SparseMultiObjectiveModelCheckerHelper<SparseModelType, RationalNumberType> helper(info); |
|||
uint_fast64_t numOfObjectivesWithoutThreshold = 0; |
|||
for(auto const& obj : info.objectives) { |
|||
if(!obj.threshold) { |
|||
++numOfObjectivesWithoutThreshold; |
|||
} |
|||
} |
|||
if(numOfObjectivesWithoutThreshold == 0) { |
|||
helper.achievabilityQuery(); |
|||
} else if (numOfObjectivesWithoutThreshold == 1) { |
|||
helper.numericalQuery(); |
|||
} else if (numOfObjectivesWithoutThreshold == info.objectives.size()) { |
|||
helper.paretoQuery(); |
|||
} else { |
|||
STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "The number of objecties without threshold is not valid. It should be either 0 (achievabilityQuery), 1 (numericalQuery), or " << info.objectives.size() << " (paretoQuery). Got " << numOfObjectivesWithoutThreshold << " instead."); |
|||
} |
|||
} |
|||
|
|||
template <class SparseModelType, typename RationalNumberType> |
|||
void SparseMultiObjectiveModelCheckerHelper<SparseModelType, RationalNumberType>::achievabilityQuery() { |
|||
Point thresholds; |
|||
thresholds.reserve(info.objectives.size()); |
|||
storm::storage::BitVector strictThresholds(info.objectives.size()); |
|||
for(uint_fast64_t objIndex = 0; objIndex < info.objectives.size(); ++objIndex) { |
|||
STORM_LOG_THROW(info.objectives[objIndex].threshold, storm::exceptions::UnexpectedException, "Can not perform achievabilityQuery: No threshold given for at least one objective."); |
|||
thresholds.push_back(storm::utility::convertNumber<RationalNumberType>(*info.objectives[objIndex].threshold)); |
|||
strictThresholds.set(objIndex, info.objectives[objIndex].thresholdIsStrict); |
|||
} |
|||
|
|||
storm::storage::BitVector individualObjectivesToBeChecked(info.objectives.size(), true); |
|||
bool converged=false; |
|||
do { |
|||
WeightVector separatingVector = findSeparatingVector(thresholds, individualObjectivesToBeChecked); |
|||
refineSolution(separatingVector); |
|||
++numIterations; |
|||
// Check for convergence
|
|||
if(!checkIfThresholdsAreSatisfied(overApproximation, thresholds, strictThresholds)){ |
|||
std::cout << "PROPERTY VIOLATED" << std::endl; |
|||
converged=true; |
|||
} |
|||
if(checkIfThresholdsAreSatisfied(underApproximation, thresholds, strictThresholds)){ |
|||
std::cout << "PROPERTY SATISFIED" << std::endl; |
|||
converged=true; |
|||
} |
|||
converged |= (storm::settings::getModule<storm::settings::modules::MultiObjectiveSettings>().isMaxIterationsSet() && numIterations >= storm::settings::getModule<storm::settings::modules::MultiObjectiveSettings>().getMaxIterations()); |
|||
} while(!converged); |
|||
} |
|||
|
|||
template <class SparseModelType, typename RationalNumberType> |
|||
void SparseMultiObjectiveModelCheckerHelper<SparseModelType, RationalNumberType>::numericalQuery() { |
|||
Point thresholds; |
|||
thresholds.reserve(info.objectives.size()); |
|||
storm::storage::BitVector strictThresholds(info.objectives.size()); |
|||
std::vector<storm::storage::geometry::Halfspace<RationalNumberType>> thresholdConstraints; |
|||
thresholdConstraints.reserve(info.objectives.size()-1); |
|||
uint_fast64_t optimizingObjIndex = info.objectives.size(); |
|||
for(uint_fast64_t objIndex = 0; objIndex < info.objectives.size(); ++objIndex) { |
|||
if(info.objectives[objIndex].threshold) { |
|||
thresholds.push_back(storm::utility::convertNumber<RationalNumberType>(*info.objectives[objIndex].threshold)); |
|||
WeightVector normalVector(info.objectives.size(), storm::utility::zero<RationalNumberType>()); |
|||
normalVector[objIndex] = -storm::utility::one<RationalNumberType>(); |
|||
thresholdConstraints.emplace_back(std::move(normalVector), -thresholds.back()); |
|||
std::cout << "adding threshold constraint" << thresholdConstraints.back().toString(true) << std::endl; |
|||
strictThresholds.set(objIndex, info.objectives[objIndex].thresholdIsStrict); |
|||
} else { |
|||
STORM_LOG_ASSERT(optimizingObjIndex == info.objectives.size(), "Numerical Query invoked but there are multiple objectives without threshold"); |
|||
optimizingObjIndex = objIndex; |
|||
thresholds.push_back(storm::utility::zero<RationalNumberType>()); |
|||
} |
|||
} |
|||
STORM_LOG_ASSERT(optimizingObjIndex < info.objectives.size(), "Numerical Query invoked but there are no objectives without threshold"); |
|||
auto thresholdsAsPolytope = storm::storage::geometry::Polytope<RationalNumberType>::create(thresholdConstraints); |
|||
WeightVector directionOfOptimizingObjective(info.objectives.size(), storm::utility::zero<RationalNumberType>()); |
|||
directionOfOptimizingObjective[optimizingObjIndex] = storm::utility::one<RationalNumberType>(); |
|||
|
|||
// Try to find one valid solution
|
|||
storm::storage::BitVector individualObjectivesToBeChecked(info.objectives.size(), true); |
|||
individualObjectivesToBeChecked.set(optimizingObjIndex, false); |
|||
do { //TODO introduce convergence criterion? (like max num of iterations)
|
|||
WeightVector separatingVector = findSeparatingVector(thresholds, individualObjectivesToBeChecked); |
|||
refineSolution(separatingVector); |
|||
//Pick the threshold for the optimizing objective low enough so valid solutions are not excluded
|
|||
for(auto const& paretoPoint : paretoOptimalPoints) { |
|||
thresholds[optimizingObjIndex] = std::min(thresholds[optimizingObjIndex], paretoPoint.first[optimizingObjIndex]); |
|||
} |
|||
if(!checkIfThresholdsAreSatisfied(overApproximation, thresholds, strictThresholds)){ |
|||
std::cout << "INFEASIBLE OBJECTIVE THRESHOLDS" << std::endl; |
|||
return; |
|||
} |
|||
} while(!checkIfThresholdsAreSatisfied(underApproximation, thresholds, strictThresholds)); |
|||
STORM_LOG_DEBUG("Found a solution that satisfies the objective thresholds."); |
|||
individualObjectivesToBeChecked.clear(); |
|||
|
|||
// Improve the found solution.
|
|||
// Note that we do not have to care whether a threshold is strict anymore, because the resulting optimum should be
|
|||
// the supremum over all strategies. Hence, one could combine a scheduler inducing the optimum value (but possibly violating strict
|
|||
// thresholds) and (with very low probability) a scheduler that satisfies all (possibly strict) thresholds.
|
|||
while (true) { |
|||
std::pair<Point, bool> optimizationRes = underApproximation->intersection(thresholdsAsPolytope)->optimize(directionOfOptimizingObjective); |
|||
STORM_LOG_THROW(optimizationRes.second, storm::exceptions::UnexpectedException, "The underapproximation is either unbounded or empty."); |
|||
thresholds[optimizingObjIndex] = optimizationRes.first[optimizingObjIndex]; |
|||
STORM_LOG_DEBUG("Best solution found so far is " << storm::utility::convertNumber<double>(thresholds[optimizingObjIndex]) << "."); |
|||
//Compute an upper bound for the optimum and check for convergence
|
|||
optimizationRes = overApproximation->intersection(thresholdsAsPolytope)->optimize(directionOfOptimizingObjective); |
|||
if(optimizationRes.second) { |
|||
RationalNumberType upperBoundOnOptimum = optimizationRes.first[optimizingObjIndex]; |
|||
STORM_LOG_DEBUG("Solution can be improved by at most " << storm::utility::convertNumber<double>(upperBoundOnOptimum - thresholds[optimizingObjIndex])); |
|||
if(storm::utility::convertNumber<double>(upperBoundOnOptimum - thresholds[optimizingObjIndex]) < storm::settings::getModule<storm::settings::modules::MultiObjectiveSettings>().getPrecision()) { std::cout << "Found Optimum: " << storm::utility::convertNumber<double>(thresholds[optimizingObjIndex]) << "." << std::endl; |
|||
if(!checkIfThresholdsAreSatisfied(underApproximation, thresholds, strictThresholds)) { |
|||
std::cout << "Optimum is only the supremum" << std::endl; |
|||
} |
|||
return; |
|||
} |
|||
} |
|||
WeightVector separatingVector = findSeparatingVector(thresholds, individualObjectivesToBeChecked); |
|||
refineSolution(separatingVector); |
|||
} |
|||
} |
|||
|
|||
template <class SparseModelType, typename RationalNumberType> |
|||
void SparseMultiObjectiveModelCheckerHelper<SparseModelType, RationalNumberType>::paretoQuery() { |
|||
//First consider the objectives individually
|
|||
for(uint_fast64_t objIndex = 0; objIndex < info.objectives.size(); ++objIndex) { |
|||
WeightVector direction(info.objectives.size(), storm::utility::zero<RationalNumberType>()); |
|||
direction[objIndex] = storm::utility::one<RationalNumberType>(); |
|||
refineSolution(direction); |
|||
} |
|||
|
|||
while(true) { //todo maxNumOfIterations ?
|
|||
|
|||
// Get the halfspace of the underApproximation with maximal distance to a vertex of the overApproximation
|
|||
std::vector<storm::storage::geometry::Halfspace<RationalNumberType>> underApproxHalfspaces = underApproximation->getHalfspaces(); |
|||
std::vector<Point> overApproxVertices = overApproximation->getVertices(); |
|||
uint_fast64_t farestHalfspaceIndex = underApproxHalfspaces.size(); |
|||
RationalNumberType farestDistance = storm::utility::zero<RationalNumberType>(); |
|||
for(uint_fast64_t halfspaceIndex = 0; halfspaceIndex < underApproxHalfspaces.size(); ++halfspaceIndex) { |
|||
for(auto const& vertex : overApproxVertices) { |
|||
RationalNumberType distance = underApproxHalfspaces[halfspaceIndex].euclideanDistance(vertex); |
|||
// Note that the distances are negative, i.e., we have to consider the minimum distance
|
|||
if(distance < farestDistance) { |
|||
farestHalfspaceIndex = halfspaceIndex; |
|||
farestDistance = distance; |
|||
} |
|||
} |
|||
} |
|||
STORM_LOG_DEBUG("Farest distance between under- and overApproximation is " << storm::utility::convertNumber<double>(farestDistance)); |
|||
if(-storm::utility::convertNumber<double>(farestDistance) < storm::settings::getModule<storm::settings::modules::MultiObjectiveSettings>().getPrecision()) { |
|||
std::cout << "DONE computing the pareto curve" << std::endl; |
|||
return; |
|||
} |
|||
WeightVector direction = underApproxHalfspaces[farestHalfspaceIndex].normalVector(); |
|||
// normalize the direction vector so the entries sum up to one
|
|||
storm::utility::vector::scaleVectorInPlace(direction, storm::utility::one<RationalNumberType>() / std::accumulate(direction.begin(), direction.end(), storm::utility::zero<RationalNumberType>())); |
|||
refineSolution(direction); |
|||
} |
|||
} |
|||
|
|||
|
|||
template <class SparseModelType, typename RationalNumberType> |
|||
void SparseMultiObjectiveModelCheckerHelper<SparseModelType, RationalNumberType>::refineSolution(WeightVector const& direction) { |
|||
weightedObjectivesChecker.check(storm::utility::vector::convertNumericVector<ModelValueType>(direction)); |
|||
|
|||
storm::storage::TotalScheduler scheduler = weightedObjectivesChecker.getScheduler(); |
|||
Point weightedObjectivesResult = storm::utility::vector::convertNumericVector<RationalNumberType>(weightedObjectivesChecker.getInitialStateResultOfObjectives()); |
|||
STORM_LOG_DEBUG("Result with weighted objectives is " << storm::utility::vector::toString(weightedObjectivesResult)); |
|||
|
|||
// Insert the computed scheduler and check whether we have already seen it before
|
|||
auto schedulerInsertRes = schedulers.insert(std::make_pair(std::move(scheduler), paretoOptimalPoints.end())); |
|||
if(schedulerInsertRes.second){ |
|||
// The scheduler is new, so insert the newly computed pareto optimal point.
|
|||
// For the corresponding scheduler, we safe the entry in the map.
|
|||
schedulerInsertRes.first->second = paretoOptimalPoints.insert(std::make_pair(std::move(weightedObjectivesResult), std::vector<WeightVector>())).first; |
|||
} |
|||
// In the case where the scheduler is not new, we assume that the corresponding pareto optimal points for the old and new scheduler are equal.
|
|||
// hence, no additional point will be inserted.
|
|||
// Note that the values might not be exactly equal due to numerical issues.
|
|||
|
|||
Point const& paretoPoint = schedulerInsertRes.first->second->first; |
|||
std::vector<WeightVector>& weightVectors = schedulerInsertRes.first->second->second; |
|||
weightVectors.push_back(direction); |
|||
|
|||
updateOverApproximation(paretoPoint, weightVectors.back()); |
|||
updateUnderApproximation(); |
|||
} |
|||
|
|||
template <class SparseModelType, typename RationalNumberType> |
|||
typename SparseMultiObjectiveModelCheckerHelper<SparseModelType, RationalNumberType>::WeightVector SparseMultiObjectiveModelCheckerHelper<SparseModelType, RationalNumberType>::findSeparatingVector(Point const& pointToBeSeparated, storm::storage::BitVector& individualObjectivesToBeChecked) const { |
|||
STORM_LOG_DEBUG("Searching a weight vector to seperate the point given by " << storm::utility::vector::convertNumericVector<double>(pointToBeSeparated) << "."); |
|||
// First, we check 'simple' weight vectors that correspond to checking a single objective
|
|||
while (!individualObjectivesToBeChecked.empty()){ |
|||
uint_fast64_t objectiveIndex = individualObjectivesToBeChecked.getNextSetIndex(0); |
|||
individualObjectivesToBeChecked.set(objectiveIndex, false); |
|||
|
|||
WeightVector normalVector(info.objectives.size(), storm::utility::zero<RationalNumberType>()); |
|||
normalVector[objectiveIndex] = storm::utility::one<RationalNumberType>(); |
|||
|
|||
storm::storage::geometry::Halfspace<RationalNumberType> h(normalVector, storm::utility::vector::dotProduct(normalVector, pointToBeSeparated)); |
|||
bool hIsSeparating = true; |
|||
for(auto const& paretoPoint : paretoOptimalPoints){ |
|||
hIsSeparating &= h.contains(paretoPoint.first); |
|||
} |
|||
if(hIsSeparating) { |
|||
return h.normalVector(); |
|||
} |
|||
} |
|||
|
|||
if(underApproximation->isEmpty()) { |
|||
// In this case [1 0..0] is always separating
|
|||
WeightVector result(info.objectives.size(), storm::utility::zero<RationalNumberType>()); |
|||
result.front() = storm::utility::one<RationalNumberType>(); |
|||
return result; |
|||
} |
|||
|
|||
// Get the halfspace of the underApproximation with maximal distance to the given point
|
|||
// Note that we are looking for a halfspace that does not contain the point. Thus the distances are negative and
|
|||
// we are actually searching for the one with minimal distance.
|
|||
std::vector<storm::storage::geometry::Halfspace<RationalNumberType>> halfspaces = underApproximation->getHalfspaces(); |
|||
uint_fast64_t farestHalfspaceIndex = 0; |
|||
RationalNumberType farestDistance = halfspaces[farestHalfspaceIndex].euclideanDistance(pointToBeSeparated); |
|||
for(uint_fast64_t halfspaceIndex = 1; halfspaceIndex < halfspaces.size(); ++halfspaceIndex) { |
|||
RationalNumberType distance = halfspaces[halfspaceIndex].euclideanDistance(pointToBeSeparated); |
|||
if(distance < farestDistance) { |
|||
farestHalfspaceIndex = halfspaceIndex; |
|||
farestDistance = distance; |
|||
} |
|||
} |
|||
STORM_LOG_THROW(farestDistance<=storm::utility::zero<RationalNumberType>(), storm::exceptions::UnexpectedException, "There is no seperating halfspace."); |
|||
// normalize the result so the entries sum up to one
|
|||
WeightVector result = halfspaces[farestHalfspaceIndex].normalVector(); |
|||
storm::utility::vector::scaleVectorInPlace(result, storm::utility::one<RationalNumberType>() / std::accumulate(result.begin(), result.end(), storm::utility::zero<RationalNumberType>())); |
|||
return result; |
|||
|
|||
|
|||
/* old version using LP solving
|
|||
// We now use LP solving to find a seperating halfspace.
|
|||
// Note that StoRM's LPSover does not support rational numbers. Hence, we optimize a polytope instead.
|
|||
// The variables of the LP are the normalVector entries w_0 ... w_{n-1} and the minimal distance d between the halfspace and the paretoOptimalPoints
|
|||
uint_fast64_t numVariables = pointToBeSeparated.size() + 1; |
|||
std::vector<storm::storage::geometry::Halfspace<RationalNumberType>> constraints; |
|||
constraints.reserve((numVariables-1) + 2 + paretoOptimalPoints.size()); |
|||
// w_i >= 0 and d>= 0 <==> -w_i <= 0 and -d <= 0
|
|||
for(uint_fast64_t i = 0; i<numVariables; ++i) { |
|||
WeightVector constraintCoefficients(numVariables, storm::utility::zero<RationalNumberType>()); |
|||
constraintCoefficients[i] = -storm::utility::one<RationalNumberType>(); |
|||
RationalNumberType constraintOffset = storm::utility::zero<RationalNumberType>(); |
|||
constraints.emplace_back(std::move(constraintCoefficients), std::move(constraintOffset)); |
|||
} |
|||
// sum_i w_i == 1 <==> sum_i w_i <=1 and sum_i -w_i <= -1
|
|||
{ |
|||
WeightVector constraintCoefficients(numVariables, storm::utility::one<RationalNumberType>()); |
|||
constraintCoefficients.back() = storm::utility::zero<RationalNumberType>(); |
|||
RationalNumberType constraintOffset = storm::utility::one<RationalNumberType>(); |
|||
constraints.emplace_back(constraintCoefficients, constraintOffset); |
|||
storm::utility::vector::scaleVectorInPlace(constraintCoefficients, -storm::utility::one<RationalNumberType>()); |
|||
constraintOffset = -storm::utility::one<RationalNumberType>(); |
|||
constraints.emplace_back(std::move(constraintCoefficients), std::move(constraintOffset)); |
|||
} |
|||
// let q=pointToBeSeparated. For each paretoPoint x: q*w - x*w >= d <==> (x-q) * w + d <= 0
|
|||
for(auto const& paretoPoint : paretoOptimalPoints){ |
|||
WeightVector constraintCoefficients(numVariables-1); |
|||
storm::utility::vector::subtractVectors(paretoPoint.first, pointToBeSeparated, constraintCoefficients); |
|||
constraintCoefficients.push_back(storm::utility::one<RationalNumberType>()); |
|||
RationalNumberType constraintOffset = storm::utility::zero<RationalNumberType>(); |
|||
constraints.emplace_back(std::move(constraintCoefficients), std::move(constraintOffset)); |
|||
} |
|||
|
|||
WeightVector optimizationVector(numVariables); |
|||
optimizationVector.back() = storm::utility::one<RationalNumberType>(); // maximize d
|
|||
std::pair<WeightVector, bool> optimizeResult = storm::storage::geometry::Polytope<RationalNumberType>::create(constraints)->optimize(optimizationVector); |
|||
STORM_LOG_THROW(optimizeResult.second, storm::exceptions::UnexpectedException, "There seems to be no seperating halfspace."); |
|||
optimizeResult.first.pop_back(); //drop the distance
|
|||
RationalNumberType offset = storm::utility::vector::dotProduct(optimizeResult.first, pointToBeSeparated); |
|||
return storm::storage::geometry::Halfspace<RationalNumberType>(std::move(optimizeResult.first), std::move(offset)); |
|||
*/ |
|||
} |
|||
|
|||
template <class SparseModelType, typename RationalNumberType> |
|||
void SparseMultiObjectiveModelCheckerHelper<SparseModelType, RationalNumberType>::updateOverApproximation(Point const& newPoint, WeightVector const& newWeightVector) { |
|||
storm::storage::geometry::Halfspace<RationalNumberType> h(newWeightVector, storm::utility::vector::dotProduct(newWeightVector, newPoint)); |
|||
|
|||
// Due to numerical issues, it might be the case that the updated overapproximation does not contain the underapproximation,
|
|||
// e.g., when the new point is strictly contained in the underapproximation. Check if this is the case.
|
|||
RationalNumberType maximumOffset = h.offset(); |
|||
for(auto const& paretoPoint : paretoOptimalPoints){ |
|||
maximumOffset = std::max(maximumOffset, storm::utility::vector::dotProduct(h.normalVector(), paretoPoint.first)); |
|||
} |
|||
if(maximumOffset > h.offset()){ |
|||
// We correct the issue by shifting the halfspace such that it contains the underapproximation
|
|||
h.offset() = maximumOffset; |
|||
STORM_LOG_WARN("Numerical issues: The overapproximation would not contain the underapproximation. Hence, a halfspace is shifted by " << storm::utility::convertNumber<double>(h.euclideanDistance(newPoint)) << "."); |
|||
} |
|||
overApproximation = overApproximation->intersection(h); |
|||
STORM_LOG_DEBUG("Updated OverApproximation to " << overApproximation->toString(true)); |
|||
} |
|||
|
|||
template <class SparseModelType, typename RationalNumberType> |
|||
void SparseMultiObjectiveModelCheckerHelper<SparseModelType, RationalNumberType>::updateUnderApproximation() { |
|||
std::vector<Point> paretoPointsVec; |
|||
paretoPointsVec.reserve(paretoOptimalPoints.size()); |
|||
for(auto const& paretoPoint : paretoOptimalPoints) { |
|||
paretoPointsVec.push_back(paretoPoint.first); |
|||
} |
|||
|
|||
STORM_LOG_WARN("REMOVE ADDING ADDITIONAL VERTICES AS SOON AS HYPRO WORKS FOR DEGENERATED POLYTOPES"); |
|||
Point p1 = {-1000, -999}; |
|||
Point p2 = {-999, -1000}; |
|||
paretoPointsVec.push_back(p1); |
|||
paretoPointsVec.push_back(p2); |
|||
|
|||
boost::optional<Point> upperBounds; |
|||
if(!paretoPointsVec.empty()){ |
|||
//Get the pointwise maximum of the pareto points
|
|||
upperBounds = paretoPointsVec.front(); |
|||
for(auto paretoPointIt = paretoPointsVec.begin()+1; paretoPointIt != paretoPointsVec.end(); ++paretoPointIt){ |
|||
auto upperBoundIt = upperBounds->begin(); |
|||
for(auto const& paretoPointCoordinate : *paretoPointIt){ |
|||
if(paretoPointCoordinate>*upperBoundIt){ |
|||
*upperBoundIt = paretoPointCoordinate; |
|||
} |
|||
++upperBoundIt; |
|||
} |
|||
} |
|||
} |
|||
|
|||
underApproximation = storm::storage::geometry::Polytope<RationalNumberType>::create(paretoPointsVec)->downwardClosure(upperBounds); |
|||
STORM_LOG_DEBUG("Updated UnderApproximation to " << underApproximation->toString(true)); |
|||
} |
|||
|
|||
template <class SparseModelType, typename RationalNumberType> |
|||
bool SparseMultiObjectiveModelCheckerHelper<SparseModelType, RationalNumberType>::checkIfThresholdsAreSatisfied(std::shared_ptr<storm::storage::geometry::Polytope<RationalNumberType>> const& polytope, Point const& thresholds, storm::storage::BitVector const& strictThresholds) const { |
|||
std::vector<storm::storage::geometry::Halfspace<RationalNumberType>> halfspaces = polytope->getHalfspaces(); |
|||
for(auto const& h : halfspaces) { |
|||
RationalNumberType distance = h.distance(thresholds); |
|||
if(distance < storm::utility::zero<RationalNumberType>()) { |
|||
return false; |
|||
} |
|||
if(distance == storm::utility::zero<RationalNumberType>()) { |
|||
// In this case, the thresholds point is on the boundary of the polytope.
|
|||
// Check if this is problematic for the strict thresholds
|
|||
for(auto strictThreshold : strictThresholds) { |
|||
if(h.normalVector()[strictThreshold] > storm::utility::zero<RationalNumberType>()) { |
|||
return false; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
|
|||
|
|||
/*
|
|||
|
|||
bool existsObjectiveWithoutThreshold = false; |
|||
bool existsObjectiveWithStrictThreshold = false; |
|||
Point thresholdPoint; |
|||
thresholdPoint.reserve(info.objectives.size()); |
|||
for(auto const& obj : info.objectives) { |
|||
if(obj.threshold) { |
|||
thresholdPoint.push_back(storm::utility::convertNumber<RationalNumber>(*obj.threshold)); |
|||
if(obj.thresholdIsStrict) { |
|||
existsObjectiveWithStrictThreshold = true; |
|||
} |
|||
} else { |
|||
existsObjectiveWithoutThreshold = true; |
|||
thresholdPoint.push_back(storm::utility::one<RationalNumberType>()); |
|||
} |
|||
} |
|||
|
|||
if(existsObjectiveWithoutThreshold) { |
|||
// We need to find values for the objectives without thresholds in a way that the result is not affected by them.
|
|||
// We build a polytope that contains valid solutions according to the thresholds and then analyze the intersection with the given polytope.
|
|||
std::vector<storm::storage::geometry::Halfspace<RationalNumberType>> thresholdConstraints; |
|||
WeightVector directionForOptimization(info.objectives.size(), storm::utility::zero<RationalNumberType>()); |
|||
for(uint_fast64_t objIndex = 0; objIndex < info.objectives.size(); ++objIndex) { |
|||
if(info.objectives[objIndex].threshold) { |
|||
WeightVector normalVector(info.objectives.size(), storm::utility::zero<RationalNumberType>()); |
|||
normalVector[objIndex] = -storm::utility::one<RationalNumberType>(); |
|||
thresholdConstraints.emplace_back(std::move(normalVector), -thresholdPoint[objIndex]); |
|||
directionForOptimization[objIndex] = -storm::utility::one<RationalNumberType>(); |
|||
} |
|||
} |
|||
auto validSolutions = polytope->intersection(storm::storage::geometry::Polytope<RationalNumberType>::create(std::move(thresholdConstraints))); |
|||
if(validSolutions->isEmpty()) { |
|||
return false; |
|||
} |
|||
auto optimizationRes = validSolutions->optimize(directionForOptimization); |
|||
STORM_LOG_THROW(optimizationRes.second, storm::exceptions::UnexpectedException, "Couldn't check whether thresholds are satisfied for a given polytope: Didn't expect an infeasible or unbounded solution."); |
|||
|
|||
for(uint_fast64_t objIndex = 0; objIndex < info.objectives.size(); ++objIndex) { |
|||
if(!info.objectives[objIndex].threshold) { |
|||
thresholdPoint[objIndex] = optimizationRes.first[objIndex] - storm::utility::one<RationalNumberType>(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
// We then check whether the intersection of the thresholdPolytope and the given polytope contains any point (that also satisfies the strict thresholds);
|
|||
|
|||
std::cout << "Valid solutions are " << validSolutions->toString(true) << std::endl; |
|||
std::cout << "not empty" << std::endl; |
|||
if(existsObjectiveWithStrictThreshold) { |
|||
std::cout << "hasObjWithStrictThreshold" << std::endl; |
|||
std::cout << "optRes is " << storm::utility::vector::convertNumericVector<double>(optimizationRes.first) << std::endl; |
|||
for(uint_fast64_t objIndex = 0; objIndex < info.objectives.size(); ++objIndex) { |
|||
if(info.objectives[objIndex].threshold && info.objectives[objIndex].thresholdIsStrict) { |
|||
RationalNumberType threshold = storm::utility::convertNumber<RationalNumberType>( *info.objectives[objIndex].threshold); |
|||
if (optimizationRes.first[objIndex] <= threshold) { |
|||
return false; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return true; |
|||
|
|||
|
|||
|
|||
Point thresholds; |
|||
thresholds.reserve(info.objectives.size()); |
|||
for(auto const& obj : info.objectives) { |
|||
if(obj.threshold) { |
|||
thresholds.push_back(storm::utility::convertNumber<RationalNumber>(*obj.threshold)); |
|||
} else { |
|||
STORM_LOG_ASSERT(numericalQueryResult, "Checking whether thresholds are satisfied for a numerical query but no threshold for the numerical objective was given."); |
|||
thresholds.push_back(*numericalQueryResult); |
|||
} |
|||
} |
|||
std::vector<storm::storage::geometry::Halfspace<RationalNumberType>> halfspaces = polytope->getHalfspaces(); |
|||
for(auto const& h : halfspaces) { |
|||
RationalNumberType distance = h.distance(thresholds); |
|||
if(distance < storm::utility::zero<RationalNumberType>()) { |
|||
return false; |
|||
} |
|||
if(distance == storm::utility::zero<RationalNumberType>()) { |
|||
// In this case, the thresholds point is on the boundary of the polytope.
|
|||
// Check if this is problematic for the strict thresholds
|
|||
for(uint_fast64_t objIndex = 0; objIndex < info.objectives.size(); ++objIndex) { |
|||
if(info.objectives[objIndex].thresholdIsStrict && h.normalVector()[objIndex] > storm::utility::zero<RationalNumberType>()) { |
|||
return false; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return true; |
|||
|
|||
|
|||
|
|||
} |
|||
*/ |
|||
|
|||
#ifdef STORM_HAVE_CARL
|
|||
template class SparseMultiObjectiveModelCheckerHelper<storm::models::sparse::Mdp<double>, storm::RationalNumber>; |
|||
#endif
|
|||
} |
|||
} |
|||
} |
@ -1,80 +0,0 @@ |
|||
#ifndef STORM_MODELCHECKER_MULTIOBJECTIVE_HELPER_SPARSEMULTIOBJECTIVEMODELCHECKERHELPER_H_ |
|||
#define STORM_MODELCHECKER_MULTIOBJECTIVE_HELPER_SPARSEMULTIOBJECTIVEMODELCHECKERHELPER_H_ |
|||
|
|||
#include <map> |
|||
#include <unordered_map> |
|||
#include "src/modelchecker/multiobjective/helper/SparseMultiObjectiveModelCheckerInformation.h" |
|||
#include "src/modelchecker/multiObjective/helper/SparseWeightedObjectivesModelCheckerHelper.h" |
|||
#include "src/storage/geometry/Polytope.h" |
|||
#include "src/storage/TotalScheduler.h" |
|||
|
|||
|
|||
namespace storm { |
|||
namespace modelchecker { |
|||
namespace helper { |
|||
|
|||
template <class SparseModelType, typename RationalNumberType> |
|||
class SparseMultiObjectiveModelCheckerHelper { |
|||
public: |
|||
typedef typename SparseModelType::ValueType ModelValueType; |
|||
typedef typename SparseModelType::RewardModelType RewardModelType; |
|||
typedef SparseMultiObjectiveModelCheckerInformation<SparseModelType> Information; |
|||
|
|||
|
|||
typedef std::vector<RationalNumberType> Point; |
|||
typedef std::vector<RationalNumberType> WeightVector; |
|||
|
|||
static void check(Information& info); |
|||
|
|||
private: |
|||
SparseMultiObjectiveModelCheckerHelper(Information& info); |
|||
|
|||
~SparseMultiObjectiveModelCheckerHelper(); |
|||
|
|||
void achievabilityQuery(); |
|||
void numericalQuery(); |
|||
void paretoQuery(); |
|||
|
|||
|
|||
|
|||
/* |
|||
* Refines the solution w.r.t. the given direction vector |
|||
*/ |
|||
void refineSolution(WeightVector const& direction); |
|||
|
|||
/* |
|||
* Returns a weight vector w that separates the underapproximation from the given point p, i.e., |
|||
* For each x in the underApproximation, it holds that w*x <= w*p |
|||
* |
|||
* @param pointToBeSeparated the point that is to be seperated |
|||
* @param objectivesToBeCheckedIndividually stores for each objective whether it still makes sense to check for this objective individually (i.e., with weight vector given by w_{objIndex} = 1 ) |
|||
*/ |
|||
WeightVector findSeparatingVector(Point const& pointToBeSeparated, storm::storage::BitVector& individualObjectivesToBeChecked) const; |
|||
|
|||
void updateOverApproximation(Point const& newPoint, WeightVector const& newWeightVector); |
|||
void updateUnderApproximation(); |
|||
|
|||
/* |
|||
* Returns true iff there is a point in the given polytope that satisfies the given thresholds. |
|||
* It is assumed that the given polytope contains the downward closure of its vertices. |
|||
*/ |
|||
bool checkIfThresholdsAreSatisfied(std::shared_ptr<storm::storage::geometry::Polytope<RationalNumberType>> const& polytope, Point const& thresholds, storm::storage::BitVector const& strictThresholds) const; |
|||
|
|||
Information& info; |
|||
SparseWeightedObjectivesModelCheckerHelper<SparseModelType> weightedObjectivesChecker; |
|||
|
|||
//TODO: sort points as needed |
|||
std::map<Point, std::vector<WeightVector>> paretoOptimalPoints; |
|||
std::unordered_map<storm::storage::TotalScheduler, typename std::map<Point, std::vector<WeightVector>>::iterator> schedulers; |
|||
|
|||
uint_fast64_t numIterations; |
|||
|
|||
std::shared_ptr<storm::storage::geometry::Polytope<RationalNumberType>> overApproximation; |
|||
std::shared_ptr<storm::storage::geometry::Polytope<RationalNumberType>> underApproximation; |
|||
}; |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
#endif /* STORM_MODELCHECKER_MULTIOBJECTIVE_HELPER_SPARSEMULTIOBJECTIVEMODELCHECKERHELPER_H_ */ |
@ -0,0 +1,67 @@ |
|||
#ifndef STORM_MODELCHECKER_MULTIOBJECTIVE_HELPER_SPARSEMULTIOBJECTIVEPREPROCESSOR_H_ |
|||
#define STORM_MODELCHECKER_MULTIOBJECTIVE_HELPER_SPARSEMULTIOBJECTIVEPREPROCESSOR_H_ |
|||
|
|||
#include "src/logic/Formulas.h" |
|||
#include "src/modelchecker/multiobjective/helper/SparseMultiObjectivePreprocessorReturnType.h" |
|||
|
|||
namespace storm { |
|||
namespace modelchecker { |
|||
|
|||
|
|||
namespace helper { |
|||
|
|||
/* |
|||
* Helper Class to invoke the necessary preprocessing for multi objective model checking |
|||
*/ |
|||
template <class SparseModelType> |
|||
class SparseMultiObjectivePreprocessor { |
|||
public: |
|||
typedef typename SparseModelType::ValueType ValueType; |
|||
typedef typename SparseModelType::RewardModelType RewardModelType; |
|||
typedef SparseMultiObjectivePreprocessorReturnType<SparseModelType> ReturnType; |
|||
typedef typename SparseMultiObjectivePreprocessorReturnType<SparseModelType>::ObjectiveInformation ObjectiveInformation; |
|||
|
|||
/*! |
|||
* Preprocesses the given model w.r.t. the given formulas. |
|||
* @param originalModel The considered model |
|||
* @param originalFormula the considered formula. The subformulas should only contain one OperatorFormula at top level, i.e., the formula is simple. |
|||
*/ |
|||
static ReturnType preprocess(storm::logic::MultiObjectiveFormula const& originalFormula, SparseModelType const& originalModel); |
|||
|
|||
private: |
|||
|
|||
/*! |
|||
* Inserts the information regarding the given formula (i.e. objective) into info.objectives |
|||
* |
|||
* @param formula OperatorFormula representing the objective |
|||
* @param the information collected so far |
|||
*/ |
|||
static void addObjective(std::shared_ptr<storm::logic::Formula const> const& formula, ReturnType& data); |
|||
|
|||
/*! |
|||
* Sets the timeBound for the given objective |
|||
*/ |
|||
static void setStepBoundOfObjective(ObjectiveInformation& currentObjective); |
|||
|
|||
/*! |
|||
* Apply the neccessary preprocessing for the given formula. |
|||
* @param formula the current (sub)formula |
|||
* @param data the information collected so far |
|||
* @param currentObjective the currently considered objective. The given formula should be a a (sub)formula of this objective |
|||
* @param optionalRewardModelName the reward model name that is considered for the formula (if available) |
|||
*/ |
|||
static void preprocessFormula(storm::logic::ProbabilityOperatorFormula const& formula, ReturnType& info, ObjectiveInformation& currentObjective); |
|||
static void preprocessFormula(storm::logic::RewardOperatorFormula const& formula, ReturnType& info, ObjectiveInformation& currentObjective); |
|||
static void preprocessFormula(storm::logic::UntilFormula const& formula, ReturnType& info, ObjectiveInformation& currentObjective); |
|||
static void preprocessFormula(storm::logic::BoundedUntilFormula const& formula, ReturnType& info, ObjectiveInformation& currentObjective); |
|||
static void preprocessFormula(storm::logic::GloballyFormula const& formula, ReturnType& info, ObjectiveInformation& currentObjective); |
|||
static void preprocessFormula(storm::logic::EventuallyFormula const& formula, ReturnType& info, ObjectiveInformation& currentObjective, boost::optional<std::string> const& optionalRewardModelName = boost::none); |
|||
static void preprocessFormula(storm::logic::CumulativeRewardFormula const& formula, ReturnType& info, ObjectiveInformation& currentObjective, boost::optional<std::string> const& optionalRewardModelName = boost::none); |
|||
|
|||
}; |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
#endif /* STORM_MODELCHECKER_MULTIOBJECTIVE_HELPER_SPARSEMULTIOBJECTIVEPREPROCESSOR_H_ */ |
Write
Preview
Loading…
Cancel
Save
Reference in new issue