diff --git a/CMakeLists.txt b/CMakeLists.txt index 190f72228..5715063ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,8 @@ option(STORM_USE_COTIRE "Sets whether Cotire should be used (for building precom option(LINK_LIBCXXABI "Sets whether libc++abi should be linked." OFF) option(USE_LIBCXX "Sets whether the standard library is libc++." OFF) option(USE_CARL "Sets whether carl should be included." ON) +option(USE_SMTRAT "Sets whether SMT-RAT should be included." OFF) +option(USE_HYPRO "Sets whether HyPro should be included." OFF) option(XML_SUPPORT "Sets whether xml based format parsing should be included." ON) option(FORCE_COLOR "Force color output" OFF) mark_as_advanced(FORCE_COLOR) diff --git a/examples/fractions.sh b/examples/fractions.sh deleted file mode 100755 index 0dc02a5eb..000000000 --- a/examples/fractions.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -executable="timeout 3600 ../build/src/storm" -arguments=" -i 1000000 --parametric --parametricRegion" -mkdir fractions -# pdtmcs -$executable -s ./pdtmc/crowds/crowds.pm -const CrowdSize=15,TotalRuns=5 --prop ./pdtmc/crowds/crowds.prctl --region:regionfile ./pdtmc/crowds/crowds_regions.txt $arguments | tee ./fractions/pdtmc_crowds.pm-constCrowdSize_15_TotalRuns_5.log -$executable -s ./pdtmc/nand/nand.pm -const N=10,K=5 --prop ./pdtmc/nand/nand.prctl --region:regionfile ./pdtmc/nand/nand_regions.txt $arguments | tee ./fractions/pdtmc_nand.pm-constN_10_K_5.log -$executable -s ./pdtmc/brp_rewards2/brp_rewards2.pm -const N=256,MAX=5 --prop ./pdtmc/brp_rewards2/brp_rewards2.prctl --region:regionfile ./pdtmc/brp_rewards2/brp_rewards2_regions.txt $arguments | tee ./fractions/pdtmc_brp_rewards2.pm-constN_256_MAX_5.log -$executable -s ./pdtmc/brp_rewards4/brp_rewards4.pm -const N=256,MAX=5 --prop ./pdtmc/brp_rewards4/brp_rewards4.prctl --region:regionfile ./pdtmc/brp_rewards4/brp_rewards4_regions.txt $arguments | tee ./fractions/pdtmc_brp_rewards4.pm-constN_256_MAX_5.log - -# pmdps -$executable -s ./pmdp/brp/brp.pm -const N=256,MAX=5 --prop ./pmdp/brp/brp.prctl --region:regionfile ./pmdp/brp/brp_regions.txt $arguments | tee ./fractions/pmdp_brp.pm-constN_256_MAX_5.log -$executable -s ./pmdp/coin4/coin4.pm -const K=2 --prop ./pmdp/coin4/coin4.prctl --region:regionfile ./pmdp/coin4/coin4_regions.txt $arguments | tee ./fractions/pmdp_coin4.pm-constK_4.log -$executable -s ./pmdp/zeroconf/zeroconf.pm -const K=2 --prop ./pmdp/zeroconf/zeroconf.prctl --region:regionfile ./pmdp/zeroconf/zeroconf_regions.txt $arguments | tee ./fractions/pmdp_zeroconf.pm-constK_5.log -$executable -s ./pmdp/reporter4/reporter4.pm -const Xsize=6,Ysize=6,MAXTRIES=2,B=2 --prop ./pmdp/reporter4/reporter4.prctl --region:regionfile ./pmdp/reporter4/reporter4_regions.txt $arguments | tee ./fractions/pmdp_reporter4.pm-constXsize_6_Ysize_6_MAXTRIES_2_B_2.log -wait - -echo "done" diff --git a/examples/list.sh b/examples/list.sh deleted file mode 100755 index 284215ddc..000000000 --- a/examples/list.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash -executable="timeout 3600 ../build/src/storm" -arguments="-bisim -i 1000000 --parametric --parametricRegion --region:refinement 0.05 --region:samplemode off" -mkdir results -# pdtmcs -$executable -s ./pdtmc/crowds/crowds.pm -const CrowdSize=10,TotalRuns=5 --prop ./pdtmc/crowds/crowds.prctl --region:regions "0.000010<=PF<=0.999990,0.000010<=badC<=0.999990;" $arguments | tee ./results/pdtmc_crowds.pm-constCrowdSize_10_TotalRuns_5.log & -$executable -s ./pdtmc/crowds/crowds.pm -const CrowdSize=20,TotalRuns=10 --prop ./pdtmc/crowds/crowds.prctl --region:regions "0.000010<=PF<=0.999990,0.000010<=badC<=0.999990;" $arguments | tee ./results/pdtmc_crowds.pm-constCrowdSize_20_TotalRuns_10.log & - - -$executable -s ./pdtmc/nand/nand.pm -const N=10,K=5 --prop ./pdtmc/nand/nand.prctl --region:regions "0.000010<=perr<=0.999990,0.000010<=prob1<=0.999990;" $arguments | tee ./results/pdtmc_nand.pm-constN_10_K_5.log & -$executable -s ./pdtmc/nand/nand.pm -const N=25,K=5 --prop ./pdtmc/nand/nand.prctl --region:regions "0.000010<=perr<=0.999990,0.000010<=prob1<=0.999990;" $arguments | tee ./results/pdtmc_nand.pm-constN_25_K_5.log & - -wait - -$executable -s ./pdtmc/brp_rewards2/brp_rewards2.pm -const N=512,MAX=5 --prop ./pdtmc/brp_rewards2/brp_rewards2.prctl --region:regions "0.000010<=pL<=0.999990,0.000010<=pK<=0.999990;" $arguments | tee ./results/pdtmc_brp_rewards2.pm-constN_512_MAX_5.log & -$executable -s ./pdtmc/brp_rewards2/brp_rewards2.pm -const N=4096,MAX=5 --prop ./pdtmc/brp_rewards2/brp_rewards2.prctl --region:regions "0.000010<=pL<=0.999990,0.000010<=pK<=0.999990;" $arguments | tee ./results/pdtmc_brp_rewards2.pm-constN_4096_MAX_5.log & - - -$executable -s ./pdtmc/brp_rewards4/brp_rewards4.pm -const N=256,MAX=5 --prop ./pdtmc/brp_rewards4/brp_rewards4.prctl --region:regions "0.000010<=pL<=0.999990,0.000010<=pK<=0.999990,0.000010<=TOMsg<=0.999990,0.000010<=TOAck<=0.999990;" $arguments | tee ./results/pdtmc_brp_rewards4.pm-constN_256_MAX_5.log & -$executable -s ./pdtmc/brp_rewards4/brp_rewards4.pm -const N=5012,MAX=5 --prop ./pdtmc/brp_rewards4/brp_rewards4.prctl --region:regions "0.000010<=pL<=0.999990,0.000010<=pK<=0.999990,0.000010<=TOMsg<=0.999990,0.000010<=TOAck<=0.999990;" $arguments | tee ./results/pdtmc_brp_rewards4.pm-constN_5012_MAX_5.log & - -$executable -s ./pdtmc/brp/brp.pm -const N=256,MAX=5 --prop ./pdtmc/brp/brp.prctl --region:regions "0.000010<=pL<=0.999990,0.000010<=pK<=0.999990;" $arguments | tee ./results/pdtmc_brp.pm-constN_256_MAX_5.log & -$executable -s ./pdtmc/brp/brp.pm -const N=4096,MAX=5 --prop ./pdtmc/brp/brp.prctl --region:regions "0.000010<=pL<=0.999990,0.000010<=pK<=0.999990;" $arguments | tee ./results/pdtmc_brp.pm-constN_4096_MAX_5.log & - -wait - -# pmdps -arguments="-i 1000000 --parametric --parametricRegion --region:refinement 0.05 --region:samplemode off" - -$executable -s ./pmdp/brp/brp.pm -const N=256,MAX=5 --prop ./pmdp/brp/brp.prctl --region:regions "0.000010<=pL<=0.999990,0.000010<=pK<=0.999990;" $arguments | tee ./results/pmdp_brp.pm-constN_256_MAX_5.log & -$executable -s ./pmdp/brp/brp.pm -const N=4096,MAX=5 --prop ./pmdp/brp/brp.prctl --region:regions "0.000010<=pL<=0.999990,0.000010<=pK<=0.999990;" $arguments | tee ./results/pmdp_brp.pm-constN_4096_MAX_5.log & - -$executable -s ./pmdp/coin2/coin2.pm -const K=2 --prop ./pmdp/coin2/coin2.prctl --region:regions "0.000010<=p1<=0.999990,0.000010<=p2<=0.999990;" $arguments | tee ./results/pmdp_coin2.pm-constK_2.log & -$executable -s ./pmdp/coin2/coin2.pm -const K=32 --prop ./pmdp/coin2/coin2.prctl --region:regions "0.000010<=p1<=0.999990,0.000010<=p2<=0.999990;" $arguments | tee ./results/pmdp_coin2.pm-constK_32.log & - -$executable -s ./pmdp/coin4/coin4.pm -const K=2 --prop ./pmdp/coin4/coin4.prctl --region:regions "0.000010<=p1<=0.999990,0.000010<=p2<=0.999990,0.000010<=p3<=0.999990,0.000010<=p4<=0.999990;" $arguments | tee ./results/pmdp_coin4.pm-constK_2.log & -$executable -s ./pmdp/coin4/coin4.pm -const K=4 --prop ./pmdp/coin4/coin4.prctl --region:regions "0.000010<=p1<=0.999990,0.000010<=p2<=0.999990,0.000010<=p3<=0.999990,0.000010<=p4<=0.999990;" $arguments | tee ./results/pmdp_coin4.pm-constK_4.log & - -wait - -$executable -s ./pmdp/zeroconf/zeroconf.pm -const K=2 --prop ./pmdp/zeroconf/zeroconf.prctl --region:regions "0.000010<=loss<=0.999990,0.000010<=old<=0.999990;" $arguments | tee ./results/pmdp_zeroconf.pm-constK_2.log & -$executable -s ./pmdp/zeroconf/zeroconf.pm -const K=5 --prop ./pmdp/zeroconf/zeroconf.prctl --region:regions "0.000010<=loss<=0.999990,0.000010<=old<=0.999990;" $arguments | tee ./results/pmdp_zeroconf.pm-constK_5.log & - -$executable -s ./pmdp/reporter2/reporter2.pm -const Xsize=6,Ysize=6,MAXTRIES=2,B=2 --prop ./pmdp/reporter2/reporter2.prctl --region:regions "0.000010<=pL<=0.999990,0.000010<=pH<=0.999990;" $arguments | tee ./results/pmdp_reporter2.pm-constXsize_6_Ysize_6_MAXTRIES_2_B_2.log & -$executable -s ./pmdp/reporter2/reporter2.pm -const Xsize=100,Ysize=100,MAXTRIES=10,B=10 --prop ./pmdp/reporter2/reporter2.prctl --region:regions "0.000010<=pL<=0.999990,0.000010<=pH<=0.999990;" $arguments | tee ./results/pmdp_reporter2.pm-constXsize_100_Ysize_100_MAXTRIES_10_B_10.log & - -$executable -s ./pmdp/reporter4/reporter4.pm -const Xsize=6,Ysize=6,MAXTRIES=2,B=2 --prop ./pmdp/reporter4/reporter4.prctl --region:regions "0.000010<=pL<=0.999990,0.000010<=pH<=0.999990,0.000010<=pLDiff<=0.999990,0.000010<=pHDiff<=0.999990;" $arguments | tee ./results/pmdp_reporter4.pm-constXsize_6_Ysize_6_MAXTRIES_2_B_2.log & -$executable -s ./pmdp/reporter4/reporter4.pm -const Xsize=10,Ysize=10,MAXTRIES=3,B=3 --prop ./pmdp/reporter4/reporter4.prctl --region:regions "0.000010<=pL<=0.999990,0.000010<=pH<=0.999990,0.000010<=pLDiff<=0.999990,0.000010<=pHDiff<=0.999990;" $arguments | tee ./results/pmdp_reporter4.pm-constXsize_10_Ysize_10_MAXTRIES_3_B_3.log & - -wait -echo "done!" diff --git a/examples/list2.sh b/examples/list2.sh deleted file mode 100755 index ee3ea030c..000000000 --- a/examples/list2.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash -executable="timeout 3600 ../build/src/storm" -arguments="-i 1000000 --parametric --parametricRegion --region:refinement 0.05 --region:samplemode off" -resultfolder=res - -mkdir $resultfolder -# pdtmcs -$executable -s ./pdtmc/crowds/crowds.pm -const CrowdSize=10,TotalRuns=5 --prop ./pdtmc/crowds/crowds.prctl --region:regions "0.000010<=PF<=0.999990,0.000010<=badC<=0.999990;" $arguments | tee ./$resultfolder/pdtmc_crowds.pm-constCrowdSize_10_TotalRuns_5.log & -$executable -s ./pdtmc/crowds/crowds.pm -const CrowdSize=15,TotalRuns=5 --prop ./pdtmc/crowds/crowds.prctl --region:regions "0.000010<=PF<=0.999990,0.000010<=badC<=0.999990;" $arguments | tee ./$resultfolder/pdtmc_crowds.pm-constCrowdSize_15_TotalRuns_5.log & -$executable -s ./pdtmc/crowds/crowds.pm -const CrowdSize=15,TotalRuns=7 --prop ./pdtmc/crowds/crowds.prctl --region:regions "0.000010<=PF<=0.999990,0.000010<=badC<=0.999990;" $arguments | tee ./$resultfolder/pdtmc_crowds.pm-constCrowdSize_15_TotalRuns_7.log & -$executable -s ./pdtmc/crowds/crowds.pm -const CrowdSize=20,TotalRuns=5 --prop ./pdtmc/crowds/crowds.prctl --region:regions "0.000010<=PF<=0.999990,0.000010<=badC<=0.999990;" $arguments | tee ./$resultfolder/pdtmc_crowds.pm-constCrowdSize_20_TotalRuns_5.log & -wait -$executable -s ./pdtmc/crowds/crowds.pm -const CrowdSize=20,TotalRuns=7 --prop ./pdtmc/crowds/crowds.prctl --region:regions "0.000010<=PF<=0.999990,0.000010<=badC<=0.999990;" $arguments | tee ./$resultfolder/pdtmc_crowds.pm-constCrowdSize_20_TotalRuns_7.log & -$executable -s ./pdtmc/crowds/crowds.pm -const CrowdSize=20,TotalRuns=10 --prop ./pdtmc/crowds/crowds.prctl --region:regions "0.000010<=PF<=0.999990,0.000010<=badC<=0.999990;" $arguments | tee ./$resultfolder/pdtmc_crowds.pm-constCrowdSize_20_TotalRuns_10.log & - -$executable -s ./pdtmc/nand/nand.pm -const N=10,K=5 --prop ./pdtmc/nand/nand.prctl --region:regions "0.000010<=perr<=0.999990,0.000010<=prob1<=0.999990;" $arguments | tee ./$resultfolder/pdtmc_nand.pm-constN_10_K_5.log & -$executable -s ./pdtmc/nand/nand.pm -const N=25,K=5 --prop ./pdtmc/nand/nand.prctl --region:regions "0.000010<=perr<=0.999990,0.000010<=prob1<=0.999990;" $arguments | tee ./$resultfolder/pdtmc_nand.pm-constN_25_K_5.log & -wait - -$executable -s ./pdtmc/brp_rewards2/brp_rewards2.pm -const N=256,MAX=5 --prop ./pdtmc/brp_rewards2/brp_rewards2.prctl --region:regions "0.000010<=pL<=0.999990,0.000010<=pK<=0.999990;" $arguments | tee ./$resultfolder/pdtmc_brp_rewards2.pm-constN_256_MAX_5.log & -$executable -s ./pdtmc/brp_rewards2/brp_rewards2.pm -const N=512,MAX=5 --prop ./pdtmc/brp_rewards2/brp_rewards2.prctl --region:regions "0.000010<=pL<=0.999990,0.000010<=pK<=0.999990;" $arguments | tee ./$resultfolder/pdtmc_brp_rewards2.pm-constN_512_MAX_5.log & -$executable -s ./pdtmc/brp_rewards2/brp_rewards2.pm -const N=4096,MAX=5 --prop ./pdtmc/brp_rewards2/brp_rewards2.prctl --region:regions "0.000010<=pL<=0.999990,0.000010<=pK<=0.999990;" $arguments | tee ./$resultfolder/pdtmc_brp_rewards2.pm-constN_4096_MAX_5.log & - -wait - -$executable -s ./pdtmc/brp_rewards4/brp_rewards4.pm -const N=64,MAX=5 --prop ./pdtmc/brp_rewards4/brp_rewards4.prctl --region:regions "0.000010<=pL<=0.999990,0.000010<=pK<=0.999990,0.000010<=TOMsg<=0.999990,0.000010<=TOAck<=0.999990;" $arguments | tee ./$resultfolder/pdtmc_brp_rewards4.pm-constN_64_MAX_5.log & -$executable -s ./pdtmc/brp_rewards4/brp_rewards4.pm -const N=128,MAX=5 --prop ./pdtmc/brp_rewards4/brp_rewards4.prctl --region:regions "0.000010<=pL<=0.999990,0.000010<=pK<=0.999990,0.000010<=TOMsg<=0.999990,0.000010<=TOAck<=0.999990;" $arguments | tee ./$resultfolder/pdtmc_brp_rewards4.pm-constN_128_MAX_5.log & -$executable -s ./pdtmc/brp_rewards4/brp_rewards4.pm -const N=256,MAX=5 --prop ./pdtmc/brp_rewards4/brp_rewards4.prctl --region:regionso "0.000010<=pL<=0.999990,0.000010<=pK<=0.999990,0.000010<=TOMsg<=0.999990,0.000010<=TOAck<=0.999990;" $arguments | tee ./$resultfolder/pdtmc_brp_rewards4.pm-constN_256_MAX_5.log & -$executable -s ./pdtmc/brp_rewards4/brp_rewards4.pm -const N=5012,MAX=5 --prop ./pdtmc/brp_rewards4/brp_rewards4.prctl --region:regionso "0.000010<=pL<=0.999990,0.000010<=pK<=0.999990,0.000010<=TOMsg<=0.999990,0.000010<=TOAck<=0.999990;" $arguments | tee ./$resultfolder/pdtmc_brp_rewards4.pm-constN_5012_MAX_5.log & -wait - - -$executable -s ./pdtmc/brp/brp.pm -const N=256,MAX=5 --prop ./pdtmc/brp/brp.prctl --region:regions "0.000010<=pL<=0.999990,0.000010<=pK<=0.999990;" $arguments | tee ./$resultfolder/pdtmc_brp.pm-constN_256_MAX_5.log & -$executable -s ./pdtmc/brp/brp.pm -const N=4096,MAX=5 --prop ./pdtmc/brp/brp.prctl --region:regions "0.000010<=pL<=0.999990,0.000010<=pK<=0.999990;" $arguments | tee ./$resultfolder/pdtmc_brp.pm-constN_4096_MAX_5.log & - -wait - - -# New instances!!!!! (tested here also with bisim) - -arguments="-bisim -i 1000000 --parametric --parametricRegion --region:refinement 0.05 --region:samplemode off" -resultfolder=res_bisim -mkdir $resultfolder -$executable -s ./pdtmc/crowds/crowds.pm -const CrowdSize=15,TotalRuns=5 --prop ./pdtmc/crowds/crowds.prctl --region:regions "0.000010<=PF<=0.999990,0.000010<=badC<=0.999990;" $arguments | tee ./$resultfolder/pdtmc_crowds.pm-constCrowdSize_15_TotalRuns_5.log & -$executable -s ./pdtmc/crowds/crowds.pm -const CrowdSize=15,TotalRuns=7 --prop ./pdtmc/crowds/crowds.prctl --region:regions "0.000010<=PF<=0.999990,0.000010<=badC<=0.999990;" $arguments | tee ./$resultfolder/pdtmc_crowds.pm-constCrowdSize_15_TotalRuns_7.log & -$executable -s ./pdtmc/crowds/crowds.pm -const CrowdSize=20,TotalRuns=5 --prop ./pdtmc/crowds/crowds.prctl --region:regions "0.000010<=PF<=0.999990,0.000010<=badC<=0.999990;" $arguments | tee ./$resultfolder/pdtmc_crowds.pm-constCrowdSize_20_TotalRuns_5.log & -wait -$executable -s ./pdtmc/crowds/crowds.pm -const CrowdSize=20,TotalRuns=7 --prop ./pdtmc/crowds/crowds.prctl --region:regions "0.000010<=PF<=0.999990,0.000010<=badC<=0.999990;" $arguments | tee ./$resultfolder/pdtmc_crowds.pm-constCrowdSize_20_TotalRuns_7.log & -$executable -s ./pdtmc/crowds/crowds.pm -const CrowdSize=20,TotalRuns=10 --prop ./pdtmc/crowds/crowds.prctl --region:regions "0.000010<=PF<=0.999990,0.000010<=badC<=0.999990;" $arguments | tee ./$resultfolder/pdtmc_crowds.pm-constCrowdSize_20_TotalRuns_10.log & -wait - -$executable -s ./pdtmc/brp_rewards2/brp_rewards2.pm -const N=256,MAX=5 --prop ./pdtmc/brp_rewards2/brp_rewards2.prctl --region:regions "0.000010<=pL<=0.999990,0.000010<=pK<=0.999990;" $arguments | tee ./$resultfolder/pdtmc_brp_rewards2.pm-constN_256_MAX_5.log & - -$executable -s ./pdtmc/brp_rewards4/brp_rewards4.pm -const N=64,MAX=5 --prop ./pdtmc/brp_rewards4/brp_rewards4.prctl --region:regions "0.000010<=pL<=0.999990,0.000010<=pK<=0.999990,0.000010<=TOMsg<=0.999990,0.000010<=TOAck<=0.999990;" $arguments | tee ./$resultfolder/pdtmc_brp_rewards4.pm-constN_64_MAX_5.log & -$executable -s ./pdtmc/brp_rewards4/brp_rewards4.pm -const N=128,MAX=5 --prop ./pdtmc/brp_rewards4/brp_rewards4.prctl --region:regions "0.000010<=pL<=0.999990,0.000010<=pK<=0.999990,0.000010<=TOMsg<=0.999990,0.000010<=TOAck<=0.999990;" $arguments | tee ./$resultfolder/pdtmc_brp_rewards4.pm-constN_128_MAX_5.log & - - -wait - - -echo "done!" diff --git a/examples/listBenchmarks.sh b/examples/listBenchmarks.sh deleted file mode 100755 index 7bdf33279..000000000 --- a/examples/listBenchmarks.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash - -if [ "$#" != 1 ]; -then - echo "Wrong number of arguments! Provide a filename for the results!" -elif [ -a $1 ]; then - echo "File for results already exists!" -else - - -DIR="." - -echo '#!/bin/bash' >> $1 -echo 'executable="timeout 3600 ../build/src/storm"' >> $1 -echo 'arguments="-bisim -i 1000000 --parametric --parametricRegion --region:refinement 0.05 --region:samplemode off"' >> $1 -echo "mkdir results" >> $1 - - -declare -a modeltypes=("pdtmc" "pmdp") - -for modeltype in "${modeltypes[@]}" -do -if [ "$modeltype" == "pdtmc" ]; -then - declare -a models=("crowds" "nand" "brp_rewards2" "brp_rewards4" "brp") - dobisim="-bisim" -else - declare -a models=("brp" "coin2" "coin4" "zeroconf" "reporter2" "reporter4") - dobisim="" -fi - echo "# $modeltype""s" >> $1 - for model in "${models[@]}" - do - modelfolder="$DIR/$modeltype/$model" - suffix="-" - while read instance; - do - - output='$executable ' - output="$output""-s $modelfolder/$instance --prop $modelfolder/$model.prctl --region:regions " - region=$(head -n 1 $modelfolder/$model"_space.txt") - region="$(echo -e "${region}" | tr -d '[[:space:]]')" - output="$output"'"'$region'" $arguments | tee ' - instanceString="$(echo -e "${instance}" | tr -d '[[:space:]]')" - instanceString=${instanceString//[,=]/_} - output="$output""./results/$modeltype""_$instanceString.log &" - echo $output >> $1 - - done < "$modelfolder/models" - - done -done -echo 'wait' >> $1 -fi diff --git a/examples/multiobjective/display_exported_plot.tex b/examples/multiobjective/display_exported_plot.tex new file mode 100644 index 000000000..bc6f76c4a --- /dev/null +++ b/examples/multiobjective/display_exported_plot.tex @@ -0,0 +1,23 @@ +% This file can be used to display the exported plots from multi-objective model checking + +\documentclass{article} +\usepackage{pgfplots} +\usepackage{filecontents} + +\newcommand{\resultPath}{../ma/stream/results/} + +\begin{document} + \centering +\begin{tikzpicture}[scale=1.75] +\begin{axis}[ + enlargelimits=false, + axis background/.style={fill=red!50} + ] +\addplot[fill=white, very thin] table [col sep=comma] {\resultPath overapproximation.csv} -- cycle; +\addplot[fill=green, very thin] table [col sep=comma] {\resultPath underapproximation.csv} -- cycle; +\addplot[mark=o, mark options={blue, scale=1.3, thick}, only marks] table [col sep=comma] {\resultPath paretopoints.csv}; +\addplot[mark=false] table [col sep=comma] {\resultPath boundaries.csv}; +\end{axis} +\end{tikzpicture} + +\end{document} \ No newline at end of file diff --git a/examples/multiobjective/ma/mutex/mutex2.ma b/examples/multiobjective/ma/mutex/mutex2.ma new file mode 100644 index 000000000..296afa8ec --- /dev/null +++ b/examples/multiobjective/ma/mutex/mutex2.ma @@ -0,0 +1,93 @@ +// Translation of the MAPA Specification of a mutex system into PRISM code +// http://wwwhome.cs.utwente.nl/~timmer/scoop/papers/qest13/index.html + +ma + +const int N; // The size of the data (should be at most 6) + +formula someEnter = s1=1 | s2=1; +formula someWait = s1=2 | s2=2; +formula someLow = s1=3 | s2=3; +formula someHigh = s1=4 | s2=4; +formula someTie = s1=5 | s2=5; +formula someAdmit = s1=6 | s2=6; +formula otherHigh = s2=4; + +formula someLowTie = someLow | someTie; +formula someLowHighTie = someLow | someHigh | someTie; +formula someAdmitHighTie = someAdmit | someHigh | someTie; +formula someEnterWait = someEnter | someWait; + + +module process1 + + // The internal state of the process + // 0: uninterested + // 1: enter + // 2: wait + // 3: low + // 4: high + // 5: tie + // 6: admit + s1 : [0..6]; + + // the phase of the protocol + phase1 : [1..12]; + + // The considered data + data1 : [1..N]; + + // The result of a coin flip + h1 : bool; + + //[] phase1=1 -> 1 : true; + [] phase1=1 -> 1 : (phase1'=2); + + [] phase1=2 & N>=1 -> 1 : (data1'=1) & (phase1'=3); + [] phase1=2 & N>=2 -> 1 : (data1'=2) & (phase1'=3); + [] phase1=2 & N>=3 -> 1 : (data1'=3) & (phase1'=3); + [] phase1=2 & N>=4 -> 1 : (data1'=4) & (phase1'=3); + [] phase1=2 & N>=5 -> 1 : (data1'=5) & (phase1'=3); + [] phase1=2 & N>=6 -> 1 : (data1'=6) & (phase1'=3); + + [] phase1=3 & (someLowHighTie & !someAdmit) -> 1 : (s1'=2) & (phase1'=4); + [] phase1=3 & (!someLowHighTie | someAdmit) -> 0.5 : (phase1'=5) & (h1'=false) + 0.5 : (phase1'=5) & (h1'=true); + + [] phase1=4 & (!someLowHighTie | someAdmit) -> 1 : (s1'=1) & (phase1'=3); + + [] phase1=5 & h1=false -> 1 : (s1'=3) & (phase1'=6); + [] phase1=5 & h1=true -> 1 : (s1'=4) & (phase1'=7) & (h1'=false); + + [] phase1=6 & !someAdmitHighTie -> 1 : (s1'=5) & (phase1'=8); + + [] phase1=7 & (someAdmit | otherHigh) -> 1 : (s1'=5) & (phase1'=9); + [] phase1=7 & (!someAdmit & !otherHigh) -> 1 : (phase1'=10); + + [] phase1=8 -> 0.5 : (phase1'=5) & (h1'=false) + 0.5 : (phase1'=5) & (h1'=true); + + [] phase1=9 & !someAdmit & !otherHigh -> 0.5 : (phase1'=5) & (h1'=false) + 0.5 : (phase1'=5) & (h1'=true); + + <> phase1=10 -> data1 : (phase1'=11) & (data1'=1); + + [] phase1=11 & (someLowTie | otherHigh) & !someEnter -> 1 : (s1'=0) & (phase1'=1); + [] phase1=11 & !someLowTie & !otherHigh -> 1 : (s1'=6) & (phase1'=12); + + [] phase1=12 & !someEnterWait -> 1 : (s1'=0) & (phase1'=1); + +endmodule + +module process2 = process1 [ s1=s2, phase1=phase2, data1=data2, h1=h2, s2=s1] endmodule + +label "crit1" = phase1=10; +label "crit2" = phase2=10; + + +rewards "timeInCrit1" + phase1=10 : 1; +endrewards + + +rewards "timeInCrit2" + phase2=10 : 1; +endrewards + diff --git a/examples/multiobjective/ma/mutex/mutex3.ma b/examples/multiobjective/ma/mutex/mutex3.ma new file mode 100644 index 000000000..f47ef31f5 --- /dev/null +++ b/examples/multiobjective/ma/mutex/mutex3.ma @@ -0,0 +1,98 @@ +// Translation of the MAPA Specification of a mutex system into PRISM code +// http://wwwhome.cs.utwente.nl/~timmer/scoop/papers/qest13/index.html + +ma + +const int N; // The size of the data (should be at most 6) + +formula someEnter = s1=1 | s2=1 | s3=1; +formula someWait = s1=2 | s2=2 | s3=2; +formula someLow = s1=3 | s2=3 | s3=3; +formula someHigh = s1=4 | s2=4 | s3=4; +formula someTie = s1=5 | s2=5 | s3=5; +formula someAdmit = s1=6 | s2=6 | s3=6; +formula otherHigh = s2=4 | s3=4; + +formula someLowTie = someLow | someTie; +formula someLowHighTie = someLow | someHigh | someTie; +formula someAdmitHighTie = someAdmit | someHigh | someTie; +formula someEnterWait = someEnter | someWait; + + +module process1 + + // The internal state of the process + // 0: uninterested + // 1: enter + // 2: wait + // 3: low + // 4: high + // 5: tie + // 6: admit + s1 : [0..6]; + + // the phase of the protocol + phase1 : [1..12]; + + // The considered data + data1 : [1..N]; + + // The result of a coin flip + h1 : bool; + + //[] phase1=1 -> 1 : true; + [] phase1=1 -> 1 : (phase1'=2); + + [] phase1=2 & N>=1 -> 1 : (data1'=1) & (phase1'=3); + [] phase1=2 & N>=2 -> 1 : (data1'=2) & (phase1'=3); + [] phase1=2 & N>=3 -> 1 : (data1'=3) & (phase1'=3); + [] phase1=2 & N>=4 -> 1 : (data1'=4) & (phase1'=3); + [] phase1=2 & N>=5 -> 1 : (data1'=5) & (phase1'=3); + [] phase1=2 & N>=6 -> 1 : (data1'=6) & (phase1'=3); + + [] phase1=3 & (someLowHighTie & !someAdmit) -> 1 : (s1'=2) & (phase1'=4); + [] phase1=3 & (!someLowHighTie | someAdmit) -> 0.5 : (phase1'=5) & (h1'=false) + 0.5 : (phase1'=5) & (h1'=true); + + [] phase1=4 & (!someLowHighTie | someAdmit) -> 1 : (s1'=1) & (phase1'=3); + + [] phase1=5 & h1=false -> 1 : (s1'=3) & (phase1'=6); + [] phase1=5 & h1=true -> 1 : (s1'=4) & (phase1'=7) & (h1'=false); + + [] phase1=6 & !someAdmitHighTie -> 1 : (s1'=5) & (phase1'=8); + + [] phase1=7 & (someAdmit | otherHigh) -> 1 : (s1'=5) & (phase1'=9); + [] phase1=7 & (!someAdmit & !otherHigh) -> 1 : (phase1'=10); + + [] phase1=8 -> 0.5 : (phase1'=5) & (h1'=false) + 0.5 : (phase1'=5) & (h1'=true); + + [] phase1=9 & !someAdmit & !otherHigh -> 0.5 : (phase1'=5) & (h1'=false) + 0.5 : (phase1'=5) & (h1'=true); + + <> phase1=10 -> data1 : (phase1'=11) & (data1'=1); + + [] phase1=11 & (someLowTie | otherHigh) & !someEnter -> 1 : (s1'=0) & (phase1'=1); + [] phase1=11 & !someLowTie & !otherHigh -> 1 : (s1'=6) & (phase1'=12); + + [] phase1=12 & !someEnterWait -> 1 : (s1'=0) & (phase1'=1); + +endmodule + +module process2 = process1 [ s1=s2, phase1=phase2, data1=data2, h1=h2, s2=s1] endmodule +module process3 = process1 [ s1=s3, phase1=phase3, data1=data3, h1=h3, s3=s1] endmodule + +label "crit1" = phase1=10; +label "crit2" = phase2=10; +label "crit3" = phase3=10; + + +rewards "timeInCrit1" + phase1=10 : 1; +endrewards + +rewards "timeInCrit2" + phase2=10 : 1; +endrewards + +rewards "timeInCrit3" + phase3=10 : 1; +endrewards + diff --git a/examples/multiobjective/ma/mutex/mutex4.ma b/examples/multiobjective/ma/mutex/mutex4.ma new file mode 100644 index 000000000..8068933ef --- /dev/null +++ b/examples/multiobjective/ma/mutex/mutex4.ma @@ -0,0 +1,103 @@ +// Translation of the MAPA Specification of a mutex system into PRISM code +// http://wwwhome.cs.utwente.nl/~timmer/scoop/papers/qest13/index.html + +ma + +const int N; // The size of the data (should be at most 6) + +formula someEnter = s1=1 | s2=1 | s3=1 | s4=1; +formula someWait = s1=2 | s2=2 | s3=2 | s4=2; +formula someLow = s1=3 | s2=3 | s3=3 | s4=3; +formula someHigh = s1=4 | s2=4 | s3=4 | s4=4; +formula someTie = s1=5 | s2=5 | s3=5 | s4=5; +formula someAdmit = s1=6 | s2=6 | s3=6 | s4=6; +formula otherHigh = s2=4 | s3=4 | s4=4; + +formula someLowTie = someLow | someTie; +formula someLowHighTie = someLow | someHigh | someTie; +formula someAdmitHighTie = someAdmit | someHigh | someTie; +formula someEnterWait = someEnter | someWait; + + +module process1 + + // The internal state of the process + // 0: uninterested + // 1: enter + // 2: wait + // 3: low + // 4: high + // 5: tie + // 6: admit + s1 : [0..6]; + + // the phase of the protocol + phase1 : [1..12]; + + // The considered data + data1 : [1..N]; + + // The result of a coin flip + h1 : bool; + + //[] phase1=1 -> 1 : true; + [] phase1=1 -> 1 : (phase1'=2); + + [] phase1=2 & N>=1 -> 1 : (data1'=1) & (phase1'=3); + [] phase1=2 & N>=2 -> 1 : (data1'=2) & (phase1'=3); + [] phase1=2 & N>=3 -> 1 : (data1'=3) & (phase1'=3); + [] phase1=2 & N>=4 -> 1 : (data1'=4) & (phase1'=3); + [] phase1=2 & N>=5 -> 1 : (data1'=5) & (phase1'=3); + [] phase1=2 & N>=6 -> 1 : (data1'=6) & (phase1'=3); + + [] phase1=3 & (someLowHighTie & !someAdmit) -> 1 : (s1'=2) & (phase1'=4); + [] phase1=3 & (!someLowHighTie | someAdmit) -> 0.5 : (phase1'=5) & (h1'=false) + 0.5 : (phase1'=5) & (h1'=true); + + [] phase1=4 & (!someLowHighTie | someAdmit) -> 1 : (s1'=1) & (phase1'=3); + + [] phase1=5 & h1=false -> 1 : (s1'=3) & (phase1'=6); + [] phase1=5 & h1=true -> 1 : (s1'=4) & (phase1'=7) & (h1'=false); + + [] phase1=6 & !someAdmitHighTie -> 1 : (s1'=5) & (phase1'=8); + + [] phase1=7 & (someAdmit | otherHigh) -> 1 : (s1'=5) & (phase1'=9); + [] phase1=7 & (!someAdmit & !otherHigh) -> 1 : (phase1'=10); + + [] phase1=8 -> 0.5 : (phase1'=5) & (h1'=false) + 0.5 : (phase1'=5) & (h1'=true); + + [] phase1=9 & !someAdmit & !otherHigh -> 0.5 : (phase1'=5) & (h1'=false) + 0.5 : (phase1'=5) & (h1'=true); + + <> phase1=10 -> data1 : (phase1'=11) & (data1'=1); + + [] phase1=11 & (someLowTie | otherHigh) & !someEnter -> 1 : (s1'=0) & (phase1'=1); + [] phase1=11 & !someLowTie & !otherHigh -> 1 : (s1'=6) & (phase1'=12); + + [] phase1=12 & !someEnterWait -> 1 : (s1'=0) & (phase1'=1); + +endmodule + +module process2 = process1 [ s1=s2, phase1=phase2, data1=data2, h1=h2, s2=s1] endmodule +module process3 = process1 [ s1=s3, phase1=phase3, data1=data3, h1=h3, s3=s1] endmodule +module process4 = process1 [ s1=s4, phase1=phase4, data1=data4, h1=h4, s4=s1] endmodule + +label "crit1" = phase1=10; +label "crit2" = phase2=10; +label "crit3" = phase3=10; +label "crit4" = phase4=10; + + +rewards "timeInCrit1" + phase1=10 : 1; +endrewards + +rewards "timeInCrit2" + phase2=10 : 1; +endrewards + +rewards "timeInCrit3" + phase3=10 : 1; +endrewards + +rewards "timeInCrit4" + phase4=10 : 1; +endrewards diff --git a/examples/multiobjective/ma/mutex/mutex5.ma b/examples/multiobjective/ma/mutex/mutex5.ma new file mode 100644 index 000000000..2755d829c --- /dev/null +++ b/examples/multiobjective/ma/mutex/mutex5.ma @@ -0,0 +1,110 @@ +// Translation of the MAPA Specification of a mutex system into PRISM code +// http://wwwhome.cs.utwente.nl/~timmer/scoop/papers/qest13/index.html + +ma + +const int N; // The size of the data (should be at most 6) + +formula someEnter = s1=1 | s2=1 | s3=1 | s4=1 | s5=1; +formula someWait = s1=2 | s2=2 | s3=2 | s4=2 | s5=2; +formula someLow = s1=3 | s2=3 | s3=3 | s4=3 | s5=3; +formula someHigh = s1=4 | s2=4 | s3=4 | s4=4 | s5=4; +formula someTie = s1=5 | s2=5 | s3=5 | s4=5 | s5=5; +formula someAdmit = s1=6 | s2=6 | s3=6 | s4=6 | s5=6; +formula otherHigh = s2=4 | s3=4 | s4=4 | s5=4; + +formula someLowTie = someLow | someTie; +formula someLowHighTie = someLow | someHigh | someTie; +formula someAdmitHighTie = someAdmit | someHigh | someTie; +formula someEnterWait = someEnter | someWait; + +module process1 + + // The internal state of the process + // 0: uninterested + // 1: enter + // 2: wait + // 3: low + // 4: high + // 5: tie + // 6: admit + s1 : [0..6]; + + // the phase of the protocol + phase1 : [1..12]; + + // The considered data + data1 : [1..N]; + + // The result of a coin flip + h1 : bool; + + //[] phase1=1 -> 1 : true; + [] phase1=1 -> 1 : (phase1'=2); + + [] phase1=2 & N>=1 -> 1 : (data1'=1) & (phase1'=3); + [] phase1=2 & N>=2 -> 1 : (data1'=2) & (phase1'=3); + [] phase1=2 & N>=3 -> 1 : (data1'=3) & (phase1'=3); + [] phase1=2 & N>=4 -> 1 : (data1'=4) & (phase1'=3); + [] phase1=2 & N>=5 -> 1 : (data1'=5) & (phase1'=3); + [] phase1=2 & N>=6 -> 1 : (data1'=6) & (phase1'=3); + + [] phase1=3 & (someLowHighTie & !someAdmit) -> 1 : (s1'=2) & (phase1'=4); + [] phase1=3 & (!someLowHighTie | someAdmit) -> 0.5 : (phase1'=5) & (h1'=false) + 0.5 : (phase1'=5) & (h1'=true); + + [] phase1=4 & (!someLowHighTie | someAdmit) -> 1 : (s1'=1) & (phase1'=3); + + [] phase1=5 & h1=false -> 1 : (s1'=3) & (phase1'=6); + [] phase1=5 & h1=true -> 1 : (s1'=4) & (phase1'=7) & (h1'=false); + + [] phase1=6 & !someAdmitHighTie -> 1 : (s1'=5) & (phase1'=8); + + [] phase1=7 & (someAdmit | otherHigh) -> 1 : (s1'=5) & (phase1'=9); + [] phase1=7 & (!someAdmit & !otherHigh) -> 1 : (phase1'=10); + + [] phase1=8 -> 0.5 : (phase1'=5) & (h1'=false) + 0.5 : (phase1'=5) & (h1'=true); + + [] phase1=9 & !someAdmit & !otherHigh -> 0.5 : (phase1'=5) & (h1'=false) + 0.5 : (phase1'=5) & (h1'=true); + + <> phase1=10 -> data1 : (phase1'=11) & (data1'=1); + + [] phase1=11 & (someLowTie | otherHigh) & !someEnter -> 1 : (s1'=0) & (phase1'=1); + [] phase1=11 & !someLowTie & !otherHigh -> 1 : (s1'=6) & (phase1'=12); + + [] phase1=12 & !someEnterWait -> 1 : (s1'=0) & (phase1'=1); + +endmodule + +module process2 = process1 [ s1=s2, phase1=phase2, data1=data2, h1=h2, s2=s1] endmodule +module process3 = process1 [ s1=s3, phase1=phase3, data1=data3, h1=h3, s3=s1] endmodule +module process4 = process1 [ s1=s4, phase1=phase4, data1=data4, h1=h4, s4=s1] endmodule +module process5 = process1 [ s1=s5, phase1=phase5, data1=data5, h1=h5, s5=s1] endmodule + + +label "crit1" = phase1=10; +label "crit2" = phase2=10; +label "crit3" = phase3=10; +label "crit4" = phase4=10; +label "crit5" = phase5=10; + + +rewards "timeInCrit1" + phase1=10 : 1; +endrewards + +rewards "timeInCrit2" + phase2=10 : 1; +endrewards + +rewards "timeInCrit3" + phase3=10 : 1; +endrewards + +rewards "timeInCrit4" + phase4=10 : 1; +endrewards + +rewards "timeInCrit5" + phase5=10 : 1; +endrewards + diff --git a/examples/multiobjective/ma/polling/polling.ma b/examples/multiobjective/ma/polling/polling.ma new file mode 100644 index 000000000..5c8a45ad8 --- /dev/null +++ b/examples/multiobjective/ma/polling/polling.ma @@ -0,0 +1,101 @@ +// Translation of the MAPA Specification of a polling system into PRISM code +// http://wwwhome.cs.utwente.nl/~timmer/scoop/papers/qest13/index.html + +ma + +const int N; // number of job types (should be at most 6) +const int Q; // Maximum queue size in each station + +// Formulae to control the LIFO queue of the stations. +// The queue is represented by some integer whose base N representation has at most Q digits, each representing one of the job types 0, 1, ..., N-1. +// In addition, we store the current size of the queue which is needed to distinguish an empty queue from a queue holding job of type 0 +formula queue1_empty = q1Size=0; +formula queue1_full = q1Size=Q; +formula queue1_pop = floor(q1/N); +formula queue1_head = q1 - (queue1_pop * N); // i.e. q1 modulo N +formula queue1_push = q1*N; +formula queue2_empty = q2Size=0; +formula queue2_full = q2Size=Q; +formula queue2_pop = floor(q2/N); +formula queue2_head = q2 - (queue2_pop * N); // i.e. q2 modulo N +formula queue2_push = q2*N; + +const int queue_maxValue = (N^Q)-1; + +const double inRate1 = 3; // = (2 * #station) + 1; +const double inRate2 = 5; // = (2 * #station) + 1; + +module pollingsys + // The queues for the stations + q1 : [0..queue_maxValue]; + q1Size : [0..Q]; + q2 : [0..queue_maxValue]; + q2Size : [0..Q]; + + // Store the job that is currently processed by the server. j=N means that no job is processed. + j : [0..N] init N; + + // Flag indicating whether a new job arrived + newJob1 : bool init false; + newJob2 : bool init false; + + //<> !newJob1 & !newJob2 & !queue1_full & queue2_full & j=N -> inRate1 : (newJob1'=true); + //<> !newJob1 & !newJob2 & queue1_full & !queue2_full & j=N -> inRate2 : (newJob2'=true); + <> !newJob1 & !newJob2 & !queue1_full & !queue2_full & j=N -> inRate1 : (newJob1'=true) + inRate2 : (newJob2'=true); + <> !newJob1 & !newJob2 & queue1_full & queue2_full & j 2*(j+1) : (j'=N); + <> !newJob1 & !newJob2 & !queue1_full & queue2_full & j inRate1 : (newJob1'=true) + 2*(j+1) : (j'=N); + <> !newJob1 & !newJob2 & queue1_full & !queue2_full & j inRate2 : (newJob2'=true) + 2*(j+1) : (j'=N); + <> !newJob1 & !newJob2 & !queue1_full & !queue2_full & j inRate1 : (newJob1'=true) + inRate2 : (newJob2'=true) + 2*(j+1) : (j'=N); + + [] newJob1 & N>=1 -> 1 : (q1Size'=q1Size+1) & (q1'=queue1_push+0) & (newJob1'=false); + [] newJob1 & N>=2 -> 1 : (q1Size'=q1Size+1) & (q1'=queue1_push+1) & (newJob1'=false); + [] newJob1 & N>=3 -> 1 : (q1Size'=q1Size+1) & (q1'=queue1_push+2) & (newJob1'=false); + [] newJob1 & N>=4 -> 1 : (q1Size'=q1Size+1) & (q1'=queue1_push+3) & (newJob1'=false); + [] newJob1 & N>=5 -> 1 : (q1Size'=q1Size+1) & (q1'=queue1_push+4) & (newJob1'=false); + [] newJob1 & N>=6 -> 1 : (q1Size'=q1Size+1) & (q1'=queue1_push+5) & (newJob1'=false); + + [] newJob2 & N>=1 -> 1 : (q2Size'=q2Size+1) & (q2'=queue2_push+0) & (newJob2'=false); + [] newJob2 & N>=2 -> 1 : (q2Size'=q2Size+1) & (q2'=queue2_push+1) & (newJob2'=false); + [] newJob2 & N>=3 -> 1 : (q2Size'=q2Size+1) & (q2'=queue2_push+2) & (newJob2'=false); + [] newJob2 & N>=4 -> 1 : (q2Size'=q2Size+1) & (q2'=queue2_push+3) & (newJob2'=false); + [] newJob2 & N>=5 -> 1 : (q2Size'=q2Size+1) & (q2'=queue2_push+4) & (newJob2'=false); + [] newJob2 & N>=6 -> 1 : (q2Size'=q2Size+1) & (q2'=queue2_push+5) & (newJob2'=false); + + [copy1] !newJob1 & !newJob2 & !queue1_empty & j=N -> 0.9 : (j'=queue1_head) & (q1Size'=q1Size-1) & (q1'=queue1_pop) + 0.1 : (j'=queue1_head); + [copy2] !newJob1 & !newJob2 & !queue2_empty & j=N -> 0.9 : (j'=queue2_head) & (q2Size'=q2Size-1) & (q2'=queue2_pop) + 0.1 : (j'=queue2_head); + +endmodule + + + +label "q1full" = q1Size=Q; +label "q2full" = q2Size=Q; +label "allqueuesfull" = q1Size=Q & q2Size=Q; + + +// Rewards adapted from Guck et al.: Modelling and Analysis of Markov Reward Automata + +rewards "processedjobs1" + [copy1] true : 0.1; +endrewards + +rewards "processedjobs2" + [copy1] true : 0.1; +endrewards + +rewards "processedjobs" + [copy1] true : 1; + [copy2] true : 1; +endrewards + +rewards "waiting1" + true : (q1Size); +endrewards + +rewards "waiting2" + true : (q2Size); +endrewards + +rewards "waiting" + true : (q1Size + q2Size); +endrewards \ No newline at end of file diff --git a/examples/multiobjective/ma/server/server.csl b/examples/multiobjective/ma/server/server.csl new file mode 100644 index 000000000..41a74ec79 --- /dev/null +++ b/examples/multiobjective/ma/server/server.csl @@ -0,0 +1 @@ +multi(Tmax=? [ F "error" ], Pmax=? [ F "processB" ]) diff --git a/examples/multiobjective/ma/server/server.ma b/examples/multiobjective/ma/server/server.ma new file mode 100644 index 000000000..d82c32474 --- /dev/null +++ b/examples/multiobjective/ma/server/server.ma @@ -0,0 +1,34 @@ + +ma + +const double rateProcessing = 2; +const double rateA = 1; +const double rateB = 1; + +module server + + s : [0..5]; // current state: + // 0: wait for request + // 1: received request from A + // 2: received request from B + // 3: starting to process request of B + // 4: processing request + // 5: error + + + + <> s=0 -> rateA : (s'=1) + rateB : (s'=2); + [alpha] s=1 -> 1 : (s'=4); + [alpha] s=2 -> 1 : (s'=3); + [beta] s=2 -> 0.5 : (s'=0) + 0.5 : (s'=3); + [] s=3 -> 1 : (s'=4); + <> s=4 -> rateProcessing : (s'=0) + (rateA+rateB) : (s'=5); + <> s=5 -> 1 : true; + +endmodule + + +label "error" = (s=5); +label "processB" = (s=3); + + diff --git a/examples/multiobjective/ma/simple/simple.csl b/examples/multiobjective/ma/simple/simple.csl new file mode 100644 index 000000000..75452f1a2 --- /dev/null +++ b/examples/multiobjective/ma/simple/simple.csl @@ -0,0 +1 @@ +multi(Pmax=? [ F s=3 ], Pmax=? [ F s=4 ]) diff --git a/examples/multiobjective/ma/simple/simple.ma b/examples/multiobjective/ma/simple/simple.ma new file mode 100644 index 000000000..92a003555 --- /dev/null +++ b/examples/multiobjective/ma/simple/simple.ma @@ -0,0 +1,15 @@ + +ma + +module simple + + s : [0..4]; + + + [alpha] (s=0) -> 1 : (s' = 1); + [beta] (s=0) -> 0.8 : (s'=0) + 0.2 : (s'=2); + <> (s=1) -> 9 : (s'=0) + 1 : (s'=3); + <> (s=2) -> 12 : (s'=4); + <> (s>2) -> 1 : true; + +endmodule \ No newline at end of file diff --git a/examples/multiobjective/ma/stream/stream.ma b/examples/multiobjective/ma/stream/stream.ma new file mode 100644 index 000000000..9a657dd04 --- /dev/null +++ b/examples/multiobjective/ma/stream/stream.ma @@ -0,0 +1,45 @@ + +ma + +const int N; // num packages + +const double inRate = 4; +const double processingRate = 4; + +module streamingclient + + s : [0..3]; // current state: + // 0: decide whether to start + // 1: buffering + // 2: running + // 3: success + + n : [0..N]; // number of received packages + k : [0..N]; // number of processed packages + + [buffer] s=0 & n 1 : (s'=1); + [buffer] s=0 & n 0.99: (s'=1) + 0.01 : (s'=2) & (k'=k+1); + [start] s=0 & k 1 : (s'=2) & (k'=k+1); + + <> s=1 -> inRate : (n'=n+1) & (s'=0); + + <> s=2 & n inRate : (n'=n+1) + processingRate : (k'=k+1); + <> s=2 & n inRate : (n'=n+1) + processingRate : (s'=0); + <> s=2 & n=N & k processingRate : (k'=k+1); + <> s=2 & n=N & k=N -> processingRate : (s'=3); + + <> s=3 -> 1 : true; +endmodule + + +label "underrun" = (s=0 & k>0); +label "running" = (s=2); +label "done" = (s=3); + +rewards "buffering" + s=1 : 1; +endrewards + +rewards "numrestarts" + [start] k > 0 : 1; +endrewards diff --git a/examples/multiobjective/ma/stream/stream_bounded_pareto.csl b/examples/multiobjective/ma/stream/stream_bounded_pareto.csl new file mode 100644 index 000000000..cdb98287f --- /dev/null +++ b/examples/multiobjective/ma/stream/stream_bounded_pareto.csl @@ -0,0 +1 @@ +multi(Pmax=? [ F<=3.5 "done" ], Pmax=? [ F<=1 s=2 ]) diff --git a/examples/multiobjective/ma/stream/stream_mixed_pareto.csl b/examples/multiobjective/ma/stream/stream_mixed_pareto.csl new file mode 100644 index 000000000..8b4085015 --- /dev/null +++ b/examples/multiobjective/ma/stream/stream_mixed_pareto.csl @@ -0,0 +1,2 @@ +multi(Pmax=? [ F<=2.5 s=2], R{"numrestarts"}min=? [ F "done"]) +// best looking on stream50 diff --git a/examples/multiobjective/ma/stream/stream_unbounded_pareto.csl b/examples/multiobjective/ma/stream/stream_unbounded_pareto.csl new file mode 100644 index 000000000..12e469030 --- /dev/null +++ b/examples/multiobjective/ma/stream/stream_unbounded_pareto.csl @@ -0,0 +1 @@ +multi(R{"initialbuffering"}min=? [ F "done" ], R{"numRestarts"}min=? [ F "done" ]) diff --git a/examples/multiobjective/mdp/consensus/consensus2_3_2.nm b/examples/multiobjective/mdp/consensus/consensus2_3_2.nm new file mode 100644 index 000000000..8b602a4d7 --- /dev/null +++ b/examples/multiobjective/mdp/consensus/consensus2_3_2.nm @@ -0,0 +1,88 @@ +// model of randomised consensus + +mdp + +const int N = 2; // num processes +const int MAX = 3; // num rounds (R) +const int K = 2; // Parameter for coins + +// need to turn these into local copies later so the reading phase is complete? +formula leaders_agree1 = (p1=1 | r1 (p1'=1) & (r1'=1); + [] s1=0 & r1=0 -> (p1'=2) & (r1'=1); + + // read registers (currently does nothing because read vs from other processes + [] s1=0 & r1>0 & r1<=MAX -> (s1'=1); + // maxke a decision + [] s1=1 & decide1 -> (s1'=4) & (p1'=1); + [] s1=1 & decide2 -> (s1'=4) & (p1'=2); + [] s1=1 & r1 (s1'=0) & (p1'=1) & (r1'=r1+1); + [] s1=1 & r1 (s1'=0) & (p1'=2) & (r1'=r1+1); + [] s1=1 & r1 (s1'=2) & (p1'=0); + [] s1=1 & r1=MAX & !(decide1 | decide2) -> (s1'=5); // run out of rounds so error + // enter the coin procotol for the current round + [coin1_s1_start] s1=2 & r1=1 -> (s1'=3); + [coin2_s1_start] s1=2 & r1=2 -> (s1'=3); + // get response from the coin protocol + [coin1_s1_p1] s1=3 & r1=1 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin1_s1_p2] s1=3 & r1=1 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + [coin2_s1_p1] s1=3 & r1=2 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin2_s1_p2] s1=3 & r1=2 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + // done so loop + [done] s1>=4 -> true; + +endmodule + +module process2 = process1[ s1=s2, + p1=p2,p2=p1, + r1=r2,r2=r1, + coin1_s1_start=coin1_s2_start,coin2_s1_start=coin2_s2_start, + coin1_s1_p1=coin1_s2_p1,coin2_s1_p1=coin2_s2_p1, + coin1_s1_p2=coin1_s2_p2,coin2_s1_p2=coin2_s2_p2 ] +endmodule + +module coin1_error + + c1 : [0..1]; // 1 is the error state + v1 : [0..2]; // value of the coin returned the first time + + // first returned value (any processes) + [coin1_s1_p1] v1=0 -> (v1'=1); + [coin1_s2_p1] v1=0 -> (v1'=1); + [coin1_s1_p2] v1=0 -> (v1'=2); + [coin1_s2_p2] v1=0 -> (v1'=2); + // later values returned + [coin1_s1_p1] v1=1 -> true; // good behaviour + [coin1_s2_p1] v1=1 -> true; // good behaviour + [coin1_s1_p2] v1=2 -> true; // good behaviour + [coin1_s2_p2] v1=2 -> true; // good behaviour + [coin1_s1_p1] v1=2 -> (c1'=1); // error + [coin1_s2_p1] v1=2 -> (c1'=1); // error + [coin1_s1_p2] v1=1 -> (c1'=1); // error + [coin1_s2_p2] v1=1 -> (c1'=1); // error + +endmodule + +// coins 2 and 3 are of no use as there are not enough rounds afterwards to decide + +// Labels +label "one_proc_err" = (s1=5 | s2=5); +label "one_coin_ok" = (c1=0); diff --git a/examples/multiobjective/mdp/consensus/consensus2_3_2_numerical.pctl b/examples/multiobjective/mdp/consensus/consensus2_3_2_numerical.pctl new file mode 100644 index 000000000..f7bb97443 --- /dev/null +++ b/examples/multiobjective/mdp/consensus/consensus2_3_2_numerical.pctl @@ -0,0 +1 @@ +multi(Pmax=? [ F "one_proc_err" ], P>=0.8916673903 [ G "one_coin_ok" ]) diff --git a/examples/multiobjective/mdp/consensus/consensus2_3_2_pareto.pctl b/examples/multiobjective/mdp/consensus/consensus2_3_2_pareto.pctl new file mode 100644 index 000000000..7cd298d8e --- /dev/null +++ b/examples/multiobjective/mdp/consensus/consensus2_3_2_pareto.pctl @@ -0,0 +1 @@ +multi(Pmax=? [ F "one_proc_err" ], Pmax=? [ G "one_coin_ok" ]) diff --git a/examples/multiobjective/mdp/consensus/consensus2_4_2.nm b/examples/multiobjective/mdp/consensus/consensus2_4_2.nm new file mode 100644 index 000000000..02724ba6e --- /dev/null +++ b/examples/multiobjective/mdp/consensus/consensus2_4_2.nm @@ -0,0 +1,114 @@ +// model of randomised consensus + +mdp + +const int N = 2; // num processes +const int MAX = 4; // num rounds (R) +const int K = 2; // Parameter for coins + +// need to turn these into local copies later so the reading phase is complete? +formula leaders_agree1 = (p1=1 | r1 (p1'=1) & (r1'=1); + [] s1=0 & r1=0 -> (p1'=2) & (r1'=1); + + // read registers (currently does nothing because read vs from other processes + [] s1=0 & r1>0 & r1<=MAX -> (s1'=1); + // maxke a decision + [] s1=1 & decide1 -> (s1'=4) & (p1'=1); + [] s1=1 & decide2 -> (s1'=4) & (p1'=2); + [] s1=1 & r1 (s1'=0) & (p1'=1) & (r1'=r1+1); + [] s1=1 & r1 (s1'=0) & (p1'=2) & (r1'=r1+1); + [] s1=1 & r1 (s1'=2) & (p1'=0); + [] s1=1 & r1=MAX & !(decide1 | decide2) -> (s1'=5); // run out of rounds so error + // enter the coin procotol for the current round + [coin1_s1_start] s1=2 & r1=1 -> (s1'=3); + [coin2_s1_start] s1=2 & r1=2 -> (s1'=3); + [coin3_s1_start] s1=2 & r1=3 -> (s1'=3); + // get response from the coin protocol + [coin1_s1_p1] s1=3 & r1=1 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin1_s1_p2] s1=3 & r1=1 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + [coin2_s1_p1] s1=3 & r1=2 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin2_s1_p2] s1=3 & r1=2 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + [coin3_s1_p1] s1=3 & r1=3 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin3_s1_p2] s1=3 & r1=3 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + // done so loop + [done] s1>=4 -> true; + +endmodule + +module process2 = process1[ s1=s2, + p1=p2,p2=p1, + r1=r2,r2=r1, + coin1_s1_start=coin1_s2_start,coin2_s1_start=coin2_s2_start,coin3_s1_start=coin3_s2_start, + coin1_s1_p1=coin1_s2_p1,coin2_s1_p1=coin2_s2_p1,coin3_s1_p1=coin3_s2_p1, + coin1_s1_p2=coin1_s2_p2,coin2_s1_p2=coin2_s2_p2,coin3_s1_p2=coin3_s2_p2 ] +endmodule + +module coin1_error + + c1 : [0..1]; // 1 is the error state + v1 : [0..2]; // value of the coin returned the first time + + // first returned value (any processes) + [coin1_s1_p1] v1=0 -> (v1'=1); + [coin1_s2_p1] v1=0 -> (v1'=1); + [coin1_s1_p2] v1=0 -> (v1'=2); + [coin1_s2_p2] v1=0 -> (v1'=2); + // later values returned + [coin1_s1_p1] v1=1 -> true; // good behaviour + [coin1_s2_p1] v1=1 -> true; // good behaviour + [coin1_s1_p2] v1=2 -> true; // good behaviour + [coin1_s2_p2] v1=2 -> true; // good behaviour + [coin1_s1_p1] v1=2 -> (c1'=1); // error + [coin1_s2_p1] v1=2 -> (c1'=1); // error + [coin1_s1_p2] v1=1 -> (c1'=1); // error + [coin1_s2_p2] v1=1 -> (c1'=1); // error + +endmodule + +// could do with renaming +module coin2_error + + c2 : [0..1]; // 1 is the error state + v2 : [0..2]; // value of the coin returned the first time + + // first returned value (any processes) + [coin2_s1_p1] v2=0 -> (v2'=1); + [coin2_s2_p1] v2=0 -> (v2'=1); + [coin2_s1_p2] v2=0 -> (v2'=2); + [coin2_s2_p2] v2=0 -> (v2'=2); + // later values returned + [coin2_s1_p1] v2=1 -> true; // good behaviour + [coin2_s2_p1] v2=1 -> true; // good behaviour + [coin2_s1_p2] v2=2 -> true; // good behaviour + [coin2_s2_p2] v2=2 -> true; // good behaviour + [coin2_s1_p1] v2=2 -> (c2'=1); // error + [coin2_s2_p1] v2=2 -> (c2'=1); // error + [coin2_s1_p2] v2=1 -> (c2'=1); // error + [coin2_s2_p2] v2=1 -> (c2'=1); // error + +endmodule + +// coin 3 is of no use because of number of rounds + +// Labels +label "one_proc_err" = (s1=5 | s2=5); +label "one_coin_ok" = (c1=0 | c2=0); diff --git a/examples/multiobjective/mdp/consensus/consensus2_4_2_numerical.pctl b/examples/multiobjective/mdp/consensus/consensus2_4_2_numerical.pctl new file mode 100644 index 000000000..62c7737ac --- /dev/null +++ b/examples/multiobjective/mdp/consensus/consensus2_4_2_numerical.pctl @@ -0,0 +1 @@ +multi(Pmax=? [ F "one_proc_err" ], P>=0.9882640457 [ G "one_coin_ok" ]) diff --git a/examples/multiobjective/mdp/consensus/consensus2_4_2_pareto.pctl b/examples/multiobjective/mdp/consensus/consensus2_4_2_pareto.pctl new file mode 100644 index 000000000..7cd298d8e --- /dev/null +++ b/examples/multiobjective/mdp/consensus/consensus2_4_2_pareto.pctl @@ -0,0 +1 @@ +multi(Pmax=? [ F "one_proc_err" ], Pmax=? [ G "one_coin_ok" ]) diff --git a/examples/multiobjective/mdp/consensus/consensus2_5_2.nm b/examples/multiobjective/mdp/consensus/consensus2_5_2.nm new file mode 100644 index 000000000..288579d38 --- /dev/null +++ b/examples/multiobjective/mdp/consensus/consensus2_5_2.nm @@ -0,0 +1,140 @@ +// model of randomised consensus + +mdp + +const int N = 2; // num processes +const int MAX = 5; // num rounds (R) +const int K = 2; // Parameter for coins + +// need to turn these into local copies later so the reading phase is complete? +formula leaders_agree1 = (p1=1 | r1 (p1'=1) & (r1'=1); + [] s1=0 & r1=0 -> (p1'=2) & (r1'=1); + + // read registers (currently does nothing because read vs from other processes + [] s1=0 & r1>0 & r1<=MAX -> (s1'=1); + // maxke a decision + [] s1=1 & decide1 -> (s1'=4) & (p1'=1); + [] s1=1 & decide2 -> (s1'=4) & (p1'=2); + [] s1=1 & r1 (s1'=0) & (p1'=1) & (r1'=r1+1); + [] s1=1 & r1 (s1'=0) & (p1'=2) & (r1'=r1+1); + [] s1=1 & r1 (s1'=2) & (p1'=0); + [] s1=1 & r1=MAX & !(decide1 | decide2) -> (s1'=5); // run out of rounds so error + // enter the coin procotol for the current round + [coin1_s1_start] s1=2 & r1=1 -> (s1'=3); + [coin2_s1_start] s1=2 & r1=2 -> (s1'=3); + [coin3_s1_start] s1=2 & r1=3 -> (s1'=3); + [coin4_s1_start] s1=2 & r1=4 -> (s1'=3); + // get response from the coin protocol + [coin1_s1_p1] s1=3 & r1=1 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin1_s1_p2] s1=3 & r1=1 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + [coin2_s1_p1] s1=3 & r1=2 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin2_s1_p2] s1=3 & r1=2 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + [coin3_s1_p1] s1=3 & r1=3 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin3_s1_p2] s1=3 & r1=3 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + [coin4_s1_p1] s1=3 & r1=4 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin4_s1_p2] s1=3 & r1=4 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + // done so loop + [done] s1>=4 -> true; + +endmodule + +module process2 = process1[ s1=s2, + p1=p2,p2=p1, + r1=r2,r2=r1, + coin1_s1_start=coin1_s2_start,coin2_s1_start=coin2_s2_start,coin3_s1_start=coin3_s2_start,coin4_s1_start=coin4_s2_start, + coin1_s1_p1=coin1_s2_p1,coin2_s1_p1=coin2_s2_p1,coin3_s1_p1=coin3_s2_p1,coin4_s1_p1=coin4_s2_p1, + coin1_s1_p2=coin1_s2_p2,coin2_s1_p2=coin2_s2_p2,coin3_s1_p2=coin3_s2_p2,coin4_s1_p2=coin4_s2_p2 ] +endmodule + +module coin1_error + + c1 : [0..1]; // 1 is the error state + v1 : [0..2]; // value of the coin returned the first time + + // first returned value (any processes) + [coin1_s1_p1] v1=0 -> (v1'=1); + [coin1_s2_p1] v1=0 -> (v1'=1); + [coin1_s1_p2] v1=0 -> (v1'=2); + [coin1_s2_p2] v1=0 -> (v1'=2); + // later values returned + [coin1_s1_p1] v1=1 -> true; // good behaviour + [coin1_s2_p1] v1=1 -> true; // good behaviour + [coin1_s1_p2] v1=2 -> true; // good behaviour + [coin1_s2_p2] v1=2 -> true; // good behaviour + [coin1_s1_p1] v1=2 -> (c1'=1); // error + [coin1_s2_p1] v1=2 -> (c1'=1); // error + [coin1_s1_p2] v1=1 -> (c1'=1); // error + [coin1_s2_p2] v1=1 -> (c1'=1); // error + +endmodule + +// could do with renaming +module coin2_error + + c2 : [0..1]; // 1 is the error state + v2 : [0..2]; // value of the coin returned the first time + + // first returned value (any processes) + [coin2_s1_p1] v2=0 -> (v2'=1); + [coin2_s2_p1] v2=0 -> (v2'=1); + [coin2_s1_p2] v2=0 -> (v2'=2); + [coin2_s2_p2] v2=0 -> (v2'=2); + // later values returned + [coin2_s1_p1] v2=1 -> true; // good behaviour + [coin2_s2_p1] v2=1 -> true; // good behaviour + [coin2_s1_p2] v2=2 -> true; // good behaviour + [coin2_s2_p2] v2=2 -> true; // good behaviour + [coin2_s1_p1] v2=2 -> (c2'=1); // error + [coin2_s2_p1] v2=2 -> (c2'=1); // error + [coin2_s1_p2] v2=1 -> (c2'=1); // error + [coin2_s2_p2] v2=1 -> (c2'=1); // error + +endmodule + +// could do with renaming +module coin3_error + + c3 : [0..1]; // 1 is the error state + v3 : [0..2]; // value of the coin returned the first time + + // first returned value (any processes) + [coin3_s1_p1] v3=0 -> (v3'=1); + [coin3_s2_p1] v3=0 -> (v3'=1); + [coin3_s1_p2] v3=0 -> (v3'=2); + [coin3_s2_p2] v3=0 -> (v3'=2); + // later values returned + [coin3_s1_p1] v3=1 -> true; // good behaviour + [coin3_s2_p1] v3=1 -> true; // good behaviour + [coin3_s1_p2] v3=2 -> true; // good behaviour + [coin3_s2_p2] v3=2 -> true; // good behaviour + [coin3_s1_p1] v3=2 -> (c3'=1); // error + [coin3_s2_p1] v3=2 -> (c3'=1); // error + [coin3_s1_p2] v3=1 -> (c3'=1); // error + [coin3_s2_p2] v3=1 -> (c3'=1); // error + +endmodule + +// coin 4 is of no use because of number of rounds + +// Labels +label "one_proc_err" = (s1=5 | s2=5); +label "one_coin_ok" = (c1=0 | c2=0 | c3=0); diff --git a/examples/multiobjective/mdp/consensus/consensus2_5_2_numerical.pctl b/examples/multiobjective/mdp/consensus/consensus2_5_2_numerical.pctl new file mode 100644 index 000000000..3b52e011e --- /dev/null +++ b/examples/multiobjective/mdp/consensus/consensus2_5_2_numerical.pctl @@ -0,0 +1 @@ +multi(Pmax=? [ F "one_proc_err" ], P>=0.9987286134 [ G "one_coin_ok" ]) diff --git a/examples/multiobjective/mdp/consensus/consensus2_5_2_pareto.pctl b/examples/multiobjective/mdp/consensus/consensus2_5_2_pareto.pctl new file mode 100644 index 000000000..7cd298d8e --- /dev/null +++ b/examples/multiobjective/mdp/consensus/consensus2_5_2_pareto.pctl @@ -0,0 +1 @@ +multi(Pmax=? [ F "one_proc_err" ], Pmax=? [ G "one_coin_ok" ]) diff --git a/examples/multiobjective/mdp/consensus/consensus3_3_2.nm b/examples/multiobjective/mdp/consensus/consensus3_3_2.nm new file mode 100644 index 000000000..a7f06fbdf --- /dev/null +++ b/examples/multiobjective/mdp/consensus/consensus3_3_2.nm @@ -0,0 +1,100 @@ +// model of randomised consensus + +mdp + +const int N = 3; // num processes +const int MAX = 3; // num rounds (R) +const int K = 2; // Parameter for coins + +// need to turn these into local copies later so the reading phase is complete? +formula leaders_agree1 = (p1=1 | r1 (p1'=1) & (r1'=1); + [] s1=0 & r1=0 -> (p1'=2) & (r1'=1); + + // read registers (currently does nothing because read vs from other processes + [] s1=0 & r1>0 & r1<=MAX -> (s1'=1); + // maxke a decision + [] s1=1 & decide1 -> (s1'=4) & (p1'=1); + [] s1=1 & decide2 -> (s1'=4) & (p1'=2); + [] s1=1 & r1 (s1'=0) & (p1'=1) & (r1'=r1+1); + [] s1=1 & r1 (s1'=0) & (p1'=2) & (r1'=r1+1); + [] s1=1 & r1 (s1'=2) & (p1'=0); + [] s1=1 & r1=MAX & !(decide1 | decide2) -> (s1'=5); // run out of rounds so error + // enter the coin procotol for the current round + [coin1_s1_start] s1=2 & r1=1 -> (s1'=3); + [coin2_s1_start] s1=2 & r1=2 -> (s1'=3); + // get response from the coin protocol + [coin1_s1_p1] s1=3 & r1=1 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin1_s1_p2] s1=3 & r1=1 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + [coin2_s1_p1] s1=3 & r1=2 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin2_s1_p2] s1=3 & r1=2 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + // done so loop + [done] s1>=4 -> true; + +endmodule + +module process2 = process1[ s1=s2, + p1=p2,p2=p3,p3=p1, + r1=r2,r2=r3,r3=r1, + coin1_s1_start=coin1_s2_start,coin2_s1_start=coin2_s2_start, + coin1_s1_p1=coin1_s2_p1,coin2_s1_p1=coin2_s2_p1, + coin1_s1_p2=coin1_s2_p2,coin2_s1_p2=coin2_s2_p2 ] +endmodule + +module process3 = process1[ s1=s3, + p1=p3,p2=p1,p3=p2, + r1=r3,r2=r1,r3=r2, + coin1_s1_start=coin1_s3_start,coin2_s1_start=coin2_s3_start, + coin1_s1_p1=coin1_s3_p1,coin2_s1_p1=coin2_s3_p1, + coin1_s1_p2=coin1_s3_p2,coin2_s1_p2=coin2_s3_p2 ] +endmodule + +module coin1_error + + c1 : [0..1]; // 1 is the error state + v1 : [0..2]; // value of the coin returned the first time + + // first returned value (any processes) + [coin1_s1_p1] v1=0 -> (v1'=1); + [coin1_s2_p1] v1=0 -> (v1'=1); + [coin1_s3_p1] v1=0 -> (v1'=1); + [coin1_s1_p2] v1=0 -> (v1'=2); + [coin1_s2_p2] v1=0 -> (v1'=2); + [coin1_s3_p2] v1=0 -> (v1'=2); + // later values returned + [coin1_s1_p1] v1=1 -> true; // good behaviour + [coin1_s2_p1] v1=1 -> true; // good behaviour + [coin1_s3_p1] v1=1 -> true; // good behaviour + [coin1_s1_p2] v1=2 -> true; // good behaviour + [coin1_s2_p2] v1=2 -> true; // good behaviour + [coin1_s3_p2] v1=2 -> true; // good behaviour + [coin1_s1_p1] v1=2 -> (c1'=1); // error + [coin1_s2_p1] v1=2 -> (c1'=1); // error + [coin1_s3_p1] v1=2 -> (c1'=1); // error + [coin1_s1_p2] v1=1 -> (c1'=1); // error + [coin1_s2_p2] v1=1 -> (c1'=1); // error + [coin1_s3_p2] v1=1 -> (c1'=1); // error + +endmodule + +// Labels +label "one_proc_err" = (s1=5 | s2=5 | s3=5); +label "one_coin_ok" = (c1=0); diff --git a/examples/multiobjective/mdp/consensus/consensus3_3_2_numerical.pctl b/examples/multiobjective/mdp/consensus/consensus3_3_2_numerical.pctl new file mode 100644 index 000000000..4e4f8e3f7 --- /dev/null +++ b/examples/multiobjective/mdp/consensus/consensus3_3_2_numerical.pctl @@ -0,0 +1 @@ +multi(Pmax=? [ F "one_proc_err" ], P>=0.7709112445 [ G "one_coin_ok" ]) diff --git a/examples/multiobjective/mdp/consensus/consensus3_3_2_pareto.pctl b/examples/multiobjective/mdp/consensus/consensus3_3_2_pareto.pctl new file mode 100644 index 000000000..7cd298d8e --- /dev/null +++ b/examples/multiobjective/mdp/consensus/consensus3_3_2_pareto.pctl @@ -0,0 +1 @@ +multi(Pmax=? [ F "one_proc_err" ], Pmax=? [ G "one_coin_ok" ]) diff --git a/examples/multiobjective/mdp/consensus/consensus3_4_2.nm b/examples/multiobjective/mdp/consensus/consensus3_4_2.nm new file mode 100644 index 000000000..abfc0ed1e --- /dev/null +++ b/examples/multiobjective/mdp/consensus/consensus3_4_2.nm @@ -0,0 +1,131 @@ +// model of randomised consensus + +mdp + +const int N = 3; // num processes +const int MAX = 4; // num rounds (R) +const int K = 2; // Parameter for coins + +// need to turn these into local copies later so the reading phase is complete? +formula leaders_agree1 = (p1=1 | r1 (p1'=1) & (r1'=1); + [] s1=0 & r1=0 -> (p1'=2) & (r1'=1); + + // read registers (currently does nothing because read vs from other processes + [] s1=0 & r1>0 & r1<=MAX -> (s1'=1); + // maxke a decision + [] s1=1 & decide1 -> (s1'=4) & (p1'=1); + [] s1=1 & decide2 -> (s1'=4) & (p1'=2); + [] s1=1 & r1 (s1'=0) & (p1'=1) & (r1'=r1+1); + [] s1=1 & r1 (s1'=0) & (p1'=2) & (r1'=r1+1); + [] s1=1 & r1 (s1'=2) & (p1'=0); + [] s1=1 & r1=MAX & !(decide1 | decide2) -> (s1'=5); // run out of rounds so error + // enter the coin procotol for the current round + [coin1_s1_start] s1=2 & r1=1 -> (s1'=3); + [coin2_s1_start] s1=2 & r1=2 -> (s1'=3); + [coin3_s1_start] s1=2 & r1=3 -> (s1'=3); + // get response from the coin protocol + [coin1_s1_p1] s1=3 & r1=1 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin1_s1_p2] s1=3 & r1=1 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + [coin2_s1_p1] s1=3 & r1=2 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin2_s1_p2] s1=3 & r1=2 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + [coin3_s1_p1] s1=3 & r1=3 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin3_s1_p2] s1=3 & r1=3 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + // done so loop + [done] s1>=4 -> true; + +endmodule + +module process2 = process1[ s1=s2, + p1=p2,p2=p3,p3=p1, + r1=r2,r2=r3,r3=r1, + coin1_s1_start=coin1_s2_start,coin2_s1_start=coin2_s2_start,coin3_s1_start=coin3_s2_start, + coin1_s1_p1=coin1_s2_p1,coin2_s1_p1=coin2_s2_p1,coin3_s1_p1=coin3_s2_p1, + coin1_s1_p2=coin1_s2_p2,coin2_s1_p2=coin2_s2_p2,coin3_s1_p2=coin3_s2_p2 ] +endmodule + +module process3 = process1[ s1=s3, + p1=p3,p2=p1,p3=p2, + r1=r3,r2=r1,r3=r2, + coin1_s1_start=coin1_s3_start,coin2_s1_start=coin2_s3_start,coin3_s1_start=coin3_s3_start, + coin1_s1_p1=coin1_s3_p1,coin2_s1_p1=coin2_s3_p1,coin3_s1_p1=coin3_s3_p1, + coin1_s1_p2=coin1_s3_p2,coin2_s1_p2=coin2_s3_p2,coin3_s1_p2=coin3_s3_p2 ] +endmodule + +module coin1_error + + c1 : [0..1]; // 1 is the error state + v1 : [0..2]; // value of the coin returned the first time + + // first returned value (any processes) + [coin1_s1_p1] v1=0 -> (v1'=1); + [coin1_s2_p1] v1=0 -> (v1'=1); + [coin1_s3_p1] v1=0 -> (v1'=1); + [coin1_s1_p2] v1=0 -> (v1'=2); + [coin1_s2_p2] v1=0 -> (v1'=2); + [coin1_s3_p2] v1=0 -> (v1'=2); + // later values returned + [coin1_s1_p1] v1=1 -> true; // good behaviour + [coin1_s2_p1] v1=1 -> true; // good behaviour + [coin1_s3_p1] v1=1 -> true; // good behaviour + [coin1_s1_p2] v1=2 -> true; // good behaviour + [coin1_s2_p2] v1=2 -> true; // good behaviour + [coin1_s3_p2] v1=2 -> true; // good behaviour + [coin1_s1_p1] v1=2 -> (c1'=1); // error + [coin1_s2_p1] v1=2 -> (c1'=1); // error + [coin1_s3_p1] v1=2 -> (c1'=1); // error + [coin1_s1_p2] v1=1 -> (c1'=1); // error + [coin1_s2_p2] v1=1 -> (c1'=1); // error + [coin1_s3_p2] v1=1 -> (c1'=1); // error + +endmodule + +module coin2_error + + c2 : [0..1]; // 1 is the error state + v2 : [0..2]; // value of the coin returned the first time + + // first returned value (any processes) + [coin2_s1_p1] v2=0 -> (v2'=1); + [coin2_s2_p1] v2=0 -> (v2'=1); + [coin2_s3_p1] v2=0 -> (v2'=1); + [coin2_s1_p2] v2=0 -> (v2'=2); + [coin2_s2_p2] v2=0 -> (v2'=2); + [coin2_s3_p2] v2=0 -> (v2'=2); + // later values returned + [coin2_s1_p1] v2=1 -> true; // good behaviour + [coin2_s2_p1] v2=1 -> true; // good behaviour + [coin2_s3_p1] v2=1 -> true; // good behaviour + [coin2_s1_p2] v2=2 -> true; // good behaviour + [coin2_s2_p2] v2=2 -> true; // good behaviour + [coin2_s3_p2] v2=2 -> true; // good behaviour + [coin2_s1_p1] v2=2 -> (c2'=1); // error + [coin2_s2_p1] v2=2 -> (c2'=1); // error + [coin2_s3_p1] v2=2 -> (c2'=1); // error + [coin2_s1_p2] v2=1 -> (c2'=1); // error + [coin2_s2_p2] v2=1 -> (c2'=1); // error + [coin2_s3_p2] v2=1 -> (c2'=1); // error + +endmodule + +// Labels +label "one_proc_err" = (s1=5 | s2=5 | s3=5); +label "one_coin_ok" = (c1=0 | c2=0); diff --git a/examples/multiobjective/mdp/consensus/consensus3_4_2_numerical.pctl b/examples/multiobjective/mdp/consensus/consensus3_4_2_numerical.pctl new file mode 100644 index 000000000..31768966d --- /dev/null +++ b/examples/multiobjective/mdp/consensus/consensus3_4_2_numerical.pctl @@ -0,0 +1 @@ +multi(Pmax=? [ F "one_proc_err" ], P>=0.9475183421 [ G "one_coin_ok" ]) diff --git a/examples/multiobjective/mdp/consensus/consensus3_4_2_pareto.pctl b/examples/multiobjective/mdp/consensus/consensus3_4_2_pareto.pctl new file mode 100644 index 000000000..7cd298d8e --- /dev/null +++ b/examples/multiobjective/mdp/consensus/consensus3_4_2_pareto.pctl @@ -0,0 +1 @@ +multi(Pmax=? [ F "one_proc_err" ], Pmax=? [ G "one_coin_ok" ]) diff --git a/examples/multiobjective/mdp/consensus/consensus3_5_2.nm b/examples/multiobjective/mdp/consensus/consensus3_5_2.nm new file mode 100644 index 000000000..cc9a7ebbf --- /dev/null +++ b/examples/multiobjective/mdp/consensus/consensus3_5_2.nm @@ -0,0 +1,162 @@ +// model of randomised consensus + +mdp + +const int N = 3; // num processes +const int MAX = 5; // num rounds (R) +const int K = 2; // Parameter for coins + +// need to turn these into local copies later so the reading phase is complete? +formula leaders_agree1 = (p1=1 | r1 (p1'=1) & (r1'=1); + [] s1=0 & r1=0 -> (p1'=2) & (r1'=1); + + // read registers (currently does nothing because read vs from other processes + [] s1=0 & r1>0 & r1<=MAX -> (s1'=1); + // maxke a decision + [] s1=1 & decide1 -> (s1'=4) & (p1'=1); + [] s1=1 & decide2 -> (s1'=4) & (p1'=2); + [] s1=1 & r1 (s1'=0) & (p1'=1) & (r1'=r1+1); + [] s1=1 & r1 (s1'=0) & (p1'=2) & (r1'=r1+1); + [] s1=1 & r1 (s1'=2) & (p1'=0); + [] s1=1 & r1=MAX & !(decide1 | decide2) -> (s1'=5); // run out of rounds so error + // enter the coin procotol for the current round + [coin1_s1_start] s1=2 & r1=1 -> (s1'=3); + [coin2_s1_start] s1=2 & r1=2 -> (s1'=3); + [coin3_s1_start] s1=2 & r1=3 -> (s1'=3); + [coin4_s1_start] s1=2 & r1=4 -> (s1'=3); + // get response from the coin protocol + [coin1_s1_p1] s1=3 & r1=1 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin1_s1_p2] s1=3 & r1=1 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + [coin2_s1_p1] s1=3 & r1=2 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin2_s1_p2] s1=3 & r1=2 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + [coin3_s1_p1] s1=3 & r1=3 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin3_s1_p2] s1=3 & r1=3 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + [coin4_s1_p1] s1=3 & r1=4 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin4_s1_p2] s1=3 & r1=4 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + // done so loop + [done] s1>=4 -> true; + +endmodule + +module process2 = process1[ s1=s2, + p1=p2,p2=p3,p3=p1, + r1=r2,r2=r3,r3=r1, + coin1_s1_start=coin1_s2_start,coin2_s1_start=coin2_s2_start,coin3_s1_start=coin3_s2_start,coin4_s1_start=coin4_s2_start, + coin1_s1_p1=coin1_s2_p1,coin2_s1_p1=coin2_s2_p1,coin3_s1_p1=coin3_s2_p1,coin4_s1_p1=coin4_s2_p1, + coin1_s1_p2=coin1_s2_p2,coin2_s1_p2=coin2_s2_p2,coin3_s1_p2=coin3_s2_p2,coin4_s1_p2=coin4_s2_p2 ] +endmodule + +module process3 = process1[ s1=s3, + p1=p3,p2=p1,p3=p2, + r1=r3,r2=r1,r3=r2, + coin1_s1_start=coin1_s3_start,coin2_s1_start=coin2_s3_start,coin3_s1_start=coin3_s3_start,coin4_s1_start=coin4_s3_start, + coin1_s1_p1=coin1_s3_p1,coin2_s1_p1=coin2_s3_p1,coin3_s1_p1=coin3_s3_p1,coin4_s1_p1=coin4_s3_p1, + coin1_s1_p2=coin1_s3_p2,coin2_s1_p2=coin2_s3_p2,coin3_s1_p2=coin3_s3_p2,coin4_s1_p2=coin4_s3_p2 ] +endmodule + +module coin1_error + + c1 : [0..1]; // 1 is the error state + v1 : [0..2]; // value of the coin returned the first time + + // first returned value (any processes) + [coin1_s1_p1] v1=0 -> (v1'=1); + [coin1_s2_p1] v1=0 -> (v1'=1); + [coin1_s3_p1] v1=0 -> (v1'=1); + [coin1_s1_p2] v1=0 -> (v1'=2); + [coin1_s2_p2] v1=0 -> (v1'=2); + [coin1_s3_p2] v1=0 -> (v1'=2); + // later values returned + [coin1_s1_p1] v1=1 -> true; // good behaviour + [coin1_s2_p1] v1=1 -> true; // good behaviour + [coin1_s3_p1] v1=1 -> true; // good behaviour + [coin1_s1_p2] v1=2 -> true; // good behaviour + [coin1_s2_p2] v1=2 -> true; // good behaviour + [coin1_s3_p2] v1=2 -> true; // good behaviour + [coin1_s1_p1] v1=2 -> (c1'=1); // error + [coin1_s2_p1] v1=2 -> (c1'=1); // error + [coin1_s3_p1] v1=2 -> (c1'=1); // error + [coin1_s1_p2] v1=1 -> (c1'=1); // error + [coin1_s2_p2] v1=1 -> (c1'=1); // error + [coin1_s3_p2] v1=1 -> (c1'=1); // error + +endmodule + +module coin2_error + + c2 : [0..1]; // 1 is the error state + v2 : [0..2]; // value of the coin returned the first time + + // first returned value (any processes) + [coin2_s1_p1] v2=0 -> (v2'=1); + [coin2_s2_p1] v2=0 -> (v2'=1); + [coin2_s3_p1] v2=0 -> (v2'=1); + [coin2_s1_p2] v2=0 -> (v2'=2); + [coin2_s2_p2] v2=0 -> (v2'=2); + [coin2_s3_p2] v2=0 -> (v2'=2); + // later values returned + [coin2_s1_p1] v2=1 -> true; // good behaviour + [coin2_s2_p1] v2=1 -> true; // good behaviour + [coin2_s3_p1] v2=1 -> true; // good behaviour + [coin2_s1_p2] v2=2 -> true; // good behaviour + [coin2_s2_p2] v2=2 -> true; // good behaviour + [coin2_s3_p2] v2=2 -> true; // good behaviour + [coin2_s1_p1] v2=2 -> (c2'=1); // error + [coin2_s2_p1] v2=2 -> (c2'=1); // error + [coin2_s3_p1] v2=2 -> (c2'=1); // error + [coin2_s1_p2] v2=1 -> (c2'=1); // error + [coin2_s2_p2] v2=1 -> (c2'=1); // error + [coin2_s3_p2] v2=1 -> (c2'=1); // error + +endmodule + +module coin3_error + + c3 : [0..1]; // 1 is the error state + v3 : [0..2]; // value of the coin returned the first time + + // first returned value (any processes) + [coin3_s1_p1] v3=0 -> (v3'=1); + [coin3_s2_p1] v3=0 -> (v3'=1); + [coin3_s3_p1] v3=0 -> (v3'=1); + [coin3_s1_p2] v3=0 -> (v3'=2); + [coin3_s2_p2] v3=0 -> (v3'=2); + [coin3_s3_p2] v3=0 -> (v3'=2); + // later values returned + [coin3_s1_p1] v3=1 -> true; // good behaviour + [coin3_s2_p1] v3=1 -> true; // good behaviour + [coin3_s3_p1] v3=1 -> true; // good behaviour + [coin3_s1_p2] v3=2 -> true; // good behaviour + [coin3_s2_p2] v3=2 -> true; // good behaviour + [coin3_s3_p2] v3=2 -> true; // good behaviour + [coin3_s1_p1] v3=2 -> (c3'=1); // error + [coin3_s2_p1] v3=2 -> (c3'=1); // error + [coin3_s3_p1] v3=2 -> (c3'=1); // error + [coin3_s1_p2] v3=1 -> (c3'=1); // error + [coin3_s2_p2] v3=1 -> (c3'=1); // error + [coin3_s3_p2] v3=1 -> (c3'=1); // error + +endmodule + +// Labels +label "one_proc_err" = (s1=5 | s2=5 | s3=5); +label "one_coin_ok" = (c1=0 | c2=0 | c3=0); diff --git a/examples/multiobjective/mdp/consensus/consensus3_5_2_numerical.pctl b/examples/multiobjective/mdp/consensus/consensus3_5_2_numerical.pctl new file mode 100644 index 000000000..6a3113398 --- /dev/null +++ b/examples/multiobjective/mdp/consensus/consensus3_5_2_numerical.pctl @@ -0,0 +1 @@ +multi(Pmax=? [ F "one_proc_err" ], P>=0.9879770423 [ G "one_coin_ok" ]) diff --git a/examples/multiobjective/mdp/consensus/consensus3_5_2_pareto.pctl b/examples/multiobjective/mdp/consensus/consensus3_5_2_pareto.pctl new file mode 100644 index 000000000..7cd298d8e --- /dev/null +++ b/examples/multiobjective/mdp/consensus/consensus3_5_2_pareto.pctl @@ -0,0 +1 @@ +multi(Pmax=? [ F "one_proc_err" ], Pmax=? [ G "one_coin_ok" ]) diff --git a/examples/multiobjective/mdp/consensus/origFiles/consensus2_multi3.nm b/examples/multiobjective/mdp/consensus/origFiles/consensus2_multi3.nm new file mode 100644 index 000000000..dbf9dec0f --- /dev/null +++ b/examples/multiobjective/mdp/consensus/origFiles/consensus2_multi3.nm @@ -0,0 +1,87 @@ +// model of randomised consensus + +mdp + +const int N = 2; // num processes +const int MAX = 3; // num rounds (R) + +// need to turn these into local copies later so the reading phase is complete? +formula leaders_agree1 = (p1=1 | r1 (p1'=1) & (r1'=1); + [] s1=0 & r1=0 -> (p1'=2) & (r1'=1); + + // read registers (currently does nothing because read vs from other processes + [] s1=0 & r1>0 & r1<=MAX -> (s1'=1); + // maxke a decision + [] s1=1 & decide1 -> (s1'=4) & (p1'=1); + [] s1=1 & decide2 -> (s1'=4) & (p1'=2); + [] s1=1 & r1 (s1'=0) & (p1'=1) & (r1'=r1+1); + [] s1=1 & r1 (s1'=0) & (p1'=2) & (r1'=r1+1); + [] s1=1 & r1 (s1'=2) & (p1'=0); + [] s1=1 & r1=MAX & !(decide1 | decide2) -> (s1'=5); // run out of rounds so error + // enter the coin procotol for the current round + [coin1_s1_start] s1=2 & r1=1 -> (s1'=3); + [coin2_s1_start] s1=2 & r1=2 -> (s1'=3); + // get response from the coin protocol + [coin1_s1_p1] s1=3 & r1=1 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin1_s1_p2] s1=3 & r1=1 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + [coin2_s1_p1] s1=3 & r1=2 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin2_s1_p2] s1=3 & r1=2 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + // done so loop + [done] s1>=4 -> true; + +endmodule + +module process2 = process1[ s1=s2, + p1=p2,p2=p1, + r1=r2,r2=r1, + coin1_s1_start=coin1_s2_start,coin2_s1_start=coin2_s2_start, + coin1_s1_p1=coin1_s2_p1,coin2_s1_p1=coin2_s2_p1, + coin1_s1_p2=coin1_s2_p2,coin2_s1_p2=coin2_s2_p2 ] +endmodule + +module coin1_error + + c1 : [0..1]; // 1 is the error state + v1 : [0..2]; // value of the coin returned the first time + + // first returned value (any processes) + [coin1_s1_p1] v1=0 -> (v1'=1); + [coin1_s2_p1] v1=0 -> (v1'=1); + [coin1_s1_p2] v1=0 -> (v1'=2); + [coin1_s2_p2] v1=0 -> (v1'=2); + // later values returned + [coin1_s1_p1] v1=1 -> true; // good behaviour + [coin1_s2_p1] v1=1 -> true; // good behaviour + [coin1_s1_p2] v1=2 -> true; // good behaviour + [coin1_s2_p2] v1=2 -> true; // good behaviour + [coin1_s1_p1] v1=2 -> (c1'=1); // error + [coin1_s2_p1] v1=2 -> (c1'=1); // error + [coin1_s1_p2] v1=1 -> (c1'=1); // error + [coin1_s2_p2] v1=1 -> (c1'=1); // error + +endmodule + +// coins 2 and 3 are of no use as there are not enough rounds afterwards to decide + +// Labels +label "one_proc_err" = (s1=5 | s2=5); +label "one_coin_ok" = (c1=0); diff --git a/examples/multiobjective/mdp/consensus/origFiles/consensus2_multi4.nm b/examples/multiobjective/mdp/consensus/origFiles/consensus2_multi4.nm new file mode 100644 index 000000000..75f6e8a4d --- /dev/null +++ b/examples/multiobjective/mdp/consensus/origFiles/consensus2_multi4.nm @@ -0,0 +1,113 @@ +// model of randomised consensus + +mdp + +const int N = 2; // num processes +const int MAX = 4; // num rounds (R) + +// need to turn these into local copies later so the reading phase is complete? +formula leaders_agree1 = (p1=1 | r1 (p1'=1) & (r1'=1); + [] s1=0 & r1=0 -> (p1'=2) & (r1'=1); + + // read registers (currently does nothing because read vs from other processes + [] s1=0 & r1>0 & r1<=MAX -> (s1'=1); + // maxke a decision + [] s1=1 & decide1 -> (s1'=4) & (p1'=1); + [] s1=1 & decide2 -> (s1'=4) & (p1'=2); + [] s1=1 & r1 (s1'=0) & (p1'=1) & (r1'=r1+1); + [] s1=1 & r1 (s1'=0) & (p1'=2) & (r1'=r1+1); + [] s1=1 & r1 (s1'=2) & (p1'=0); + [] s1=1 & r1=MAX & !(decide1 | decide2) -> (s1'=5); // run out of rounds so error + // enter the coin procotol for the current round + [coin1_s1_start] s1=2 & r1=1 -> (s1'=3); + [coin2_s1_start] s1=2 & r1=2 -> (s1'=3); + [coin3_s1_start] s1=2 & r1=3 -> (s1'=3); + // get response from the coin protocol + [coin1_s1_p1] s1=3 & r1=1 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin1_s1_p2] s1=3 & r1=1 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + [coin2_s1_p1] s1=3 & r1=2 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin2_s1_p2] s1=3 & r1=2 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + [coin3_s1_p1] s1=3 & r1=3 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin3_s1_p2] s1=3 & r1=3 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + // done so loop + [done] s1>=4 -> true; + +endmodule + +module process2 = process1[ s1=s2, + p1=p2,p2=p1, + r1=r2,r2=r1, + coin1_s1_start=coin1_s2_start,coin2_s1_start=coin2_s2_start,coin3_s1_start=coin3_s2_start, + coin1_s1_p1=coin1_s2_p1,coin2_s1_p1=coin2_s2_p1,coin3_s1_p1=coin3_s2_p1, + coin1_s1_p2=coin1_s2_p2,coin2_s1_p2=coin2_s2_p2,coin3_s1_p2=coin3_s2_p2 ] +endmodule + +module coin1_error + + c1 : [0..1]; // 1 is the error state + v1 : [0..2]; // value of the coin returned the first time + + // first returned value (any processes) + [coin1_s1_p1] v1=0 -> (v1'=1); + [coin1_s2_p1] v1=0 -> (v1'=1); + [coin1_s1_p2] v1=0 -> (v1'=2); + [coin1_s2_p2] v1=0 -> (v1'=2); + // later values returned + [coin1_s1_p1] v1=1 -> true; // good behaviour + [coin1_s2_p1] v1=1 -> true; // good behaviour + [coin1_s1_p2] v1=2 -> true; // good behaviour + [coin1_s2_p2] v1=2 -> true; // good behaviour + [coin1_s1_p1] v1=2 -> (c1'=1); // error + [coin1_s2_p1] v1=2 -> (c1'=1); // error + [coin1_s1_p2] v1=1 -> (c1'=1); // error + [coin1_s2_p2] v1=1 -> (c1'=1); // error + +endmodule + +// could do with renaming +module coin2_error + + c2 : [0..1]; // 1 is the error state + v2 : [0..2]; // value of the coin returned the first time + + // first returned value (any processes) + [coin2_s1_p1] v2=0 -> (v2'=1); + [coin2_s2_p1] v2=0 -> (v2'=1); + [coin2_s1_p2] v2=0 -> (v2'=2); + [coin2_s2_p2] v2=0 -> (v2'=2); + // later values returned + [coin2_s1_p1] v2=1 -> true; // good behaviour + [coin2_s2_p1] v2=1 -> true; // good behaviour + [coin2_s1_p2] v2=2 -> true; // good behaviour + [coin2_s2_p2] v2=2 -> true; // good behaviour + [coin2_s1_p1] v2=2 -> (c2'=1); // error + [coin2_s2_p1] v2=2 -> (c2'=1); // error + [coin2_s1_p2] v2=1 -> (c2'=1); // error + [coin2_s2_p2] v2=1 -> (c2'=1); // error + +endmodule + +// coin 3 is of no use because of number of rounds + +// Labels +label "one_proc_err" = (s1=5 | s2=5); +label "one_coin_ok" = (c1=0 | c2=0); diff --git a/examples/multiobjective/mdp/consensus/origFiles/consensus2_multi5.nm b/examples/multiobjective/mdp/consensus/origFiles/consensus2_multi5.nm new file mode 100644 index 000000000..a8a7aff6e --- /dev/null +++ b/examples/multiobjective/mdp/consensus/origFiles/consensus2_multi5.nm @@ -0,0 +1,139 @@ +// model of randomised consensus + +mdp + +const int N = 2; // num processes +const int MAX = 5; // num rounds (R) + +// need to turn these into local copies later so the reading phase is complete? +formula leaders_agree1 = (p1=1 | r1 (p1'=1) & (r1'=1); + [] s1=0 & r1=0 -> (p1'=2) & (r1'=1); + + // read registers (currently does nothing because read vs from other processes + [] s1=0 & r1>0 & r1<=MAX -> (s1'=1); + // maxke a decision + [] s1=1 & decide1 -> (s1'=4) & (p1'=1); + [] s1=1 & decide2 -> (s1'=4) & (p1'=2); + [] s1=1 & r1 (s1'=0) & (p1'=1) & (r1'=r1+1); + [] s1=1 & r1 (s1'=0) & (p1'=2) & (r1'=r1+1); + [] s1=1 & r1 (s1'=2) & (p1'=0); + [] s1=1 & r1=MAX & !(decide1 | decide2) -> (s1'=5); // run out of rounds so error + // enter the coin procotol for the current round + [coin1_s1_start] s1=2 & r1=1 -> (s1'=3); + [coin2_s1_start] s1=2 & r1=2 -> (s1'=3); + [coin3_s1_start] s1=2 & r1=3 -> (s1'=3); + [coin4_s1_start] s1=2 & r1=4 -> (s1'=3); + // get response from the coin protocol + [coin1_s1_p1] s1=3 & r1=1 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin1_s1_p2] s1=3 & r1=1 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + [coin2_s1_p1] s1=3 & r1=2 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin2_s1_p2] s1=3 & r1=2 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + [coin3_s1_p1] s1=3 & r1=3 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin3_s1_p2] s1=3 & r1=3 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + [coin4_s1_p1] s1=3 & r1=4 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin4_s1_p2] s1=3 & r1=4 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + // done so loop + [done] s1>=4 -> true; + +endmodule + +module process2 = process1[ s1=s2, + p1=p2,p2=p1, + r1=r2,r2=r1, + coin1_s1_start=coin1_s2_start,coin2_s1_start=coin2_s2_start,coin3_s1_start=coin3_s2_start,coin4_s1_start=coin4_s2_start, + coin1_s1_p1=coin1_s2_p1,coin2_s1_p1=coin2_s2_p1,coin3_s1_p1=coin3_s2_p1,coin4_s1_p1=coin4_s2_p1, + coin1_s1_p2=coin1_s2_p2,coin2_s1_p2=coin2_s2_p2,coin3_s1_p2=coin3_s2_p2,coin4_s1_p2=coin4_s2_p2 ] +endmodule + +module coin1_error + + c1 : [0..1]; // 1 is the error state + v1 : [0..2]; // value of the coin returned the first time + + // first returned value (any processes) + [coin1_s1_p1] v1=0 -> (v1'=1); + [coin1_s2_p1] v1=0 -> (v1'=1); + [coin1_s1_p2] v1=0 -> (v1'=2); + [coin1_s2_p2] v1=0 -> (v1'=2); + // later values returned + [coin1_s1_p1] v1=1 -> true; // good behaviour + [coin1_s2_p1] v1=1 -> true; // good behaviour + [coin1_s1_p2] v1=2 -> true; // good behaviour + [coin1_s2_p2] v1=2 -> true; // good behaviour + [coin1_s1_p1] v1=2 -> (c1'=1); // error + [coin1_s2_p1] v1=2 -> (c1'=1); // error + [coin1_s1_p2] v1=1 -> (c1'=1); // error + [coin1_s2_p2] v1=1 -> (c1'=1); // error + +endmodule + +// could do with renaming +module coin2_error + + c2 : [0..1]; // 1 is the error state + v2 : [0..2]; // value of the coin returned the first time + + // first returned value (any processes) + [coin2_s1_p1] v2=0 -> (v2'=1); + [coin2_s2_p1] v2=0 -> (v2'=1); + [coin2_s1_p2] v2=0 -> (v2'=2); + [coin2_s2_p2] v2=0 -> (v2'=2); + // later values returned + [coin2_s1_p1] v2=1 -> true; // good behaviour + [coin2_s2_p1] v2=1 -> true; // good behaviour + [coin2_s1_p2] v2=2 -> true; // good behaviour + [coin2_s2_p2] v2=2 -> true; // good behaviour + [coin2_s1_p1] v2=2 -> (c2'=1); // error + [coin2_s2_p1] v2=2 -> (c2'=1); // error + [coin2_s1_p2] v2=1 -> (c2'=1); // error + [coin2_s2_p2] v2=1 -> (c2'=1); // error + +endmodule + +// could do with renaming +module coin3_error + + c3 : [0..1]; // 1 is the error state + v3 : [0..2]; // value of the coin returned the first time + + // first returned value (any processes) + [coin3_s1_p1] v3=0 -> (v3'=1); + [coin3_s2_p1] v3=0 -> (v3'=1); + [coin3_s1_p2] v3=0 -> (v3'=2); + [coin3_s2_p2] v3=0 -> (v3'=2); + // later values returned + [coin3_s1_p1] v3=1 -> true; // good behaviour + [coin3_s2_p1] v3=1 -> true; // good behaviour + [coin3_s1_p2] v3=2 -> true; // good behaviour + [coin3_s2_p2] v3=2 -> true; // good behaviour + [coin3_s1_p1] v3=2 -> (c3'=1); // error + [coin3_s2_p1] v3=2 -> (c3'=1); // error + [coin3_s1_p2] v3=1 -> (c3'=1); // error + [coin3_s2_p2] v3=1 -> (c3'=1); // error + +endmodule + +// coin 4 is of no use because of number of rounds + +// Labels +label "one_proc_err" = (s1=5 | s2=5); +label "one_coin_ok" = (c1=0 | c2=0 | c3=0); diff --git a/examples/multiobjective/mdp/consensus/origFiles/consensus3_multi3.nm b/examples/multiobjective/mdp/consensus/origFiles/consensus3_multi3.nm new file mode 100644 index 000000000..4b1e95373 --- /dev/null +++ b/examples/multiobjective/mdp/consensus/origFiles/consensus3_multi3.nm @@ -0,0 +1,99 @@ +// model of randomised consensus + +mdp + +const int N = 3; // num processes +const int MAX = 3; // num rounds (R) + +// need to turn these into local copies later so the reading phase is complete? +formula leaders_agree1 = (p1=1 | r1 (p1'=1) & (r1'=1); + [] s1=0 & r1=0 -> (p1'=2) & (r1'=1); + + // read registers (currently does nothing because read vs from other processes + [] s1=0 & r1>0 & r1<=MAX -> (s1'=1); + // maxke a decision + [] s1=1 & decide1 -> (s1'=4) & (p1'=1); + [] s1=1 & decide2 -> (s1'=4) & (p1'=2); + [] s1=1 & r1 (s1'=0) & (p1'=1) & (r1'=r1+1); + [] s1=1 & r1 (s1'=0) & (p1'=2) & (r1'=r1+1); + [] s1=1 & r1 (s1'=2) & (p1'=0); + [] s1=1 & r1=MAX & !(decide1 | decide2) -> (s1'=5); // run out of rounds so error + // enter the coin procotol for the current round + [coin1_s1_start] s1=2 & r1=1 -> (s1'=3); + [coin2_s1_start] s1=2 & r1=2 -> (s1'=3); + // get response from the coin protocol + [coin1_s1_p1] s1=3 & r1=1 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin1_s1_p2] s1=3 & r1=1 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + [coin2_s1_p1] s1=3 & r1=2 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin2_s1_p2] s1=3 & r1=2 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + // done so loop + [done] s1>=4 -> true; + +endmodule + +module process2 = process1[ s1=s2, + p1=p2,p2=p3,p3=p1, + r1=r2,r2=r3,r3=r1, + coin1_s1_start=coin1_s2_start,coin2_s1_start=coin2_s2_start, + coin1_s1_p1=coin1_s2_p1,coin2_s1_p1=coin2_s2_p1, + coin1_s1_p2=coin1_s2_p2,coin2_s1_p2=coin2_s2_p2 ] +endmodule + +module process3 = process1[ s1=s3, + p1=p3,p2=p1,p3=p2, + r1=r3,r2=r1,r3=r2, + coin1_s1_start=coin1_s3_start,coin2_s1_start=coin2_s3_start, + coin1_s1_p1=coin1_s3_p1,coin2_s1_p1=coin2_s3_p1, + coin1_s1_p2=coin1_s3_p2,coin2_s1_p2=coin2_s3_p2 ] +endmodule + +module coin1_error + + c1 : [0..1]; // 1 is the error state + v1 : [0..2]; // value of the coin returned the first time + + // first returned value (any processes) + [coin1_s1_p1] v1=0 -> (v1'=1); + [coin1_s2_p1] v1=0 -> (v1'=1); + [coin1_s3_p1] v1=0 -> (v1'=1); + [coin1_s1_p2] v1=0 -> (v1'=2); + [coin1_s2_p2] v1=0 -> (v1'=2); + [coin1_s3_p2] v1=0 -> (v1'=2); + // later values returned + [coin1_s1_p1] v1=1 -> true; // good behaviour + [coin1_s2_p1] v1=1 -> true; // good behaviour + [coin1_s3_p1] v1=1 -> true; // good behaviour + [coin1_s1_p2] v1=2 -> true; // good behaviour + [coin1_s2_p2] v1=2 -> true; // good behaviour + [coin1_s3_p2] v1=2 -> true; // good behaviour + [coin1_s1_p1] v1=2 -> (c1'=1); // error + [coin1_s2_p1] v1=2 -> (c1'=1); // error + [coin1_s3_p1] v1=2 -> (c1'=1); // error + [coin1_s1_p2] v1=1 -> (c1'=1); // error + [coin1_s2_p2] v1=1 -> (c1'=1); // error + [coin1_s3_p2] v1=1 -> (c1'=1); // error + +endmodule + +// Labels +label "one_proc_err" = (s1=5 | s2=5 | s3=5); +label "one_coin_ok" = (c1=0); diff --git a/examples/multiobjective/mdp/consensus/origFiles/consensus3_multi4.nm b/examples/multiobjective/mdp/consensus/origFiles/consensus3_multi4.nm new file mode 100644 index 000000000..052114d1d --- /dev/null +++ b/examples/multiobjective/mdp/consensus/origFiles/consensus3_multi4.nm @@ -0,0 +1,130 @@ +// model of randomised consensus + +mdp + +const int N = 3; // num processes +const int MAX = 4; // num rounds (R) + +// need to turn these into local copies later so the reading phase is complete? +formula leaders_agree1 = (p1=1 | r1 (p1'=1) & (r1'=1); + [] s1=0 & r1=0 -> (p1'=2) & (r1'=1); + + // read registers (currently does nothing because read vs from other processes + [] s1=0 & r1>0 & r1<=MAX -> (s1'=1); + // maxke a decision + [] s1=1 & decide1 -> (s1'=4) & (p1'=1); + [] s1=1 & decide2 -> (s1'=4) & (p1'=2); + [] s1=1 & r1 (s1'=0) & (p1'=1) & (r1'=r1+1); + [] s1=1 & r1 (s1'=0) & (p1'=2) & (r1'=r1+1); + [] s1=1 & r1 (s1'=2) & (p1'=0); + [] s1=1 & r1=MAX & !(decide1 | decide2) -> (s1'=5); // run out of rounds so error + // enter the coin procotol for the current round + [coin1_s1_start] s1=2 & r1=1 -> (s1'=3); + [coin2_s1_start] s1=2 & r1=2 -> (s1'=3); + [coin3_s1_start] s1=2 & r1=3 -> (s1'=3); + // get response from the coin protocol + [coin1_s1_p1] s1=3 & r1=1 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin1_s1_p2] s1=3 & r1=1 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + [coin2_s1_p1] s1=3 & r1=2 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin2_s1_p2] s1=3 & r1=2 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + [coin3_s1_p1] s1=3 & r1=3 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin3_s1_p2] s1=3 & r1=3 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + // done so loop + [done] s1>=4 -> true; + +endmodule + +module process2 = process1[ s1=s2, + p1=p2,p2=p3,p3=p1, + r1=r2,r2=r3,r3=r1, + coin1_s1_start=coin1_s2_start,coin2_s1_start=coin2_s2_start,coin3_s1_start=coin3_s2_start, + coin1_s1_p1=coin1_s2_p1,coin2_s1_p1=coin2_s2_p1,coin3_s1_p1=coin3_s2_p1, + coin1_s1_p2=coin1_s2_p2,coin2_s1_p2=coin2_s2_p2,coin3_s1_p2=coin3_s2_p2 ] +endmodule + +module process3 = process1[ s1=s3, + p1=p3,p2=p1,p3=p2, + r1=r3,r2=r1,r3=r2, + coin1_s1_start=coin1_s3_start,coin2_s1_start=coin2_s3_start,coin3_s1_start=coin3_s3_start, + coin1_s1_p1=coin1_s3_p1,coin2_s1_p1=coin2_s3_p1,coin3_s1_p1=coin3_s3_p1, + coin1_s1_p2=coin1_s3_p2,coin2_s1_p2=coin2_s3_p2,coin3_s1_p2=coin3_s3_p2 ] +endmodule + +module coin1_error + + c1 : [0..1]; // 1 is the error state + v1 : [0..2]; // value of the coin returned the first time + + // first returned value (any processes) + [coin1_s1_p1] v1=0 -> (v1'=1); + [coin1_s2_p1] v1=0 -> (v1'=1); + [coin1_s3_p1] v1=0 -> (v1'=1); + [coin1_s1_p2] v1=0 -> (v1'=2); + [coin1_s2_p2] v1=0 -> (v1'=2); + [coin1_s3_p2] v1=0 -> (v1'=2); + // later values returned + [coin1_s1_p1] v1=1 -> true; // good behaviour + [coin1_s2_p1] v1=1 -> true; // good behaviour + [coin1_s3_p1] v1=1 -> true; // good behaviour + [coin1_s1_p2] v1=2 -> true; // good behaviour + [coin1_s2_p2] v1=2 -> true; // good behaviour + [coin1_s3_p2] v1=2 -> true; // good behaviour + [coin1_s1_p1] v1=2 -> (c1'=1); // error + [coin1_s2_p1] v1=2 -> (c1'=1); // error + [coin1_s3_p1] v1=2 -> (c1'=1); // error + [coin1_s1_p2] v1=1 -> (c1'=1); // error + [coin1_s2_p2] v1=1 -> (c1'=1); // error + [coin1_s3_p2] v1=1 -> (c1'=1); // error + +endmodule + +module coin2_error + + c2 : [0..1]; // 1 is the error state + v2 : [0..2]; // value of the coin returned the first time + + // first returned value (any processes) + [coin2_s1_p1] v2=0 -> (v2'=1); + [coin2_s2_p1] v2=0 -> (v2'=1); + [coin2_s3_p1] v2=0 -> (v2'=1); + [coin2_s1_p2] v2=0 -> (v2'=2); + [coin2_s2_p2] v2=0 -> (v2'=2); + [coin2_s3_p2] v2=0 -> (v2'=2); + // later values returned + [coin2_s1_p1] v2=1 -> true; // good behaviour + [coin2_s2_p1] v2=1 -> true; // good behaviour + [coin2_s3_p1] v2=1 -> true; // good behaviour + [coin2_s1_p2] v2=2 -> true; // good behaviour + [coin2_s2_p2] v2=2 -> true; // good behaviour + [coin2_s3_p2] v2=2 -> true; // good behaviour + [coin2_s1_p1] v2=2 -> (c2'=1); // error + [coin2_s2_p1] v2=2 -> (c2'=1); // error + [coin2_s3_p1] v2=2 -> (c2'=1); // error + [coin2_s1_p2] v2=1 -> (c2'=1); // error + [coin2_s2_p2] v2=1 -> (c2'=1); // error + [coin2_s3_p2] v2=1 -> (c2'=1); // error + +endmodule + +// Labels +label "one_proc_err" = (s1=5 | s2=5 | s3=5); +label "one_coin_ok" = (c1=0 | c2=0); diff --git a/examples/multiobjective/mdp/consensus/origFiles/consensus3_multi5.nm b/examples/multiobjective/mdp/consensus/origFiles/consensus3_multi5.nm new file mode 100644 index 000000000..8097dc437 --- /dev/null +++ b/examples/multiobjective/mdp/consensus/origFiles/consensus3_multi5.nm @@ -0,0 +1,161 @@ +// model of randomised consensus + +mdp + +const int N = 3; // num processes +const int MAX = 5; // num rounds (R) + +// need to turn these into local copies later so the reading phase is complete? +formula leaders_agree1 = (p1=1 | r1 (p1'=1) & (r1'=1); + [] s1=0 & r1=0 -> (p1'=2) & (r1'=1); + + // read registers (currently does nothing because read vs from other processes + [] s1=0 & r1>0 & r1<=MAX -> (s1'=1); + // maxke a decision + [] s1=1 & decide1 -> (s1'=4) & (p1'=1); + [] s1=1 & decide2 -> (s1'=4) & (p1'=2); + [] s1=1 & r1 (s1'=0) & (p1'=1) & (r1'=r1+1); + [] s1=1 & r1 (s1'=0) & (p1'=2) & (r1'=r1+1); + [] s1=1 & r1 (s1'=2) & (p1'=0); + [] s1=1 & r1=MAX & !(decide1 | decide2) -> (s1'=5); // run out of rounds so error + // enter the coin procotol for the current round + [coin1_s1_start] s1=2 & r1=1 -> (s1'=3); + [coin2_s1_start] s1=2 & r1=2 -> (s1'=3); + [coin3_s1_start] s1=2 & r1=3 -> (s1'=3); + [coin4_s1_start] s1=2 & r1=4 -> (s1'=3); + // get response from the coin protocol + [coin1_s1_p1] s1=3 & r1=1 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin1_s1_p2] s1=3 & r1=1 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + [coin2_s1_p1] s1=3 & r1=2 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin2_s1_p2] s1=3 & r1=2 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + [coin3_s1_p1] s1=3 & r1=3 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin3_s1_p2] s1=3 & r1=3 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + [coin4_s1_p1] s1=3 & r1=4 -> (s1'=0) & (p1'=1) & (r1'=r1+1); + [coin4_s1_p2] s1=3 & r1=4 -> (s1'=0) & (p1'=2) & (r1'=r1+1); + // done so loop + [done] s1>=4 -> true; + +endmodule + +module process2 = process1[ s1=s2, + p1=p2,p2=p3,p3=p1, + r1=r2,r2=r3,r3=r1, + coin1_s1_start=coin1_s2_start,coin2_s1_start=coin2_s2_start,coin3_s1_start=coin3_s2_start,coin4_s1_start=coin4_s2_start, + coin1_s1_p1=coin1_s2_p1,coin2_s1_p1=coin2_s2_p1,coin3_s1_p1=coin3_s2_p1,coin4_s1_p1=coin4_s2_p1, + coin1_s1_p2=coin1_s2_p2,coin2_s1_p2=coin2_s2_p2,coin3_s1_p2=coin3_s2_p2,coin4_s1_p2=coin4_s2_p2 ] +endmodule + +module process3 = process1[ s1=s3, + p1=p3,p2=p1,p3=p2, + r1=r3,r2=r1,r3=r2, + coin1_s1_start=coin1_s3_start,coin2_s1_start=coin2_s3_start,coin3_s1_start=coin3_s3_start,coin4_s1_start=coin4_s3_start, + coin1_s1_p1=coin1_s3_p1,coin2_s1_p1=coin2_s3_p1,coin3_s1_p1=coin3_s3_p1,coin4_s1_p1=coin4_s3_p1, + coin1_s1_p2=coin1_s3_p2,coin2_s1_p2=coin2_s3_p2,coin3_s1_p2=coin3_s3_p2,coin4_s1_p2=coin4_s3_p2 ] +endmodule + +module coin1_error + + c1 : [0..1]; // 1 is the error state + v1 : [0..2]; // value of the coin returned the first time + + // first returned value (any processes) + [coin1_s1_p1] v1=0 -> (v1'=1); + [coin1_s2_p1] v1=0 -> (v1'=1); + [coin1_s3_p1] v1=0 -> (v1'=1); + [coin1_s1_p2] v1=0 -> (v1'=2); + [coin1_s2_p2] v1=0 -> (v1'=2); + [coin1_s3_p2] v1=0 -> (v1'=2); + // later values returned + [coin1_s1_p1] v1=1 -> true; // good behaviour + [coin1_s2_p1] v1=1 -> true; // good behaviour + [coin1_s3_p1] v1=1 -> true; // good behaviour + [coin1_s1_p2] v1=2 -> true; // good behaviour + [coin1_s2_p2] v1=2 -> true; // good behaviour + [coin1_s3_p2] v1=2 -> true; // good behaviour + [coin1_s1_p1] v1=2 -> (c1'=1); // error + [coin1_s2_p1] v1=2 -> (c1'=1); // error + [coin1_s3_p1] v1=2 -> (c1'=1); // error + [coin1_s1_p2] v1=1 -> (c1'=1); // error + [coin1_s2_p2] v1=1 -> (c1'=1); // error + [coin1_s3_p2] v1=1 -> (c1'=1); // error + +endmodule + +module coin2_error + + c2 : [0..1]; // 1 is the error state + v2 : [0..2]; // value of the coin returned the first time + + // first returned value (any processes) + [coin2_s1_p1] v2=0 -> (v2'=1); + [coin2_s2_p1] v2=0 -> (v2'=1); + [coin2_s3_p1] v2=0 -> (v2'=1); + [coin2_s1_p2] v2=0 -> (v2'=2); + [coin2_s2_p2] v2=0 -> (v2'=2); + [coin2_s3_p2] v2=0 -> (v2'=2); + // later values returned + [coin2_s1_p1] v2=1 -> true; // good behaviour + [coin2_s2_p1] v2=1 -> true; // good behaviour + [coin2_s3_p1] v2=1 -> true; // good behaviour + [coin2_s1_p2] v2=2 -> true; // good behaviour + [coin2_s2_p2] v2=2 -> true; // good behaviour + [coin2_s3_p2] v2=2 -> true; // good behaviour + [coin2_s1_p1] v2=2 -> (c2'=1); // error + [coin2_s2_p1] v2=2 -> (c2'=1); // error + [coin2_s3_p1] v2=2 -> (c2'=1); // error + [coin2_s1_p2] v2=1 -> (c2'=1); // error + [coin2_s2_p2] v2=1 -> (c2'=1); // error + [coin2_s3_p2] v2=1 -> (c2'=1); // error + +endmodule + +module coin3_error + + c3 : [0..1]; // 1 is the error state + v3 : [0..2]; // value of the coin returned the first time + + // first returned value (any processes) + [coin3_s1_p1] v3=0 -> (v3'=1); + [coin3_s2_p1] v3=0 -> (v3'=1); + [coin3_s3_p1] v3=0 -> (v3'=1); + [coin3_s1_p2] v3=0 -> (v3'=2); + [coin3_s2_p2] v3=0 -> (v3'=2); + [coin3_s3_p2] v3=0 -> (v3'=2); + // later values returned + [coin3_s1_p1] v3=1 -> true; // good behaviour + [coin3_s2_p1] v3=1 -> true; // good behaviour + [coin3_s3_p1] v3=1 -> true; // good behaviour + [coin3_s1_p2] v3=2 -> true; // good behaviour + [coin3_s2_p2] v3=2 -> true; // good behaviour + [coin3_s3_p2] v3=2 -> true; // good behaviour + [coin3_s1_p1] v3=2 -> (c3'=1); // error + [coin3_s2_p1] v3=2 -> (c3'=1); // error + [coin3_s3_p1] v3=2 -> (c3'=1); // error + [coin3_s1_p2] v3=1 -> (c3'=1); // error + [coin3_s2_p2] v3=1 -> (c3'=1); // error + [coin3_s3_p2] v3=1 -> (c3'=1); // error + +endmodule + +// Labels +label "one_proc_err" = (s1=5 | s2=5 | s3=5); +label "one_coin_ok" = (c1=0 | c2=0 | c3=0); diff --git a/examples/multiobjective/mdp/consensus/origFiles/consensus_multi.pctl b/examples/multiobjective/mdp/consensus/origFiles/consensus_multi.pctl new file mode 100644 index 000000000..7a5f12b31 --- /dev/null +++ b/examples/multiobjective/mdp/consensus/origFiles/consensus_multi.pctl @@ -0,0 +1,27 @@ +// Parameter K for coins +const int K; + +// Max probability of component (coins) violating assumption property (checked separately) +const double p_coin_fail = +N=2 ? ( + K=2 ? 0.10833260973166493 : + K=12 ? 0.04164301267240658 : + K=20 ? 0.01249126244810821 : +0 ) : +N=3 ? ( + K=2 ? 0.22908875545788154 : + K=4 ? 0.12450138796380239 : + K=8 ? 0.06248479880890645 : + K=12 ? 0.04164365757451993 : + K=16 ? 0.031218839562495382 : + K=20 ? 0.024960596483605935 : +0 ) : 0; + +// Probability bound for assumption, derived from above +const double p_one_coin_ok = 1 - pow(p_coin_fail, MAX-2); + +// Assume-guarantee check via multi-objective (using ASYM rule) +"num_ag": multi(Pmax=? [ F "one_proc_err" ], P>=p_one_coin_ok [ G "one_coin_ok" ]) + +// Pareto query for assume-guarantee check +"pareto": multi(Pmax=? [ F "one_proc_err" ], Pmax=? [ G "one_coin_ok" ]) diff --git a/examples/multiobjective/mdp/dpm/dpm100.nm b/examples/multiobjective/mdp/dpm/dpm100.nm new file mode 100644 index 000000000..0862cfb73 --- /dev/null +++ b/examples/multiobjective/mdp/dpm/dpm100.nm @@ -0,0 +1,160 @@ +// power manager example +mdp + +const int QMAX =2; // 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 diff --git a/examples/multiobjective/mdp/dpm/dpm100_numerical.pctl b/examples/multiobjective/mdp/dpm/dpm100_numerical.pctl new file mode 100644 index 000000000..97de9f0e1 --- /dev/null +++ b/examples/multiobjective/mdp/dpm/dpm100_numerical.pctl @@ -0,0 +1,3 @@ + multi(R{"power"}min=? [ C<=100 ], R{"queue"}<=70 [ C<=100 ]) +// Note: The property file from http://www.prismmodelchecker.org/files/atva12mo/ does not provide a threshold for the second objective. +// We pick a threshold that intersects the pareto curve. \ No newline at end of file diff --git a/examples/multiobjective/mdp/dpm/dpm100_pareto.pctl b/examples/multiobjective/mdp/dpm/dpm100_pareto.pctl new file mode 100644 index 000000000..19e3cd685 --- /dev/null +++ b/examples/multiobjective/mdp/dpm/dpm100_pareto.pctl @@ -0,0 +1 @@ + multi(R{"power"}min=? [ C<=100 ], R{"queue"}min=? [ C<=100 ]) diff --git a/examples/multiobjective/mdp/dpm/dpm200.nm b/examples/multiobjective/mdp/dpm/dpm200.nm new file mode 100644 index 000000000..0862cfb73 --- /dev/null +++ b/examples/multiobjective/mdp/dpm/dpm200.nm @@ -0,0 +1,160 @@ +// power manager example +mdp + +const int QMAX =2; // 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 diff --git a/examples/multiobjective/mdp/dpm/dpm200_numerical.pctl b/examples/multiobjective/mdp/dpm/dpm200_numerical.pctl new file mode 100644 index 000000000..916821715 --- /dev/null +++ b/examples/multiobjective/mdp/dpm/dpm200_numerical.pctl @@ -0,0 +1,3 @@ + multi(R{"power"}min=? [ C<=200 ], R{"queue"}<=170 [ C<=200 ]) +// Note: The property file from http://www.prismmodelchecker.org/files/atva12mo/ does not provide a threshold for the second objective. +// We pick a threshold that intersects the pareto curve. \ No newline at end of file diff --git a/examples/multiobjective/mdp/dpm/dpm200_pareto.pctl b/examples/multiobjective/mdp/dpm/dpm200_pareto.pctl new file mode 100644 index 000000000..f03284f83 --- /dev/null +++ b/examples/multiobjective/mdp/dpm/dpm200_pareto.pctl @@ -0,0 +1 @@ + multi(R{"power"}min=? [ C<=200 ], R{"queue"}min=? [ C<=200 ]) diff --git a/examples/multiobjective/mdp/dpm/dpm300.nm b/examples/multiobjective/mdp/dpm/dpm300.nm new file mode 100644 index 000000000..3ac45e621 --- /dev/null +++ b/examples/multiobjective/mdp/dpm/dpm300.nm @@ -0,0 +1,160 @@ +// power manager example +mdp + +const int QMAX=2; // 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 diff --git a/examples/multiobjective/mdp/dpm/dpm300_numerical.pctl b/examples/multiobjective/mdp/dpm/dpm300_numerical.pctl new file mode 100644 index 000000000..a75851347 --- /dev/null +++ b/examples/multiobjective/mdp/dpm/dpm300_numerical.pctl @@ -0,0 +1,3 @@ + multi(R{"power"}min=? [ C<=300 ], R{"queue"}<=270 [ C<=300 ]) +// Note: The property file from http://www.prismmodelchecker.org/files/atva12mo/ does not provide a threshold for the second objective. +// We pick a threshold that intersects the pareto curve. \ No newline at end of file diff --git a/examples/multiobjective/mdp/dpm/dpm300_pareto.pctl b/examples/multiobjective/mdp/dpm/dpm300_pareto.pctl new file mode 100644 index 000000000..bde0f5eb0 --- /dev/null +++ b/examples/multiobjective/mdp/dpm/dpm300_pareto.pctl @@ -0,0 +1 @@ + multi(R{"power"}min=? [ C<=300 ], R{"queue"}min=? [ C<=300 ]) diff --git a/examples/multiobjective/mdp/dpm/origFiles/power-timed.nm b/examples/multiobjective/mdp/dpm/origFiles/power-timed.nm new file mode 100644 index 000000000..561742815 --- /dev/null +++ b/examples/multiobjective/mdp/dpm/origFiles/power-timed.nm @@ -0,0 +1,160 @@ +// 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 diff --git a/examples/multiobjective/mdp/dpm/origFiles/power-timed.pctl b/examples/multiobjective/mdp/dpm/origFiles/power-timed.pctl new file mode 100644 index 000000000..153141e7c --- /dev/null +++ b/examples/multiobjective/mdp/dpm/origFiles/power-timed.pctl @@ -0,0 +1,11 @@ +// 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 ]) diff --git a/examples/multiobjective/mdp/scheduler/origFiles/scheduler.pctl b/examples/multiobjective/mdp/scheduler/origFiles/scheduler.pctl new file mode 100644 index 000000000..24a09241b --- /dev/null +++ b/examples/multiobjective/mdp/scheduler/origFiles/scheduler.pctl @@ -0,0 +1,8 @@ +// Minimise expected completion time given a bound on expected energy usage +"num_time": multi(R{"time"}min=?[ C ], R{"energy"}<=1.45 [ C ]) + +// Minimise expected energy usage given a bound on expected completion time +"num_energy": multi( R{"energy"}min=?[ C ], R{"time"}<=1000[ C ]) + +// Pareto query for assume-guarantee check +"pareto": multi(R{"energy"}min=?[ C ], R{"time"}min=? [ C ]) diff --git a/examples/multiobjective/mdp/scheduler/origFiles/scheduler_prob2_K.nm b/examples/multiobjective/mdp/scheduler/origFiles/scheduler_prob2_K.nm new file mode 100644 index 000000000..22064b3cb --- /dev/null +++ b/examples/multiobjective/mdp/scheduler/origFiles/scheduler_prob2_K.nm @@ -0,0 +1,95 @@ +mdp + +label "tasks_complete" = (task6=3); + +const int K; + +module scheduler + + task1 : [0..3]; + task2 : [0..3]; + task3 : [0..3]; + task4 : [0..3]; + task5 : [0..3]; + task6 : [0..3]; + + [p1_add] task1=0 -> (task1'=1); + [p2_add] task1=0 -> (task1'=2); + [p1_mult] task2=0 -> (task2'=1); + [p2_mult] task2=0 -> (task2'=2); + [p1_mult] task3=0&task1=3 -> (task3'=1); + [p2_mult] task3=0&task1=3 -> (task3'=2); + [p1_add] task4=0&task1=3&task2=3 -> (task4'=1); + [p2_add] task4=0&task1=3&task2=3 -> (task4'=2); + [p1_mult] task5=0&task3=3 -> (task5'=1); + [p2_mult] task5=0&task3=3 -> (task5'=2); + [p1_add] task6=0&task4=3&task5=3 -> (task6'=1); + [p2_add] task6=0&task4=3&task5=3 -> (task6'=2); + [p1_done] task1=1 -> (task1'=3); + [p1_done] task2=1 -> (task2'=3); + [p1_done] task3=1 -> (task3'=3); + [p1_done] task4=1 -> (task4'=3); + [p1_done] task5=1 -> (task5'=3); + [p1_done] task6=1 -> (task6'=3); + [p2_done] task1=2 -> (task1'=3); + [p2_done] task2=2 -> (task2'=3); + [p2_done] task3=2 -> (task3'=3); + [p2_done] task4=2 -> (task4'=3); + [p2_done] task5=2 -> (task5'=3); + [p2_done] task6=2 -> (task6'=3); + [time] true -> 1.0 : true; + +endmodule + +module P1 + + p1 : [0..3]; + c1 : [0..2]; + x1 : [0..4*K+1]; + + [p1_add] (p1=0) -> (p1'=1) & (x1'=0); + [] (p1=1)&(x1=1*K)&(c1=0) -> 1/3 : (p1'=3) & (x1'=0) & (c1'=0) + 2/3 : (c1'=1) & (x1'=0); + [] (p1=1)&(x1=1*K)&(c1=1) -> 1/2 : (p1'=3) & (x1'=0) & (c1'=0) + 1/2 : (c1'=2) & (x1'=0); + [p1_done] (p1=1)&(x1=1*K)&(c1=2) -> (p1'=0) & (x1'=0) & (c1'=0); + [p1_mult] (p1=0) -> (p1'=2) & (x1'=0); + [] (p1=2)&(x1=2*K)&(c1=0) -> 1/3 : (p1'=3) & (x1'=0) & (c1'=0) + 2/3 : (c1'=1) & (x1'=0); + [] (p1=2)&(x1=1*K)&(c1=1) -> 1/2 : (p1'=3) & (x1'=0) & (c1'=0) + 1/2 : (c1'=2) & (x1'=0); + [p1_done] (p1=2)&(x1=1*K)&(c1=2) -> (p1'=0) & (x1'=0) & (c1'=0); + [p1_done] (p1=3) -> (p1'=0); + [time] (p1=1=>x1+1<=1*K)&((p1=2&c1=0)=>x1+1<=2*K)&((p1=2&c1>0)=>x1+1<=1*K)&(p1=3=>x1+1<=0) -> 1.0 : (x1'=min(x1+1,4*K+1)); + +endmodule + +module P2 + + p2 : [0..3]; + c2 : [0..2]; + x2 : [0..6*K+1]; + + [p2_add] (p2=0) -> (p2'=1) & (x2'=0); + [] (p2=1)&(x2=4*K)&(c2=0) -> 1/3 : (p2'=3) & (x2'=0) & (c2'=0) + 2/3 : (c2'=1) & (x2'=0); + [] (p2=1)&(x2=1)&(c2=1) -> 1/2 : (p2'=3) & (x2'=0) & (c2'=0) + 1/2 : (c2'=2) & (x2'=0); + [p2_done] (p2=1)&(x2=1)&(c2=2) -> (p2'=0) & (x2'=0) & (c2'=0); + [p2_mult] (p2=0) -> (p2'=2) & (x2'=0); + [] (p2=2)&(x2=6*K)&(c2=0) -> 1/3 : (p2'=3) & (x2'=0) & (c2'=0) + 2/3 : (c2'=1) & (x2'=0); + [] (p2=2)&(x2=1)&(c2=1) -> 1/2 : (p2'=3) & (x2'=0) & (c2'=0) + 1/2 : (c2'=2) & (x2'=0); + [p2_done] (p2=2)&(x2=1)&(c2=2) -> (p2'=0) & (x2'=0) & (c2'=0); + [p2_done] (p2=3) -> (p2'=0); + [time] ((p2=1&c2=0)=>x2+1<=4*K)&((p2=1&c2>0)=>x2+1<=1)&((p2=2&c2=0)=>x2+1<=6*K)&((p2=2&c2>0)=>x2+1<=1)&(p2=3=>x2+1<=0) -> 1.0 : (x2'=min(x2+1,6*K+1)); + +endmodule + +rewards "time" + + [time] true : 1/K; + +endrewards + +rewards "energy" + + [time] p1=0 : 10/(1000*K); + [time] p1>0 : 90/(1000*K); + [time] p2=0 : 20/(1000*K); + [time] p2>0 : 30/(1000*K); + +endrewards diff --git a/examples/multiobjective/mdp/scheduler/scheduler05.nm b/examples/multiobjective/mdp/scheduler/scheduler05.nm new file mode 100644 index 000000000..00f4e9539 --- /dev/null +++ b/examples/multiobjective/mdp/scheduler/scheduler05.nm @@ -0,0 +1,95 @@ +mdp + +label "tasks_complete" = (task6=3); + +const int K=5; + +module scheduler + + task1 : [0..3]; + task2 : [0..3]; + task3 : [0..3]; + task4 : [0..3]; + task5 : [0..3]; + task6 : [0..3]; + + [p1_add] task1=0 -> (task1'=1); + [p2_add] task1=0 -> (task1'=2); + [p1_mult] task2=0 -> (task2'=1); + [p2_mult] task2=0 -> (task2'=2); + [p1_mult] task3=0&task1=3 -> (task3'=1); + [p2_mult] task3=0&task1=3 -> (task3'=2); + [p1_add] task4=0&task1=3&task2=3 -> (task4'=1); + [p2_add] task4=0&task1=3&task2=3 -> (task4'=2); + [p1_mult] task5=0&task3=3 -> (task5'=1); + [p2_mult] task5=0&task3=3 -> (task5'=2); + [p1_add] task6=0&task4=3&task5=3 -> (task6'=1); + [p2_add] task6=0&task4=3&task5=3 -> (task6'=2); + [p1_done] task1=1 -> (task1'=3); + [p1_done] task2=1 -> (task2'=3); + [p1_done] task3=1 -> (task3'=3); + [p1_done] task4=1 -> (task4'=3); + [p1_done] task5=1 -> (task5'=3); + [p1_done] task6=1 -> (task6'=3); + [p2_done] task1=2 -> (task1'=3); + [p2_done] task2=2 -> (task2'=3); + [p2_done] task3=2 -> (task3'=3); + [p2_done] task4=2 -> (task4'=3); + [p2_done] task5=2 -> (task5'=3); + [p2_done] task6=2 -> (task6'=3); + [time] true -> 1.0 : true; + +endmodule + +module P1 + + p1 : [0..3]; + c1 : [0..2]; + x1 : [0..4*K+1]; + + [p1_add] (p1=0) -> (p1'=1) & (x1'=0); + [] (p1=1)&(x1=1*K)&(c1=0) -> 1/3 : (p1'=3) & (x1'=0) & (c1'=0) + 2/3 : (c1'=1) & (x1'=0); + [] (p1=1)&(x1=1*K)&(c1=1) -> 1/2 : (p1'=3) & (x1'=0) & (c1'=0) + 1/2 : (c1'=2) & (x1'=0); + [p1_done] (p1=1)&(x1=1*K)&(c1=2) -> (p1'=0) & (x1'=0) & (c1'=0); + [p1_mult] (p1=0) -> (p1'=2) & (x1'=0); + [] (p1=2)&(x1=2*K)&(c1=0) -> 1/3 : (p1'=3) & (x1'=0) & (c1'=0) + 2/3 : (c1'=1) & (x1'=0); + [] (p1=2)&(x1=1*K)&(c1=1) -> 1/2 : (p1'=3) & (x1'=0) & (c1'=0) + 1/2 : (c1'=2) & (x1'=0); + [p1_done] (p1=2)&(x1=1*K)&(c1=2) -> (p1'=0) & (x1'=0) & (c1'=0); + [p1_done] (p1=3) -> (p1'=0); + [time] (p1=1=>x1+1<=1*K)&((p1=2&c1=0)=>x1+1<=2*K)&((p1=2&c1>0)=>x1+1<=1*K)&(p1=3=>x1+1<=0) -> 1.0 : (x1'=min(x1+1,4*K+1)); + +endmodule + +module P2 + + p2 : [0..3]; + c2 : [0..2]; + x2 : [0..6*K+1]; + + [p2_add] (p2=0) -> (p2'=1) & (x2'=0); + [] (p2=1)&(x2=4*K)&(c2=0) -> 1/3 : (p2'=3) & (x2'=0) & (c2'=0) + 2/3 : (c2'=1) & (x2'=0); + [] (p2=1)&(x2=1)&(c2=1) -> 1/2 : (p2'=3) & (x2'=0) & (c2'=0) + 1/2 : (c2'=2) & (x2'=0); + [p2_done] (p2=1)&(x2=1)&(c2=2) -> (p2'=0) & (x2'=0) & (c2'=0); + [p2_mult] (p2=0) -> (p2'=2) & (x2'=0); + [] (p2=2)&(x2=6*K)&(c2=0) -> 1/3 : (p2'=3) & (x2'=0) & (c2'=0) + 2/3 : (c2'=1) & (x2'=0); + [] (p2=2)&(x2=1)&(c2=1) -> 1/2 : (p2'=3) & (x2'=0) & (c2'=0) + 1/2 : (c2'=2) & (x2'=0); + [p2_done] (p2=2)&(x2=1)&(c2=2) -> (p2'=0) & (x2'=0) & (c2'=0); + [p2_done] (p2=3) -> (p2'=0); + [time] ((p2=1&c2=0)=>x2+1<=4*K)&((p2=1&c2>0)=>x2+1<=1)&((p2=2&c2=0)=>x2+1<=6*K)&((p2=2&c2>0)=>x2+1<=1)&(p2=3=>x2+1<=0) -> 1.0 : (x2'=min(x2+1,6*K+1)); + +endmodule + +rewards "time" + + [time] true : 1/K; + +endrewards + +rewards "energy" + + [time] p1=0 : 10/(1000*K); + [time] p1>0 : 90/(1000*K); + [time] p2=0 : 20/(1000*K); + [time] p2>0 : 30/(1000*K); + +endrewards diff --git a/examples/multiobjective/mdp/scheduler/scheduler05_numerical.pctl b/examples/multiobjective/mdp/scheduler/scheduler05_numerical.pctl new file mode 100644 index 000000000..47ab6d61e --- /dev/null +++ b/examples/multiobjective/mdp/scheduler/scheduler05_numerical.pctl @@ -0,0 +1,4 @@ +multi(R{"time"}min=?[ F "tasks_complete" ], R{"energy"}<=1.45 [ F "tasks_complete" ]) +// Original query: +//multi(R{"time"}min=?[ C ], R{"energy"}<=1.45 [ C ]) +// Note that the min values are actually infinity and prism (currently) gives wrong results for this, e.g., R{"time"}min=?[ C<=2000 ] gives a larger value than multi(R{"time"}min=?[ C ], R{"energy"}<=1.45 [ C ]) . \ No newline at end of file diff --git a/examples/multiobjective/mdp/scheduler/scheduler05_pareto.pctl b/examples/multiobjective/mdp/scheduler/scheduler05_pareto.pctl new file mode 100644 index 000000000..b95651662 --- /dev/null +++ b/examples/multiobjective/mdp/scheduler/scheduler05_pareto.pctl @@ -0,0 +1,4 @@ + multi(R{"time"}min=?[ F "tasks_complete" ], R{"energy"}min=? [ F "tasks_complete" ]) + // Original query: + //multi(R{"energy"}min=?[ C ], R{"time"}min=? [ C ]) +// Note that the min values are actually infinity and prism (currently) gives wrong results for this, e.g., R{"time"}min=?[ C<=2000 ] gives a larger value than multi(R{"time"}min=?[ C ], R{"energy"}<=1.45 [ C ]) . \ No newline at end of file diff --git a/examples/multiobjective/mdp/scheduler/scheduler25.nm b/examples/multiobjective/mdp/scheduler/scheduler25.nm new file mode 100644 index 000000000..2e75dfbdd --- /dev/null +++ b/examples/multiobjective/mdp/scheduler/scheduler25.nm @@ -0,0 +1,95 @@ +mdp + +label "tasks_complete" = (task6=3); + +const int K=25; + +module scheduler + + task1 : [0..3]; + task2 : [0..3]; + task3 : [0..3]; + task4 : [0..3]; + task5 : [0..3]; + task6 : [0..3]; + + [p1_add] task1=0 -> (task1'=1); + [p2_add] task1=0 -> (task1'=2); + [p1_mult] task2=0 -> (task2'=1); + [p2_mult] task2=0 -> (task2'=2); + [p1_mult] task3=0&task1=3 -> (task3'=1); + [p2_mult] task3=0&task1=3 -> (task3'=2); + [p1_add] task4=0&task1=3&task2=3 -> (task4'=1); + [p2_add] task4=0&task1=3&task2=3 -> (task4'=2); + [p1_mult] task5=0&task3=3 -> (task5'=1); + [p2_mult] task5=0&task3=3 -> (task5'=2); + [p1_add] task6=0&task4=3&task5=3 -> (task6'=1); + [p2_add] task6=0&task4=3&task5=3 -> (task6'=2); + [p1_done] task1=1 -> (task1'=3); + [p1_done] task2=1 -> (task2'=3); + [p1_done] task3=1 -> (task3'=3); + [p1_done] task4=1 -> (task4'=3); + [p1_done] task5=1 -> (task5'=3); + [p1_done] task6=1 -> (task6'=3); + [p2_done] task1=2 -> (task1'=3); + [p2_done] task2=2 -> (task2'=3); + [p2_done] task3=2 -> (task3'=3); + [p2_done] task4=2 -> (task4'=3); + [p2_done] task5=2 -> (task5'=3); + [p2_done] task6=2 -> (task6'=3); + [time] true -> 1.0 : true; + +endmodule + +module P1 + + p1 : [0..3]; + c1 : [0..2]; + x1 : [0..4*K+1]; + + [p1_add] (p1=0) -> (p1'=1) & (x1'=0); + [] (p1=1)&(x1=1*K)&(c1=0) -> 1/3 : (p1'=3) & (x1'=0) & (c1'=0) + 2/3 : (c1'=1) & (x1'=0); + [] (p1=1)&(x1=1*K)&(c1=1) -> 1/2 : (p1'=3) & (x1'=0) & (c1'=0) + 1/2 : (c1'=2) & (x1'=0); + [p1_done] (p1=1)&(x1=1*K)&(c1=2) -> (p1'=0) & (x1'=0) & (c1'=0); + [p1_mult] (p1=0) -> (p1'=2) & (x1'=0); + [] (p1=2)&(x1=2*K)&(c1=0) -> 1/3 : (p1'=3) & (x1'=0) & (c1'=0) + 2/3 : (c1'=1) & (x1'=0); + [] (p1=2)&(x1=1*K)&(c1=1) -> 1/2 : (p1'=3) & (x1'=0) & (c1'=0) + 1/2 : (c1'=2) & (x1'=0); + [p1_done] (p1=2)&(x1=1*K)&(c1=2) -> (p1'=0) & (x1'=0) & (c1'=0); + [p1_done] (p1=3) -> (p1'=0); + [time] (p1=1=>x1+1<=1*K)&((p1=2&c1=0)=>x1+1<=2*K)&((p1=2&c1>0)=>x1+1<=1*K)&(p1=3=>x1+1<=0) -> 1.0 : (x1'=min(x1+1,4*K+1)); + +endmodule + +module P2 + + p2 : [0..3]; + c2 : [0..2]; + x2 : [0..6*K+1]; + + [p2_add] (p2=0) -> (p2'=1) & (x2'=0); + [] (p2=1)&(x2=4*K)&(c2=0) -> 1/3 : (p2'=3) & (x2'=0) & (c2'=0) + 2/3 : (c2'=1) & (x2'=0); + [] (p2=1)&(x2=1)&(c2=1) -> 1/2 : (p2'=3) & (x2'=0) & (c2'=0) + 1/2 : (c2'=2) & (x2'=0); + [p2_done] (p2=1)&(x2=1)&(c2=2) -> (p2'=0) & (x2'=0) & (c2'=0); + [p2_mult] (p2=0) -> (p2'=2) & (x2'=0); + [] (p2=2)&(x2=6*K)&(c2=0) -> 1/3 : (p2'=3) & (x2'=0) & (c2'=0) + 2/3 : (c2'=1) & (x2'=0); + [] (p2=2)&(x2=1)&(c2=1) -> 1/2 : (p2'=3) & (x2'=0) & (c2'=0) + 1/2 : (c2'=2) & (x2'=0); + [p2_done] (p2=2)&(x2=1)&(c2=2) -> (p2'=0) & (x2'=0) & (c2'=0); + [p2_done] (p2=3) -> (p2'=0); + [time] ((p2=1&c2=0)=>x2+1<=4*K)&((p2=1&c2>0)=>x2+1<=1)&((p2=2&c2=0)=>x2+1<=6*K)&((p2=2&c2>0)=>x2+1<=1)&(p2=3=>x2+1<=0) -> 1.0 : (x2'=min(x2+1,6*K+1)); + +endmodule + +rewards "time" + + [time] true : 1/K; + +endrewards + +rewards "energy" + + [time] p1=0 : 10/(1000*K); + [time] p1>0 : 90/(1000*K); + [time] p2=0 : 20/(1000*K); + [time] p2>0 : 30/(1000*K); + +endrewards diff --git a/examples/multiobjective/mdp/scheduler/scheduler25_numerical.pctl b/examples/multiobjective/mdp/scheduler/scheduler25_numerical.pctl new file mode 100644 index 000000000..6d91107a4 --- /dev/null +++ b/examples/multiobjective/mdp/scheduler/scheduler25_numerical.pctl @@ -0,0 +1,4 @@ + multi(R{"time"}min=?[ F "tasks_complete" ], R{"energy"}<=1.45 [ F "tasks_complete" ]) +// Original query: +// multi(R{"time"}min=?[ C ], R{"energy"}<=1.45 [ C ]) +// Note that the min values are actually infinity and prism (currently) gives wrong results for this, e.g., R{"time"}min=?[ C<=2000 ] gives a larger value than multi(R{"time"}min=?[ C ], R{"energy"}<=1.45 [ C ]) . \ No newline at end of file diff --git a/examples/multiobjective/mdp/scheduler/scheduler25_pareto.pctl b/examples/multiobjective/mdp/scheduler/scheduler25_pareto.pctl new file mode 100644 index 000000000..d818ef6d2 --- /dev/null +++ b/examples/multiobjective/mdp/scheduler/scheduler25_pareto.pctl @@ -0,0 +1,4 @@ + multi(R{"time"}min=?[ F "tasks_complete" ], R{"energy"}min=? [ F "tasks_complete" ]) +// Original query: +// multi(R{"energy"}min=?[ C ], R{"time"}min=? [ C ]) +// Note that the min values are actually infinity and prism (currently) gives wrong results for this, e.g., R{"time"}min=?[ C<=2000 ] gives a larger value than multi(R{"time"}min=?[ C ], R{"energy"}<=1.45 [ C ]) . \ No newline at end of file diff --git a/examples/multiobjective/mdp/scheduler/scheduler50.nm b/examples/multiobjective/mdp/scheduler/scheduler50.nm new file mode 100644 index 000000000..5e44a94fc --- /dev/null +++ b/examples/multiobjective/mdp/scheduler/scheduler50.nm @@ -0,0 +1,95 @@ +mdp + +label "tasks_complete" = (task6=3); + +const int K=50; + +module scheduler + + task1 : [0..3]; + task2 : [0..3]; + task3 : [0..3]; + task4 : [0..3]; + task5 : [0..3]; + task6 : [0..3]; + + [p1_add] task1=0 -> (task1'=1); + [p2_add] task1=0 -> (task1'=2); + [p1_mult] task2=0 -> (task2'=1); + [p2_mult] task2=0 -> (task2'=2); + [p1_mult] task3=0&task1=3 -> (task3'=1); + [p2_mult] task3=0&task1=3 -> (task3'=2); + [p1_add] task4=0&task1=3&task2=3 -> (task4'=1); + [p2_add] task4=0&task1=3&task2=3 -> (task4'=2); + [p1_mult] task5=0&task3=3 -> (task5'=1); + [p2_mult] task5=0&task3=3 -> (task5'=2); + [p1_add] task6=0&task4=3&task5=3 -> (task6'=1); + [p2_add] task6=0&task4=3&task5=3 -> (task6'=2); + [p1_done] task1=1 -> (task1'=3); + [p1_done] task2=1 -> (task2'=3); + [p1_done] task3=1 -> (task3'=3); + [p1_done] task4=1 -> (task4'=3); + [p1_done] task5=1 -> (task5'=3); + [p1_done] task6=1 -> (task6'=3); + [p2_done] task1=2 -> (task1'=3); + [p2_done] task2=2 -> (task2'=3); + [p2_done] task3=2 -> (task3'=3); + [p2_done] task4=2 -> (task4'=3); + [p2_done] task5=2 -> (task5'=3); + [p2_done] task6=2 -> (task6'=3); + [time] true -> 1.0 : true; + +endmodule + +module P1 + + p1 : [0..3]; + c1 : [0..2]; + x1 : [0..4*K+1]; + + [p1_add] (p1=0) -> (p1'=1) & (x1'=0); + [] (p1=1)&(x1=1*K)&(c1=0) -> 1/3 : (p1'=3) & (x1'=0) & (c1'=0) + 2/3 : (c1'=1) & (x1'=0); + [] (p1=1)&(x1=1*K)&(c1=1) -> 1/2 : (p1'=3) & (x1'=0) & (c1'=0) + 1/2 : (c1'=2) & (x1'=0); + [p1_done] (p1=1)&(x1=1*K)&(c1=2) -> (p1'=0) & (x1'=0) & (c1'=0); + [p1_mult] (p1=0) -> (p1'=2) & (x1'=0); + [] (p1=2)&(x1=2*K)&(c1=0) -> 1/3 : (p1'=3) & (x1'=0) & (c1'=0) + 2/3 : (c1'=1) & (x1'=0); + [] (p1=2)&(x1=1*K)&(c1=1) -> 1/2 : (p1'=3) & (x1'=0) & (c1'=0) + 1/2 : (c1'=2) & (x1'=0); + [p1_done] (p1=2)&(x1=1*K)&(c1=2) -> (p1'=0) & (x1'=0) & (c1'=0); + [p1_done] (p1=3) -> (p1'=0); + [time] (p1=1=>x1+1<=1*K)&((p1=2&c1=0)=>x1+1<=2*K)&((p1=2&c1>0)=>x1+1<=1*K)&(p1=3=>x1+1<=0) -> 1.0 : (x1'=min(x1+1,4*K+1)); + +endmodule + +module P2 + + p2 : [0..3]; + c2 : [0..2]; + x2 : [0..6*K+1]; + + [p2_add] (p2=0) -> (p2'=1) & (x2'=0); + [] (p2=1)&(x2=4*K)&(c2=0) -> 1/3 : (p2'=3) & (x2'=0) & (c2'=0) + 2/3 : (c2'=1) & (x2'=0); + [] (p2=1)&(x2=1)&(c2=1) -> 1/2 : (p2'=3) & (x2'=0) & (c2'=0) + 1/2 : (c2'=2) & (x2'=0); + [p2_done] (p2=1)&(x2=1)&(c2=2) -> (p2'=0) & (x2'=0) & (c2'=0); + [p2_mult] (p2=0) -> (p2'=2) & (x2'=0); + [] (p2=2)&(x2=6*K)&(c2=0) -> 1/3 : (p2'=3) & (x2'=0) & (c2'=0) + 2/3 : (c2'=1) & (x2'=0); + [] (p2=2)&(x2=1)&(c2=1) -> 1/2 : (p2'=3) & (x2'=0) & (c2'=0) + 1/2 : (c2'=2) & (x2'=0); + [p2_done] (p2=2)&(x2=1)&(c2=2) -> (p2'=0) & (x2'=0) & (c2'=0); + [p2_done] (p2=3) -> (p2'=0); + [time] ((p2=1&c2=0)=>x2+1<=4*K)&((p2=1&c2>0)=>x2+1<=1)&((p2=2&c2=0)=>x2+1<=6*K)&((p2=2&c2>0)=>x2+1<=1)&(p2=3=>x2+1<=0) -> 1.0 : (x2'=min(x2+1,6*K+1)); + +endmodule + +rewards "time" + + [time] true : 1/K; + +endrewards + +rewards "energy" + + [time] p1=0 : 10/(1000*K); + [time] p1>0 : 90/(1000*K); + [time] p2=0 : 20/(1000*K); + [time] p2>0 : 30/(1000*K); + +endrewards diff --git a/examples/multiobjective/mdp/scheduler/scheduler50_numerical.pctl b/examples/multiobjective/mdp/scheduler/scheduler50_numerical.pctl new file mode 100644 index 000000000..6d91107a4 --- /dev/null +++ b/examples/multiobjective/mdp/scheduler/scheduler50_numerical.pctl @@ -0,0 +1,4 @@ + multi(R{"time"}min=?[ F "tasks_complete" ], R{"energy"}<=1.45 [ F "tasks_complete" ]) +// Original query: +// multi(R{"time"}min=?[ C ], R{"energy"}<=1.45 [ C ]) +// Note that the min values are actually infinity and prism (currently) gives wrong results for this, e.g., R{"time"}min=?[ C<=2000 ] gives a larger value than multi(R{"time"}min=?[ C ], R{"energy"}<=1.45 [ C ]) . \ No newline at end of file diff --git a/examples/multiobjective/mdp/scheduler/scheduler50_pareto.pctl b/examples/multiobjective/mdp/scheduler/scheduler50_pareto.pctl new file mode 100644 index 000000000..9cb7bf3d0 --- /dev/null +++ b/examples/multiobjective/mdp/scheduler/scheduler50_pareto.pctl @@ -0,0 +1,4 @@ + multi(R{"time"}min=?[ F "tasks_complete" ], R{"energy"}min=? [ F "tasks_complete" ]) +// Original query: +// multi(R{"energy"}min=?[ C ], R{"time"}min=? [ C ]) +// Note that the min values are actually infinity and prism (currently) gives wrong results for this, e.g., R{"time"}min=?[ C<=2000 ] gives a larger value than multi(R{"time"}min=?[ C ], R{"energy"}<=1.45 [ C ]) . \ No newline at end of file diff --git a/examples/multiobjective/mdp/simple/simple.nm b/examples/multiobjective/mdp/simple/simple.nm new file mode 100644 index 000000000..04c1a0ea0 --- /dev/null +++ b/examples/multiobjective/mdp/simple/simple.nm @@ -0,0 +1,25 @@ + +mdp + +module simple + + // local state + s : [0..2] init 0; + + [A] s=0 -> 0.2 : (s'=1) + 0.8 : (s'=0); + [B] s=0 -> 1 : (s'=2); + [C] s=0 -> 1 : (s'=0); + [] s>0 -> 1 : (s'=s); +endmodule + +rewards "actA" + [A] true : 1; +endrewards + +rewards "actB" + [B] true : 2; +endrewards + + +label "a" = s=1; +label "b" = s=2; \ No newline at end of file diff --git a/examples/multiobjective/mdp/simple/simple.pctl b/examples/multiobjective/mdp/simple/simple.pctl new file mode 100644 index 000000000..91d5becac --- /dev/null +++ b/examples/multiobjective/mdp/simple/simple.pctl @@ -0,0 +1,2 @@ +multi(P<0.4 [ F "a"], P<0.3 [ F "b"] ) +//multi(Pmin=? [ F<=10 "a"], R<0.3 [ F "b" | "a"] ) \ No newline at end of file diff --git a/examples/multiobjective/mdp/team/origFiles/MDP_a2_r3_t2_full_exp.nm b/examples/multiobjective/mdp/team/origFiles/MDP_a2_r3_t2_full_exp.nm new file mode 100644 index 000000000..4c183b9cf --- /dev/null +++ b/examples/multiobjective/mdp/team/origFiles/MDP_a2_r3_t2_full_exp.nm @@ -0,0 +1,231 @@ +mdp + +// parameters +const int n_resources = 3; +const int n_tasks = 2; +const int n_sensors = 2; + + +// sensor resources +const int resource1=1; +const int resource2=2; + +// network configuration +const int e12=1; +const int e21=e12; + +module controller // schedules the algorithm + + // algorithm status + status : [0..5]; + + // task resource indicator variables + t1_r1 : [0..1]; + t1_r2 : [0..1]; + t1_r3 : [0..1]; + + t2_r1 : [0..1]; + t2_r2 : [0..1]; + t2_r3 : [0..1]; + + // schedule placeholders + turn1 : [0..n_sensors]; + turn2 : [0..n_sensors]; + + // selecting schedule uniformly at random + [] status=0 -> 1/2 : (turn1'=1) & (turn2'=2) & (status'=1) + + 1/2 : (turn1'=2) & (turn2'=1) & (status'=1); + + + // initialising non-empty tasks uniformly at random + [] status=1 -> 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2); + + // executing the schedule + + // 1st round + [str1] status=2 & turn1=1 -> (status'=2); + [fin1] status=2 & turn1=1 -> (status'=3); + [str2] status=2 & turn1=2 -> (status'=2); + [fin2] status=2 & turn1=2 -> (status'=3); + + // 2nd round + [str1] status=3 & turn2=1 -> (status'=3); + [fin1] status=3 & turn2=1 -> (status'=4); + [str2] status=3 & turn2=2 -> (status'=3); + [fin2] status=3 & turn2=2 -> (status'=4); + + + [] status=4 -> (status'=5); + + [] status=5 -> true; + +endmodule + +module sensor1 + + state1 : [0..1]; + + // team membership indicators + m1_t1 : [0..1]; + m1_t2 : [0..1]; + + // starting turn, selecting order of tasks + [str1] state1=0 -> (state1'=1); + + // if there is no team and has required skill - initiating the team + [] state1=1 & !committed & team_size_t1=0 & has_resource_t1 -> (m1_t1'=1); + [] state1=1 & !committed & team_size_t2=0 & has_resource_t2 -> (m1_t2'=1); + + // if team already exists and one of the neighbours is in it - joining the team + [] state1=1 & !committed & team_size_t1>0 & can_join_t1 & has_resource_t1 & !resource_filled_t1 -> (m1_t1'=1); + [] state1=1 & !committed & team_size_t2>0 & can_join_t2 & has_resource_t2 & !resource_filled_t2 -> (m1_t2'=1); + + [fin1] state1>0 -> (state1'=0); + +endmodule + +module sensor2 = sensor1 +[ + state1=state2, + + str1=str2, + fin1=fin2, + + m1_t1=m2_t1, + m1_t2=m2_t2, + + m2_t1=m1_t1, + m2_t2=m1_t2, + + resource1=resource2, + resource2=resource1, + + e12=e21, + e13=e23, + e14=e24, + e15=e25, + + e21=e12, + e23=e13, + e24=e14, + e25=e15 +] +endmodule + + + + +// agent is committed to some team +formula committed = (m1_t1+m1_t2) > 0; + +// formulae to compute team sizes +formula team_size_t1 = m1_t1+m2_t1; +formula team_size_t2 = m1_t2+m2_t2; + +// formulae to check whether the agent can join the team +formula can_join_t1 = e12*m2_t1 > 0; +formula can_join_t2 = e12*m2_t2 > 0; + +// formulae to check whether agent has the resource required by the task +formula has_resource_t1 = ( (t1_r1=1&resource1=1) | (t1_r2=1&resource1=2) | (t1_r3=1&resource1=3) ); +formula has_resource_t2 = ( (t2_r1=1&resource1=1) | (t2_r2=1&resource1=2) | (t2_r3=1&resource1=3) ); + +// formulae to check whether the resource of an agent has been already filled in the team +formula resource_filled_t1 = (m2_t1=1 & resource1=resource2); +formula resource_filled_t2 = (m2_t2=1 & resource1=resource2); + +// formula to compute team initiation probability (assuming each agent has at least one connection) +formula IP = (e12*(1-((m2_t1+m2_t2)=0?0:1))) / (e12); + + + + +// labels and formulae for property specification +formula finished = (status=4); +label "end" = (status=5); + + +formula task1_completed = finished + & ((t1_r1=1)=>((m1_t1=1&resource1=1)|(m2_t1=1&resource2=1))) + & ((t1_r2=1)=>((m1_t1=1&resource1=2)|(m2_t1=1&resource2=2))) + & ((t1_r3=1)=>((m1_t1=1&resource1=3)|(m2_t1=1&resource2=3))); + +formula task2_completed = finished + & ((t2_r1=1)=>((m1_t2=1&resource1=1)|(m2_t2=1&resource2=1))) + & ((t2_r2=1)=>((m1_t2=1&resource1=2)|(m2_t2=1&resource2=2))) + & ((t2_r3=1)=>((m1_t2=1&resource1=3)|(m2_t2=1&resource2=3))); + + + +formula agent1_joins_successful_team = (task1_completed & m1_t1=1) | (task2_completed & m1_t2=1); +formula agent1_joins_successful_team_of_1 = (task1_completed & m1_t1=1 & team_size_t1=1) | (task2_completed & m1_t2=1 & team_size_t2=1); +formula agent1_joins_successful_team_of_2 = (task1_completed & m1_t1=1 & team_size_t1=2) | (task2_completed & m1_t2=1 & team_size_t2=2); +formula agent1_joins_successful_team_of_3 = (task1_completed & m1_t1=1 & team_size_t1=3) | (task2_completed & m1_t2=1 & team_size_t2=3); + +formula agent2_joins_successful_team = (task1_completed & m2_t1=1) | (task2_completed & m2_t2=1); +formula agent2_joins_successful_team_of_1 = (task1_completed & m2_t1=1 & team_size_t1=1) | (task2_completed & m2_t2=1 & team_size_t2=1); +formula agent2_joins_successful_team_of_2 = (task1_completed & m2_t1=1 & team_size_t1=2) | (task2_completed & m2_t2=1 & team_size_t2=2); +formula agent2_joins_successful_team_of_3 = (task1_completed & m2_t1=1 & team_size_t1=3) | (task2_completed & m2_t2=1 & team_size_t2=3); + +// rewards +rewards "w_1_total" + [] agent1_joins_successful_team : 1; + [] agent2_joins_successful_team : 1; +endrewards + +rewards "w_2_total" + [] task1_completed : 1; + [] task2_completed : 1; +endrewards + + + + diff --git a/examples/multiobjective/mdp/team/origFiles/MDP_a3_r3_t2_full_exp.nm b/examples/multiobjective/mdp/team/origFiles/MDP_a3_r3_t2_full_exp.nm new file mode 100644 index 000000000..8a754d922 --- /dev/null +++ b/examples/multiobjective/mdp/team/origFiles/MDP_a3_r3_t2_full_exp.nm @@ -0,0 +1,287 @@ +mdp + +// parameters +const int n_resources = 3; +const int n_tasks = 2; +const int n_sensors = 3; + + +// sensor resources +const int resource1=1; +const int resource2=2; +const int resource3=3; + +// network configuration +const int e12=1; +const int e13=1; + +const int e21=e12; +const int e23=1; + +const int e31=e13; +const int e32=e23; + + +module controller // schedules the algorithm + + // algorithm status + status : [0..6]; + + // task resource indicator variables + t1_r1 : [0..1]; + t1_r2 : [0..1]; + t1_r3 : [0..1]; + + t2_r1 : [0..1]; + t2_r2 : [0..1]; + t2_r3 : [0..1]; + + // schedule placeholders + turn1 : [0..n_sensors]; + turn2 : [0..n_sensors]; + turn3 : [0..n_sensors]; + + // selecting schedule uniformly at random + [] status=0 -> 1/6 : (turn1'=1) & (turn2'=2) & (turn3'=3) & (status'=1) + + 1/6 : (turn1'=1) & (turn2'=3) & (turn3'=2) & (status'=1) + + 1/6 : (turn1'=2) & (turn2'=1) & (turn3'=3) & (status'=1) + + 1/6 : (turn1'=2) & (turn2'=3) & (turn3'=1) & (status'=1) + + 1/6 : (turn1'=3) & (turn2'=1) & (turn3'=2) & (status'=1) + + 1/6 : (turn1'=3) & (turn2'=2) & (turn3'=1) & (status'=1); + + + // initialising non-empty tasks uniformly at random + [] status=1 -> 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2); + + // executing the schedule + + // 1st round + [str1] status=2 & turn1=1 -> (status'=2); + [fin1] status=2 & turn1=1 -> (status'=3); + [str2] status=2 & turn1=2 -> (status'=2); + [fin2] status=2 & turn1=2 -> (status'=3); + [str3] status=2 & turn1=3 -> (status'=2); + [fin3] status=2 & turn1=3 -> (status'=3); + + // 2nd round + [str1] status=3 & turn2=1 -> (status'=3); + [fin1] status=3 & turn2=1 -> (status'=4); + [str2] status=3 & turn2=2 -> (status'=3); + [fin2] status=3 & turn2=2 -> (status'=4); + [str3] status=3 & turn2=3 -> (status'=3); + [fin3] status=3 & turn2=3 -> (status'=4); + + // 3rd round + [str1] status=4 & turn3=1 -> (status'=4); + [fin1] status=4 & turn3=1 -> (status'=5); + [str2] status=4 & turn3=2 -> (status'=4); + [fin2] status=4 & turn3=2 -> (status'=5); + [str3] status=4 & turn3=3 -> (status'=4); + [fin3] status=4 & turn3=3 -> (status'=5); + + [] status=5 -> (status'=6); + + [] status=6 -> true; + +endmodule + +module sensor1 + + state1 : [0..1]; + + // team membership indicators + m1_t1 : [0..1]; + m1_t2 : [0..1]; + + // starting turn, selecting order of tasks + [str1] state1=0 -> (state1'=1); + + // if there is no team and has required skill - initiating the team + [] state1=1 & !committed & team_size_t1=0 & has_resource_t1 -> (m1_t1'=1); + [] state1=1 & !committed & team_size_t2=0 & has_resource_t2 -> (m1_t2'=1); + + // if team already exists and one of the neighbours is in it - joining the team + [] state1=1 & !committed & team_size_t1>0 & can_join_t1 & has_resource_t1 & !resource_filled_t1 -> (m1_t1'=1); + [] state1=1 & !committed & team_size_t2>0 & can_join_t2 & has_resource_t2 & !resource_filled_t2 -> (m1_t2'=1); + + [fin1] state1>0 -> (state1'=0); + +endmodule + +module sensor2 = sensor1 +[ + state1=state2, + + str1=str2, + fin1=fin2, + + m1_t1=m2_t1, + m1_t2=m2_t2, + + m2_t1=m1_t1, + m2_t2=m1_t2, + + resource1=resource2, + resource2=resource1, + + e12=e21, + e13=e23, + e14=e24, + e15=e25, + + e21=e12, + e23=e13, + e24=e14, + e25=e15 +] +endmodule + +module sensor3 = sensor1 +[ + state1=state3, + + str1=str3, + fin1=fin3, + + m1_t1=m3_t1, + m1_t2=m3_t2, + m3_t1=m1_t1, + m3_t2=m1_t2, + + resource1=resource3, + resource3=resource1, + + e12=e32, + e13=e31, + e14=e34, + e15=e35, + + e31=e13, + e32=e12, + e34=e14, + e35=e15 +] +endmodule + + + +// agent is committed to some team +formula committed = (m1_t1+m1_t2) > 0; + +// formulae to compute team sizes +formula team_size_t1 = m1_t1+m2_t1+m3_t1; +formula team_size_t2 = m1_t2+m2_t2+m3_t2; + +// formulae to check whether the agent can join the team +formula can_join_t1 = e12*m2_t1 + e13*m3_t1 > 0; +formula can_join_t2 = e12*m2_t2 + e13*m3_t2 > 0; + +// formulae to check whether agent has the resource required by the task +formula has_resource_t1 = ( (t1_r1=1&resource1=1) | (t1_r2=1&resource1=2) | (t1_r3=1&resource1=3) ); +formula has_resource_t2 = ( (t2_r1=1&resource1=1) | (t2_r2=1&resource1=2) | (t2_r3=1&resource1=3) ); + +// formulae to check whether the resource of an agent has been already filled in the team +formula resource_filled_t1 = (m2_t1=1 & resource1=resource2) | (m3_t1=1 & resource1=resource3); +formula resource_filled_t2 = (m2_t2=1 & resource1=resource2) | (m3_t2=1 & resource1=resource3); + +// formula to compute team initiation probability (assuming each agent has at least one connection) +formula IP = (e12*(1-((m2_t1+m2_t2)=0?0:1))+e13*(1-((m3_t1+m3_t2)=0?0:1))) / (e12+e13); + + + + +// labels and formulae for property specification +formula finished = (status=5); +label "end" = (status=6); + + +formula task1_completed = finished + & ((t1_r1=1)=>((m1_t1=1&resource1=1)|(m2_t1=1&resource2=1)|(m3_t1=1&resource3=1))) + & ((t1_r2=1)=>((m1_t1=1&resource1=2)|(m2_t1=1&resource2=2)|(m3_t1=1&resource3=2))) + & ((t1_r3=1)=>((m1_t1=1&resource1=3)|(m2_t1=1&resource2=3)|(m3_t1=1&resource3=3))); + +formula task2_completed = finished + & ((t2_r1=1)=>((m1_t2=1&resource1=1)|(m2_t2=1&resource2=1)|(m3_t2=1&resource3=1))) + & ((t2_r2=1)=>((m1_t2=1&resource1=2)|(m2_t2=1&resource2=2)|(m3_t2=1&resource3=2))) + & ((t2_r3=1)=>((m1_t2=1&resource1=3)|(m2_t2=1&resource2=3)|(m3_t2=1&resource3=3))); + + + +formula agent1_joins_successful_team = (task1_completed & m1_t1=1) | (task2_completed & m1_t2=1); +formula agent1_joins_successful_team_of_1 = (task1_completed & m1_t1=1 & team_size_t1=1) | (task2_completed & m1_t2=1 & team_size_t2=1); +formula agent1_joins_successful_team_of_2 = (task1_completed & m1_t1=1 & team_size_t1=2) | (task2_completed & m1_t2=1 & team_size_t2=2); +formula agent1_joins_successful_team_of_3 = (task1_completed & m1_t1=1 & team_size_t1=3) | (task2_completed & m1_t2=1 & team_size_t2=3); + +formula agent2_joins_successful_team = (task1_completed & m2_t1=1) | (task2_completed & m2_t2=1); +formula agent2_joins_successful_team_of_1 = (task1_completed & m2_t1=1 & team_size_t1=1) | (task2_completed & m2_t2=1 & team_size_t2=1); +formula agent2_joins_successful_team_of_2 = (task1_completed & m2_t1=1 & team_size_t1=2) | (task2_completed & m2_t2=1 & team_size_t2=2); +formula agent2_joins_successful_team_of_3 = (task1_completed & m2_t1=1 & team_size_t1=3) | (task2_completed & m2_t2=1 & team_size_t2=3); + +formula agent3_joins_successful_team = (task1_completed & m3_t1=1) | (task2_completed & m3_t2=1); +formula agent3_joins_successful_team_of_1 = (task1_completed & m3_t1=1 & team_size_t1=1) | (task2_completed & m3_t2=1 & team_size_t2=1); +formula agent3_joins_successful_team_of_2 = (task1_completed & m3_t1=1 & team_size_t1=2) | (task2_completed & m3_t2=1 & team_size_t2=2); +formula agent3_joins_successful_team_of_3 = (task1_completed & m3_t1=1 & team_size_t1=3) | (task2_completed & m3_t2=1 & team_size_t2=3); + +// rewards +rewards "w_1_total" + [] agent1_joins_successful_team : 1; + [] agent2_joins_successful_team : 1; + [] agent3_joins_successful_team : 1; +endrewards + +rewards "w_2_total" + [] task1_completed : 1; + [] task2_completed : 1; +endrewards + + + + diff --git a/examples/multiobjective/mdp/team/origFiles/MDP_a4_r3_t2_full_exp.nm b/examples/multiobjective/mdp/team/origFiles/MDP_a4_r3_t2_full_exp.nm new file mode 100644 index 000000000..bc9e536c2 --- /dev/null +++ b/examples/multiobjective/mdp/team/origFiles/MDP_a4_r3_t2_full_exp.nm @@ -0,0 +1,364 @@ +mdp + +// parameters +const int n_resources = 3; +const int n_tasks = 2; +const int n_sensors = 4; + + +// sensor resources +const int resource1=1; +const int resource2=2; +const int resource3=3; +const int resource4=1; + +// network configuration +const int e12=1; +const int e13=1; +const int e14=1; + +const int e21=e12; +const int e23=1; +const int e24=1; + +const int e31=e13; +const int e32=e23; +const int e34=1; + +const int e41=e14; +const int e42=e24; +const int e43=e34; + + +module controller // schedules the algorithm + + // algorithm status + status : [0..7]; + + // task resource indicator variables + t1_r1 : [0..1]; + t1_r2 : [0..1]; + t1_r3 : [0..1]; + + t2_r1 : [0..1]; + t2_r2 : [0..1]; + t2_r3 : [0..1]; + + // schedule placeholders + turn1 : [0..n_sensors]; + turn2 : [0..n_sensors]; + turn3 : [0..n_sensors]; + turn4 : [0..n_sensors]; + turn5 : [0..n_sensors]; + + // selecting schedule uniformly at random + [] status=0 -> 1/24 : (turn1'=1) & (turn2'=2) & (turn3'=3) & (turn4'=4) & (status'=1) + + 1/24 : (turn1'=1) & (turn2'=2) & (turn3'=4) & (turn4'=3) & (status'=1) + + 1/24 : (turn1'=1) & (turn2'=3) & (turn3'=2) & (turn4'=4) & (status'=1) + + 1/24 : (turn1'=1) & (turn2'=3) & (turn3'=4) & (turn4'=2) & (status'=1) + + 1/24 : (turn1'=1) & (turn2'=4) & (turn3'=2) & (turn4'=3) & (status'=1) + + 1/24 : (turn1'=1) & (turn2'=4) & (turn3'=3) & (turn4'=2) & (status'=1) + + 1/24 : (turn1'=2) & (turn2'=1) & (turn3'=3) & (turn4'=4) & (status'=1) + + 1/24 : (turn1'=2) & (turn2'=1) & (turn3'=4) & (turn4'=3) & (status'=1) + + 1/24 : (turn1'=2) & (turn2'=3) & (turn3'=1) & (turn4'=4) & (status'=1) + + 1/24 : (turn1'=2) & (turn2'=3) & (turn3'=4) & (turn4'=1) & (status'=1) + + 1/24 : (turn1'=2) & (turn2'=4) & (turn3'=1) & (turn4'=3) & (status'=1) + + 1/24 : (turn1'=2) & (turn2'=4) & (turn3'=3) & (turn4'=1) & (status'=1) + + 1/24 : (turn1'=3) & (turn2'=1) & (turn3'=2) & (turn4'=4) & (status'=1) + + 1/24 : (turn1'=3) & (turn2'=1) & (turn3'=4) & (turn4'=2) & (status'=1) + + 1/24 : (turn1'=3) & (turn2'=2) & (turn3'=1) & (turn4'=4) & (status'=1) + + 1/24 : (turn1'=3) & (turn2'=2) & (turn3'=4) & (turn4'=1) & (status'=1) + + 1/24 : (turn1'=3) & (turn2'=4) & (turn3'=1) & (turn4'=2) & (status'=1) + + 1/24 : (turn1'=3) & (turn2'=4) & (turn3'=2) & (turn4'=1) & (status'=1) + + 1/24 : (turn1'=4) & (turn2'=1) & (turn3'=2) & (turn4'=3) & (status'=1) + + 1/24 : (turn1'=4) & (turn2'=1) & (turn3'=3) & (turn4'=2) & (status'=1) + + 1/24 : (turn1'=4) & (turn2'=2) & (turn3'=1) & (turn4'=3) & (status'=1) + + 1/24 : (turn1'=4) & (turn2'=2) & (turn3'=3) & (turn4'=1) & (status'=1) + + 1/24 : (turn1'=4) & (turn2'=3) & (turn3'=1) & (turn4'=2) & (status'=1) + + 1/24 : (turn1'=4) & (turn2'=3) & (turn3'=2) & (turn4'=1) & (status'=1); + + + // initialising non-empty tasks uniformly at random + [] status=1 -> 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2); + + // executing the schedule + + // 1st round + [str1] status=2 & turn1=1 -> (status'=2); + [fin1] status=2 & turn1=1 -> (status'=3); + [str2] status=2 & turn1=2 -> (status'=2); + [fin2] status=2 & turn1=2 -> (status'=3); + [str3] status=2 & turn1=3 -> (status'=2); + [fin3] status=2 & turn1=3 -> (status'=3); + [str4] status=2 & turn1=4 -> (status'=2); + [fin4] status=2 & turn1=4 -> (status'=3); + + // 2nd round + [str1] status=3 & turn2=1 -> (status'=3); + [fin1] status=3 & turn2=1 -> (status'=4); + [str2] status=3 & turn2=2 -> (status'=3); + [fin2] status=3 & turn2=2 -> (status'=4); + [str3] status=3 & turn2=3 -> (status'=3); + [fin3] status=3 & turn2=3 -> (status'=4); + [str4] status=3 & turn2=4 -> (status'=3); + [fin4] status=3 & turn2=4 -> (status'=4); + + // 3rd round + [str1] status=4 & turn3=1 -> (status'=4); + [fin1] status=4 & turn3=1 -> (status'=5); + [str2] status=4 & turn3=2 -> (status'=4); + [fin2] status=4 & turn3=2 -> (status'=5); + [str3] status=4 & turn3=3 -> (status'=4); + [fin3] status=4 & turn3=3 -> (status'=5); + [str4] status=4 & turn3=4 -> (status'=4); + [fin4] status=4 & turn3=4 -> (status'=5); + + // 4th round + [str1] status=5 & turn4=1 -> (status'=5); + [fin1] status=5 & turn4=1 -> (status'=6); + [str2] status=5 & turn4=2 -> (status'=5); + [fin2] status=5 & turn4=2 -> (status'=6); + [str3] status=5 & turn4=3 -> (status'=5); + [fin3] status=5 & turn4=3 -> (status'=6); + [str4] status=5 & turn4=4 -> (status'=5); + [fin4] status=5 & turn4=4 -> (status'=6); + + [] status=6 -> (status'=7); + + [] status=7 -> (status'=7); + +endmodule + +module sensor1 + + state1 : [0..1]; + + // team membership indicators + m1_t1 : [0..1]; + m1_t2 : [0..1]; + + // starting turn, selecting order of tasks + [str1] state1=0 -> (state1'=1); + + // if there is no team and has required skill - initiating the team + [] state1=1 & !committed & team_size_t1=0 & has_resource_t1 -> (m1_t1'=1); + [] state1=1 & !committed & team_size_t2=0 & has_resource_t2 -> (m1_t2'=1); + + // if team already exists and one of the neighbours is in it - joining the team + [] state1=1 & !committed & team_size_t1>0 & can_join_t1 & has_resource_t1 & !resource_filled_t1 -> (m1_t1'=1); + [] state1=1 & !committed & team_size_t2>0 & can_join_t2 & has_resource_t2 & !resource_filled_t2 -> (m1_t2'=1); + + [fin1] state1>0 -> (state1'=0); + +endmodule + +module sensor2 = sensor1 +[ + state1=state2, + + str1=str2, + fin1=fin2, + + m1_t1=m2_t1, + m1_t2=m2_t2, + + m2_t1=m1_t1, + m2_t2=m1_t2, + + resource1=resource2, + resource2=resource1, + + e12=e21, + e13=e23, + e14=e24, + e15=e25, + + e21=e12, + e23=e13, + e24=e14, + e25=e15 +] +endmodule + +module sensor3 = sensor1 +[ + state1=state3, + + str1=str3, + fin1=fin3, + + m1_t1=m3_t1, + m1_t2=m3_t2, + m3_t1=m1_t1, + m3_t2=m1_t2, + + resource1=resource3, + resource3=resource1, + + e12=e32, + e13=e31, + e14=e34, + e15=e35, + + e31=e13, + e32=e12, + e34=e14, + e35=e15 +] +endmodule + +module sensor4 = sensor1 +[ + state1=state4, + + str1=str4, + fin1=fin4, + + m1_t1=m4_t1, + m1_t2=m4_t2, + + m4_t1=m1_t1, + m4_t2=m1_t2, + + resource1=resource4, + resource4=resource1, + + e12=e42, + e13=e43, + e14=e41, + e15=e45, + + e41=e14, + e42=e12, + e43=e13, + e45=e15 +] +endmodule + + + +// agent is committed to some team +formula committed = (m1_t1+m1_t2) > 0; + +// formulae to compute team sizes +formula team_size_t1 = m1_t1+m2_t1+m3_t1+m4_t1; +formula team_size_t2 = m1_t2+m2_t2+m3_t2+m4_t2; + +// formulae to check whether the agent can join the team +formula can_join_t1 = e12*m2_t1 + e13*m3_t1 + e14*m4_t1 > 0; +formula can_join_t2 = e12*m2_t2 + e13*m3_t2 + e14*m4_t2 > 0; + +// formulae to check whether agent has the resource required by the task +formula has_resource_t1 = ( (t1_r1=1&resource1=1) | (t1_r2=1&resource1=2) | (t1_r3=1&resource1=3) ); +formula has_resource_t2 = ( (t2_r1=1&resource1=1) | (t2_r2=1&resource1=2) | (t2_r3=1&resource1=3) ); + +// formulae to check whether the resource of an agent has been already filled in the team +formula resource_filled_t1 = (m2_t1=1 & resource1=resource2) | (m3_t1=1 & resource1=resource3) | (m4_t1=1 & resource1=resource4); +formula resource_filled_t2 = (m2_t2=1 & resource1=resource2) | (m3_t2=1 & resource1=resource3) | (m4_t2=1 & resource1=resource4); + +// formula to compute team initiation probability (assuming each agent has at least one connection) +formula IP = (e12*(1-((m2_t1+m2_t2)=0?0:1))+e13*(1-((m3_t1+m3_t2)=0?0:1))+e14*(1-((m4_t1+m4_t2)=0?0:1))) / (e12+e13+e14); + + + + +// labels and formulae for property specification +formula finished = (status=6); +label "end" = (status=7); + + +formula agent1_joins_successful_team = (task1_completed & m1_t1=1) | (task2_completed & m1_t2=1); +formula agent1_joins_successful_team_of_1 = (task1_completed & m1_t1=1 & team_size_t1=1) | (task2_completed & m1_t2=1 & team_size_t2=1); +formula agent1_joins_successful_team_of_2 = (task1_completed & m1_t1=1 & team_size_t1=2) | (task2_completed & m1_t2=1 & team_size_t2=2); +formula agent1_joins_successful_team_of_3 = (task1_completed & m1_t1=1 & team_size_t1=3) | (task2_completed & m1_t2=1 & team_size_t2=3); + +formula agent2_joins_successful_team = (task1_completed & m2_t1=1) | (task2_completed & m2_t2=1); +formula agent2_joins_successful_team_of_1 = (task1_completed & m2_t1=1 & team_size_t1=1) | (task2_completed & m2_t2=1 & team_size_t2=1); +formula agent2_joins_successful_team_of_2 = (task1_completed & m2_t1=1 & team_size_t1=2) | (task2_completed & m2_t2=1 & team_size_t2=2); +formula agent2_joins_successful_team_of_3 = (task1_completed & m2_t1=1 & team_size_t1=3) | (task2_completed & m2_t2=1 & team_size_t2=3); + +formula agent3_joins_successful_team = (task1_completed & m3_t1=1) | (task2_completed & m3_t2=1); +formula agent3_joins_successful_team_of_1 = (task1_completed & m3_t1=1 & team_size_t1=1) | (task2_completed & m3_t2=1 & team_size_t2=1); +formula agent3_joins_successful_team_of_2 = (task1_completed & m3_t1=1 & team_size_t1=2) | (task2_completed & m3_t2=1 & team_size_t2=2); +formula agent3_joins_successful_team_of_3 = (task1_completed & m3_t1=1 & team_size_t1=3) | (task2_completed & m3_t2=1 & team_size_t2=3); + +formula agent4_joins_successful_team = (task1_completed & m4_t1=1) | (task2_completed & m4_t2=1); +formula agent4_joins_successful_team_of_1 = (task1_completed & m4_t1=1 & team_size_t1=1) | (task2_completed & m4_t2=1 & team_size_t2=1); +formula agent4_joins_successful_team_of_2 = (task1_completed & m4_t1=1 & team_size_t1=2) | (task2_completed & m4_t2=1 & team_size_t2=2); +formula agent4_joins_successful_team_of_3 = (task1_completed & m4_t1=1 & team_size_t1=3) | (task2_completed & m4_t2=1 & team_size_t2=3); + +formula task1_completed = finished + & ((t1_r1=1)=>((m1_t1=1&resource1=1)|(m2_t1=1&resource2=1)|(m3_t1=1&resource3=1)|(m4_t1=1&resource4=1))) + & ((t1_r2=1)=>((m1_t1=1&resource1=2)|(m2_t1=1&resource2=2)|(m3_t1=1&resource3=2)|(m4_t1=1&resource4=2))) + & ((t1_r3=1)=>((m1_t1=1&resource1=3)|(m2_t1=1&resource2=3)|(m3_t1=1&resource3=3)|(m4_t1=1&resource4=3))); + +formula task2_completed = finished + & ((t2_r1=1)=>((m1_t2=1&resource1=1)|(m2_t2=1&resource2=1)|(m3_t2=1&resource3=1)|(m4_t2=1&resource4=1))) + & ((t2_r2=1)=>((m1_t2=1&resource1=2)|(m2_t2=1&resource2=2)|(m3_t2=1&resource3=2)|(m4_t2=1&resource4=2))) + & ((t2_r3=1)=>((m1_t2=1&resource1=3)|(m2_t2=1&resource2=3)|(m3_t2=1&resource3=3)|(m4_t2=1&resource4=3))); + +// rewards +rewards "w_1_total" + [] agent1_joins_successful_team : 1; + [] agent2_joins_successful_team : 1; + [] agent3_joins_successful_team : 1; + [] agent4_joins_successful_team : 1; +endrewards + +rewards "w_2_total" + [] task1_completed : 1; + [] task2_completed : 1; +endrewards + + + + + diff --git a/examples/multiobjective/mdp/team/origFiles/MDP_a5_r3_t2_full_exp.nm b/examples/multiobjective/mdp/team/origFiles/MDP_a5_r3_t2_full_exp.nm new file mode 100644 index 000000000..4a02b8746 --- /dev/null +++ b/examples/multiobjective/mdp/team/origFiles/MDP_a5_r3_t2_full_exp.nm @@ -0,0 +1,531 @@ +mdp + +// parameters +const int n_resources = 3; +const int n_tasks = 2; +const int n_sensors = 5; + + +// sensor resources +const int resource1=1; +const int resource2=2; +const int resource3=3; +const int resource4=1; +const int resource5=2; + +// network configuration +const int e12=1; +const int e13=1; +const int e14=1; +const int e15=1; + +const int e21=e12; +const int e23=1; +const int e24=1; +const int e25=1; + +const int e31=e13; +const int e32=e23; +const int e34=1; +const int e35=1; + +const int e41=e14; +const int e42=e24; +const int e43=e34; +const int e45=1; + +const int e51=e15; +const int e52=e25; +const int e53=e35; +const int e54=e45; + +module controller // schedules the algorithm + + // algorithm status + status : [0..8]; + + // task resource indicator variables + t1_r1 : [0..1]; + t1_r2 : [0..1]; + t1_r3 : [0..1]; + + t2_r1 : [0..1]; + t2_r2 : [0..1]; + t2_r3 : [0..1]; + + // schedule placeholders + turn1 : [0..n_sensors]; + turn2 : [0..n_sensors]; + turn3 : [0..n_sensors]; + turn4 : [0..n_sensors]; + turn5 : [0..n_sensors]; + + // selecting schedule uniformly at random + [] status=0 -> 1/120 : (turn1'=1) & (turn2'=2) & (turn3'=3) & (turn4'=4) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=2) & (turn3'=3) & (turn4'=5) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=2) & (turn3'=4) & (turn4'=3) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=2) & (turn3'=4) & (turn4'=5) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=2) & (turn3'=5) & (turn4'=3) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=2) & (turn3'=5) & (turn4'=4) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=3) & (turn3'=2) & (turn4'=4) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=3) & (turn3'=2) & (turn4'=5) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=3) & (turn3'=4) & (turn4'=2) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=3) & (turn3'=4) & (turn4'=5) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=3) & (turn3'=5) & (turn4'=2) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=3) & (turn3'=5) & (turn4'=4) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=4) & (turn3'=2) & (turn4'=3) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=4) & (turn3'=2) & (turn4'=5) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=4) & (turn3'=3) & (turn4'=2) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=4) & (turn3'=3) & (turn4'=5) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=4) & (turn3'=5) & (turn4'=2) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=4) & (turn3'=5) & (turn4'=3) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=5) & (turn3'=2) & (turn4'=3) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=5) & (turn3'=2) & (turn4'=4) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=5) & (turn3'=3) & (turn4'=2) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=5) & (turn3'=3) & (turn4'=4) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=5) & (turn3'=4) & (turn4'=2) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=5) & (turn3'=4) & (turn4'=3) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=1) & (turn3'=3) & (turn4'=4) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=1) & (turn3'=3) & (turn4'=5) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=1) & (turn3'=4) & (turn4'=3) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=1) & (turn3'=4) & (turn4'=5) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=1) & (turn3'=5) & (turn4'=3) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=1) & (turn3'=5) & (turn4'=4) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=3) & (turn3'=1) & (turn4'=4) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=3) & (turn3'=1) & (turn4'=5) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=3) & (turn3'=4) & (turn4'=1) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=3) & (turn3'=4) & (turn4'=5) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=3) & (turn3'=5) & (turn4'=1) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=3) & (turn3'=5) & (turn4'=4) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=4) & (turn3'=1) & (turn4'=3) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=4) & (turn3'=1) & (turn4'=5) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=4) & (turn3'=3) & (turn4'=1) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=4) & (turn3'=3) & (turn4'=5) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=4) & (turn3'=5) & (turn4'=1) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=4) & (turn3'=5) & (turn4'=3) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=5) & (turn3'=1) & (turn4'=3) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=5) & (turn3'=1) & (turn4'=4) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=5) & (turn3'=3) & (turn4'=1) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=5) & (turn3'=3) & (turn4'=4) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=5) & (turn3'=4) & (turn4'=1) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=5) & (turn3'=4) & (turn4'=3) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=1) & (turn3'=2) & (turn4'=4) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=1) & (turn3'=2) & (turn4'=5) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=1) & (turn3'=4) & (turn4'=2) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=1) & (turn3'=4) & (turn4'=5) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=1) & (turn3'=5) & (turn4'=2) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=1) & (turn3'=5) & (turn4'=4) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=2) & (turn3'=1) & (turn4'=4) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=2) & (turn3'=1) & (turn4'=5) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=2) & (turn3'=4) & (turn4'=1) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=2) & (turn3'=4) & (turn4'=5) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=2) & (turn3'=5) & (turn4'=1) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=2) & (turn3'=5) & (turn4'=4) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=4) & (turn3'=1) & (turn4'=2) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=4) & (turn3'=1) & (turn4'=5) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=4) & (turn3'=2) & (turn4'=1) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=4) & (turn3'=2) & (turn4'=5) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=4) & (turn3'=5) & (turn4'=1) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=4) & (turn3'=5) & (turn4'=2) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=5) & (turn3'=1) & (turn4'=2) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=5) & (turn3'=1) & (turn4'=4) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=5) & (turn3'=2) & (turn4'=1) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=5) & (turn3'=2) & (turn4'=4) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=5) & (turn3'=4) & (turn4'=1) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=5) & (turn3'=4) & (turn4'=2) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=1) & (turn3'=2) & (turn4'=3) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=1) & (turn3'=2) & (turn4'=5) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=1) & (turn3'=3) & (turn4'=2) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=1) & (turn3'=3) & (turn4'=5) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=1) & (turn3'=5) & (turn4'=2) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=1) & (turn3'=5) & (turn4'=3) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=2) & (turn3'=1) & (turn4'=3) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=2) & (turn3'=1) & (turn4'=5) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=2) & (turn3'=3) & (turn4'=1) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=2) & (turn3'=3) & (turn4'=5) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=2) & (turn3'=5) & (turn4'=1) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=2) & (turn3'=5) & (turn4'=3) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=3) & (turn3'=1) & (turn4'=2) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=3) & (turn3'=1) & (turn4'=5) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=3) & (turn3'=2) & (turn4'=1) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=3) & (turn3'=2) & (turn4'=5) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=3) & (turn3'=5) & (turn4'=1) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=3) & (turn3'=5) & (turn4'=2) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=5) & (turn3'=1) & (turn4'=2) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=5) & (turn3'=1) & (turn4'=3) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=5) & (turn3'=2) & (turn4'=1) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=5) & (turn3'=2) & (turn4'=3) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=5) & (turn3'=3) & (turn4'=1) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=5) & (turn3'=3) & (turn4'=2) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=1) & (turn3'=2) & (turn4'=3) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=1) & (turn3'=2) & (turn4'=4) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=1) & (turn3'=3) & (turn4'=2) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=1) & (turn3'=3) & (turn4'=4) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=1) & (turn3'=4) & (turn4'=2) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=1) & (turn3'=4) & (turn4'=3) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=2) & (turn3'=1) & (turn4'=3) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=2) & (turn3'=1) & (turn4'=4) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=2) & (turn3'=3) & (turn4'=1) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=2) & (turn3'=3) & (turn4'=4) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=2) & (turn3'=4) & (turn4'=1) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=2) & (turn3'=4) & (turn4'=3) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=3) & (turn3'=1) & (turn4'=2) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=3) & (turn3'=1) & (turn4'=4) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=3) & (turn3'=2) & (turn4'=1) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=3) & (turn3'=2) & (turn4'=4) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=3) & (turn3'=4) & (turn4'=1) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=3) & (turn3'=4) & (turn4'=2) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=4) & (turn3'=1) & (turn4'=2) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=4) & (turn3'=1) & (turn4'=3) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=4) & (turn3'=2) & (turn4'=1) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=4) & (turn3'=2) & (turn4'=3) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=4) & (turn3'=3) & (turn4'=1) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=4) & (turn3'=3) & (turn4'=2) & (turn5'=1) & (status'=1); + + + // initialising non-empty tasks uniformly at random + [] status=1 -> 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2); + + // executing the schedule + + // 1st round + [str1] status=2 & turn1=1 -> (status'=2); + [fin1] status=2 & turn1=1 -> (status'=3); + [str2] status=2 & turn1=2 -> (status'=2); + [fin2] status=2 & turn1=2 -> (status'=3); + [str3] status=2 & turn1=3 -> (status'=2); + [fin3] status=2 & turn1=3 -> (status'=3); + [str4] status=2 & turn1=4 -> (status'=2); + [fin4] status=2 & turn1=4 -> (status'=3); + [str5] status=2 & turn1=5 -> (status'=2); + [fin5] status=2 & turn1=5 -> (status'=3); + + // 2nd round + [str1] status=3 & turn2=1 -> (status'=3); + [fin1] status=3 & turn2=1 -> (status'=4); + [str2] status=3 & turn2=2 -> (status'=3); + [fin2] status=3 & turn2=2 -> (status'=4); + [str3] status=3 & turn2=3 -> (status'=3); + [fin3] status=3 & turn2=3 -> (status'=4); + [str4] status=3 & turn2=4 -> (status'=3); + [fin4] status=3 & turn2=4 -> (status'=4); + [str5] status=3 & turn2=5 -> (status'=3); + [fin5] status=3 & turn2=5 -> (status'=4); + + // 3rd round + [str1] status=4 & turn3=1 -> (status'=4); + [fin1] status=4 & turn3=1 -> (status'=5); + [str2] status=4 & turn3=2 -> (status'=4); + [fin2] status=4 & turn3=2 -> (status'=5); + [str3] status=4 & turn3=3 -> (status'=4); + [fin3] status=4 & turn3=3 -> (status'=5); + [str4] status=4 & turn3=4 -> (status'=4); + [fin4] status=4 & turn3=4 -> (status'=5); + [str5] status=4 & turn3=5 -> (status'=4); + [fin5] status=4 & turn3=5 -> (status'=5); + + // 4th round + [str1] status=5 & turn4=1 -> (status'=5); + [fin1] status=5 & turn4=1 -> (status'=6); + [str2] status=5 & turn4=2 -> (status'=5); + [fin2] status=5 & turn4=2 -> (status'=6); + [str3] status=5 & turn4=3 -> (status'=5); + [fin3] status=5 & turn4=3 -> (status'=6); + [str4] status=5 & turn4=4 -> (status'=5); + [fin4] status=5 & turn4=4 -> (status'=6); + [str5] status=5 & turn4=5 -> (status'=5); + [fin5] status=5 & turn4=5 -> (status'=6); + + // 5th round + [str1] status=6 & turn5=1 -> (status'=6); + [fin1] status=6 & turn5=1 -> (status'=7); + [str2] status=6 & turn5=2 -> (status'=6); + [fin2] status=6 & turn5=2 -> (status'=7); + [str3] status=6 & turn5=3 -> (status'=6); + [fin3] status=6 & turn5=3 -> (status'=7); + [str4] status=6 & turn5=4 -> (status'=6); + [fin4] status=6 & turn5=4 -> (status'=7); + [str5] status=6 & turn5=5 -> (status'=6); + [fin5] status=6 & turn5=5 -> (status'=7); + + [] status=7 -> (status'=8); + + [] status=8 -> (status'=8); + +endmodule + +module sensor1 + + state1 : [0..1]; + + // team membership indicators + m1_t1 : [0..1]; + m1_t2 : [0..1]; + + // starting turn, selecting order of tasks + [str1] state1=0 -> (state1'=1); + + // if there is no team and has required skill - initiating the team + [] state1=1 & !committed & team_size_t1=0 & has_resource_t1 -> (m1_t1'=1); + [] state1=1 & !committed & team_size_t2=0 & has_resource_t2 -> (m1_t2'=1); + + // if team already exists and one of the neighbours is in it - joining the team + [] state1=1 & !committed & team_size_t1>0 & can_join_t1 & has_resource_t1 & !resource_filled_t1 -> (m1_t1'=1); + [] state1=1 & !committed & team_size_t2>0 & can_join_t2 & has_resource_t2 & !resource_filled_t2 -> (m1_t2'=1); + + [fin1] state1>0 -> (state1'=0); + +endmodule + +module sensor2 = sensor1 +[ + state1=state2, + + str1=str2, + fin1=fin2, + + m1_t1=m2_t1, + m1_t2=m2_t2, + + m2_t1=m1_t1, + m2_t2=m1_t2, + + resource1=resource2, + resource2=resource1, + + e12=e21, + e13=e23, + e14=e24, + e15=e25, + + e21=e12, + e23=e13, + e24=e14, + e25=e15 +] +endmodule + +module sensor3 = sensor1 +[ + state1=state3, + + str1=str3, + fin1=fin3, + + m1_t1=m3_t1, + m1_t2=m3_t2, + m3_t1=m1_t1, + m3_t2=m1_t2, + + resource1=resource3, + resource3=resource1, + + e12=e32, + e13=e31, + e14=e34, + e15=e35, + + e31=e13, + e32=e12, + e34=e14, + e35=e15 +] +endmodule + +module sensor4 = sensor1 +[ + state1=state4, + + str1=str4, + fin1=fin4, + + m1_t1=m4_t1, + m1_t2=m4_t2, + + m4_t1=m1_t1, + m4_t2=m1_t2, + + resource1=resource4, + resource4=resource1, + + e12=e42, + e13=e43, + e14=e41, + e15=e45, + + e41=e14, + e42=e12, + e43=e13, + e45=e15 +] +endmodule + +module sensor5 = sensor1 +[ + state1=state5, + + str1=str5, + fin1=fin5, + + m1_t1=m5_t1, + m1_t2=m5_t2, + + m5_t1=m1_t1, + m5_t2=m1_t2, + + resource1=resource5, + resource5=resource1, + + e12=e52, + e13=e53, + e14=e54, + e15=e51, + + e51=e15, + e52=e12, + e53=e13, + e54=e14 +] +endmodule + +// formulae for scheduling +formula s1_sched = (turn1=1 | turn2=1 | turn3=1 | turn4=1 | turn5=1); +formula s2_sched = (turn1=2 | turn2=2 | turn3=2 | turn4=2 | turn5=2); +formula s3_sched = (turn1=3 | turn2=3 | turn3=3 | turn4=3 | turn5=3); +formula s4_sched = (turn1=4 | turn2=4 | turn3=4 | turn4=4 | turn5=4); +formula s5_sched = (turn1=5 | turn2=5 | turn3=5 | turn4=5 | turn5=5); +formula all_not_sched = !(s1_sched | s2_sched | s3_sched | s4_sched | s5_sched); +formula all_sched = (s1_sched & s2_sched & s3_sched & s4_sched & s5_sched); + + +// agent is committed to some team +formula committed = (m1_t1+m1_t2) > 0; + +// formulae to compute team sizes +formula team_size_t1 = m1_t1+m2_t1+m3_t1+m4_t1+m5_t1; +formula team_size_t2 = m1_t2+m2_t2+m3_t2+m4_t2+m5_t2; + +// formulae to check whether the agent can join the team +formula can_join_t1 = e12*m2_t1 + e13*m3_t1 + e14*m4_t1 + e15*m5_t1 > 0; +formula can_join_t2 = e12*m2_t2 + e13*m3_t2 + e14*m4_t2 + e15*m5_t2 > 0; + +// formulae to check whether agent has the resource required by the task +formula has_resource_t1 = ( (t1_r1=1&resource1=1) | (t1_r2=1&resource1=2) | (t1_r3=1&resource1=3) ); +formula has_resource_t2 = ( (t2_r1=1&resource1=1) | (t2_r2=1&resource1=2) | (t2_r3=1&resource1=3) ); + +// formulae to check whether the resource of an agent has been already filled in the team +formula resource_filled_t1 = (m2_t1=1 & resource1=resource2) | (m3_t1=1 & resource1=resource3) | (m4_t1=1 & resource1=resource4) | (m5_t1=1 & resource1=resource5); +formula resource_filled_t2 = (m2_t2=1 & resource1=resource2) | (m3_t2=1 & resource1=resource3) | (m4_t2=1 & resource1=resource4) | (m5_t2=1 & resource1=resource5); + +// formula to compute team initiation probability (assuming each agent has at least one connection) +formula IP = (e12*(1-((m2_t1+m2_t2)=0?0:1))+e13*(1-((m3_t1+m3_t2)=0?0:1))+e14*(1-((m4_t1+m4_t2)=0?0:1))+e15*(1-((m5_t1+m5_t2)=0?0:1))) / (e12+e13+e14+e15); + + + + +// labels and formulae for property specification +formula finished = (status=7); +label "end" = (status=8); + + +formula agent1_joins_successful_team = (task1_completed & m1_t1=1) | (task2_completed & m1_t2=1); +formula agent1_joins_successful_team_of_1 = (task1_completed & m1_t1=1 & team_size_t1=1) | (task2_completed & m1_t2=1 & team_size_t2=1); +formula agent1_joins_successful_team_of_2 = (task1_completed & m1_t1=1 & team_size_t1=2) | (task2_completed & m1_t2=1 & team_size_t2=2); +formula agent1_joins_successful_team_of_3 = (task1_completed & m1_t1=1 & team_size_t1=3) | (task2_completed & m1_t2=1 & team_size_t2=3); + +formula agent2_joins_successful_team = (task1_completed & m2_t1=1) | (task2_completed & m2_t2=1); +formula agent2_joins_successful_team_of_1 = (task1_completed & m2_t1=1 & team_size_t1=1) | (task2_completed & m2_t2=1 & team_size_t2=1); +formula agent2_joins_successful_team_of_2 = (task1_completed & m2_t1=1 & team_size_t1=2) | (task2_completed & m2_t2=1 & team_size_t2=2); +formula agent2_joins_successful_team_of_3 = (task1_completed & m2_t1=1 & team_size_t1=3) | (task2_completed & m2_t2=1 & team_size_t2=3); + +formula agent3_joins_successful_team = (task1_completed & m3_t1=1) | (task2_completed & m3_t2=1); +formula agent3_joins_successful_team_of_1 = (task1_completed & m3_t1=1 & team_size_t1=1) | (task2_completed & m3_t2=1 & team_size_t2=1); +formula agent3_joins_successful_team_of_2 = (task1_completed & m3_t1=1 & team_size_t1=2) | (task2_completed & m3_t2=1 & team_size_t2=2); +formula agent3_joins_successful_team_of_3 = (task1_completed & m3_t1=1 & team_size_t1=3) | (task2_completed & m3_t2=1 & team_size_t2=3); + +formula agent4_joins_successful_team = (task1_completed & m4_t1=1) | (task2_completed & m4_t2=1); +formula agent4_joins_successful_team_of_1 = (task1_completed & m4_t1=1 & team_size_t1=1) | (task2_completed & m4_t2=1 & team_size_t2=1); +formula agent4_joins_successful_team_of_2 = (task1_completed & m4_t1=1 & team_size_t1=2) | (task2_completed & m4_t2=1 & team_size_t2=2); +formula agent4_joins_successful_team_of_3 = (task1_completed & m4_t1=1 & team_size_t1=3) | (task2_completed & m4_t2=1 & team_size_t2=3); + +formula agent5_joins_successful_team = (task1_completed & m5_t1=1) | (task2_completed & m5_t2=1); +formula agent5_joins_successful_team_of_1 = (task1_completed & m5_t1=1 & team_size_t1=1) | (task2_completed & m5_t2=1 & team_size_t2=1); +formula agent5_joins_successful_team_of_2 = (task1_completed & m5_t1=1 & team_size_t1=2) | (task2_completed & m5_t2=1 & team_size_t2=2); +formula agent5_joins_successful_team_of_3 = (task1_completed & m5_t1=1 & team_size_t1=3) | (task2_completed & m5_t2=1 & team_size_t2=3); + +formula task1_completed = finished + & ((t1_r1=1)=>((m1_t1=1&resource1=1)|(m2_t1=1&resource2=1)|(m3_t1=1&resource3=1)|(m4_t1=1&resource4=1)|(m5_t1=1&resource5=1))) + & ((t1_r2=1)=>((m1_t1=1&resource1=2)|(m2_t1=1&resource2=2)|(m3_t1=1&resource3=2)|(m4_t1=1&resource4=2)|(m5_t1=1&resource5=2))) + & ((t1_r3=1)=>((m1_t1=1&resource1=3)|(m2_t1=1&resource2=3)|(m3_t1=1&resource3=3)|(m4_t1=1&resource4=3)|(m5_t1=1&resource5=3))); + +formula task2_completed = finished + & ((t2_r1=1)=>((m1_t2=1&resource1=1)|(m2_t2=1&resource2=1)|(m3_t2=1&resource3=1)|(m4_t2=1&resource4=1)|(m5_t2=1&resource5=1))) + & ((t2_r2=1)=>((m1_t2=1&resource1=2)|(m2_t2=1&resource2=2)|(m3_t2=1&resource3=2)|(m4_t2=1&resource4=2)|(m5_t2=1&resource5=2))) + & ((t2_r3=1)=>((m1_t2=1&resource1=3)|(m2_t2=1&resource2=3)|(m3_t2=1&resource3=3)|(m4_t2=1&resource4=3)|(m5_t2=1&resource5=3))); + +// rewards +rewards "w_1_total" + [] agent1_joins_successful_team : 1; + [] agent2_joins_successful_team : 1; + [] agent3_joins_successful_team : 1; + [] agent4_joins_successful_team : 1; + [] agent5_joins_successful_team : 1; +endrewards + +rewards "w_2_total" + [] task1_completed : 1; + [] task2_completed : 1; +endrewards + + + + + diff --git a/examples/multiobjective/mdp/team/origFiles/team.pctl b/examples/multiobjective/mdp/team/origFiles/team.pctl new file mode 100644 index 000000000..6d262eceb --- /dev/null +++ b/examples/multiobjective/mdp/team/origFiles/team.pctl @@ -0,0 +1,45 @@ + +// Max probability of completing task 1 +"single_task1": Pmax=? [ F task1_completed ] + +// Max possible expected W1 (size of successful team) +"single_w1": R{"w_1_total"}max=? [ F "end" ] + +// Max possible expected W2 (num tasks completed) +"single_w2": R{"w_2_total"}max=? [ F "end" ] + +// Values computed using above queries: +const double q1 = +n_sensors=2 ? 0.9795918367346945 : +n_sensors=3 ? 2.3265306122448983 : +n_sensors=4 ? 2.551020408163265 : +n_sensors=5 ? 2.8979591836734775 : +0.0; + +const double q2 = +n_sensors=2 ? 0.7142857142857146 : +n_sensors=3 ? 1.2448979591836744 : +n_sensors=4 ? 1.4285714285714293 : +n_sensors=5 ? 1.6734693877551006 : +0.0; + +// Numerical: maximise probability of completing task 1 +// with 95% of possible value for expected W1 (size of successful team) +"num_task1": multi(Pmax=? [ F task1_completed ], R{"w_1_total"}>=(0.95*q1) [ F true ]) + +// Numerical (3-objective): maximise probability of completing task 1 +// with 95% of possible value for expected W1 (size of successful team) +// and also at least 0.5 probability of completing task 2 +"num_task1_3": multi(Pmax=? [ F task1_completed ], R{"w_1_total"}>=(0.95*q1) [ F true ], P>=0.5 [ F task2_completed ]) + +// Other numerical queries: + +multi(R{"w_1_total"}max=? [ F true ], R{"w_2_total"}>=(0.95*q2) [ F true ]) + +multi(R{"w_2_total"}max=? [ F true ], R{"w_1_total"}>=(0.95*q1) [ F true ]) + +// Pareto: maximise probability of completing task 1 and expected W1 (size of successful team) +"pareto": multi(Pmax=? [ F task1_completed ], R{"w_1_total"}max=? [ F true ]) + +// Pareto (3-objective): maximise probability of completing tasks 1/2 and expected W1 (size of successful team) +"pareto3": multi(Pmax=? [ F task1_completed ], R{"w_1_total"}max=? [ F true ], Pmax=? [ F task2_completed ]) diff --git a/examples/multiobjective/mdp/team/team2obj_3.nm b/examples/multiobjective/mdp/team/team2obj_3.nm new file mode 100644 index 000000000..cd17863ca --- /dev/null +++ b/examples/multiobjective/mdp/team/team2obj_3.nm @@ -0,0 +1,286 @@ +mdp + +// parameters +const int n_resources = 3; +const int n_tasks = 2; +const int n_sensors = 3; + + +// sensor resources +const int resource1=1; +const int resource2=2; +const int resource3=3; + +// network configuration +const int e12=1; +const int e13=1; + +const int e21=e12; +const int e23=1; + +const int e31=e13; +const int e32=e23; + + +// agent is committed to some team +formula committed = (m1_t1+m1_t2) > 0; + +// formulae to compute team sizes +formula team_size_t1 = m1_t1+m2_t1+m3_t1; +formula team_size_t2 = m1_t2+m2_t2+m3_t2; + +// formulae to check whether the agent can join the team +formula can_join_t1 = e12*m2_t1 + e13*m3_t1 > 0; +formula can_join_t2 = e12*m2_t2 + e13*m3_t2 > 0; + +// formulae to check whether agent has the resource required by the task +formula has_resource_t1 = ( (t1_r1=1&resource1=1) | (t1_r2=1&resource1=2) | (t1_r3=1&resource1=3) ); +formula has_resource_t2 = ( (t2_r1=1&resource1=1) | (t2_r2=1&resource1=2) | (t2_r3=1&resource1=3) ); + +// formulae to check whether the resource of an agent has been already filled in the team +formula resource_filled_t1 = (m2_t1=1 & resource1=resource2) | (m3_t1=1 & resource1=resource3); +formula resource_filled_t2 = (m2_t2=1 & resource1=resource2) | (m3_t2=1 & resource1=resource3); + +// formula to compute team initiation probability (assuming each agent has at least one connection) +formula IP = (e12*(1-((m2_t1+m2_t2)=0?0:1))+e13*(1-((m3_t1+m3_t2)=0?0:1))) / (e12+e13); + +module controller // schedules the algorithm + + // algorithm status + status : [0..6]; + + // task resource indicator variables + t1_r1 : [0..1]; + t1_r2 : [0..1]; + t1_r3 : [0..1]; + + t2_r1 : [0..1]; + t2_r2 : [0..1]; + t2_r3 : [0..1]; + + // schedule placeholders + turn1 : [0..n_sensors]; + turn2 : [0..n_sensors]; + turn3 : [0..n_sensors]; + + // selecting schedule uniformly at random + [] status=0 -> 1/6 : (turn1'=1) & (turn2'=2) & (turn3'=3) & (status'=1) + + 1/6 : (turn1'=1) & (turn2'=3) & (turn3'=2) & (status'=1) + + 1/6 : (turn1'=2) & (turn2'=1) & (turn3'=3) & (status'=1) + + 1/6 : (turn1'=2) & (turn2'=3) & (turn3'=1) & (status'=1) + + 1/6 : (turn1'=3) & (turn2'=1) & (turn3'=2) & (status'=1) + + 1/6 : (turn1'=3) & (turn2'=2) & (turn3'=1) & (status'=1); + + + // initialising non-empty tasks uniformly at random + [] status=1 -> 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2); + + // executing the schedule + + // 1st round + [str1] status=2 & turn1=1 -> (status'=2); + [fin1] status=2 & turn1=1 -> (status'=3); + [str2] status=2 & turn1=2 -> (status'=2); + [fin2] status=2 & turn1=2 -> (status'=3); + [str3] status=2 & turn1=3 -> (status'=2); + [fin3] status=2 & turn1=3 -> (status'=3); + + // 2nd round + [str1] status=3 & turn2=1 -> (status'=3); + [fin1] status=3 & turn2=1 -> (status'=4); + [str2] status=3 & turn2=2 -> (status'=3); + [fin2] status=3 & turn2=2 -> (status'=4); + [str3] status=3 & turn2=3 -> (status'=3); + [fin3] status=3 & turn2=3 -> (status'=4); + + // 3rd round + [str1] status=4 & turn3=1 -> (status'=4); + [fin1] status=4 & turn3=1 -> (status'=5); + [str2] status=4 & turn3=2 -> (status'=4); + [fin2] status=4 & turn3=2 -> (status'=5); + [str3] status=4 & turn3=3 -> (status'=4); + [fin3] status=4 & turn3=3 -> (status'=5); + + [] status=5 -> (status'=6); + + [] status=6 -> true; + +endmodule + +module sensor1 + + state1 : [0..1]; + + // team membership indicators + m1_t1 : [0..1]; + m1_t2 : [0..1]; + + // starting turn, selecting order of tasks + [str1] state1=0 -> (state1'=1); + + // if there is no team and has required skill - initiating the team + [] state1=1 & !committed & team_size_t1=0 & has_resource_t1 -> (m1_t1'=1); + [] state1=1 & !committed & team_size_t2=0 & has_resource_t2 -> (m1_t2'=1); + + // if team already exists and one of the neighbours is in it - joining the team + [] state1=1 & !committed & team_size_t1>0 & can_join_t1 & has_resource_t1 & !resource_filled_t1 -> (m1_t1'=1); + [] state1=1 & !committed & team_size_t2>0 & can_join_t2 & has_resource_t2 & !resource_filled_t2 -> (m1_t2'=1); + + [fin1] state1>0 -> (state1'=0); + +endmodule + +module sensor2 = sensor1 +[ + state1=state2, + + str1=str2, + fin1=fin2, + + m1_t1=m2_t1, + m1_t2=m2_t2, + + m2_t1=m1_t1, + m2_t2=m1_t2, + + resource1=resource2, + resource2=resource1, + + e12=e21, + e13=e23, + e14=e24, + e15=e25, + + e21=e12, + e23=e13, + e24=e14, + e25=e15 +] +endmodule + +module sensor3 = sensor1 +[ + state1=state3, + + str1=str3, + fin1=fin3, + + m1_t1=m3_t1, + m1_t2=m3_t2, + m3_t1=m1_t1, + m3_t2=m1_t2, + + resource1=resource3, + resource3=resource1, + + e12=e32, + e13=e31, + e14=e34, + e15=e35, + + e31=e13, + e32=e12, + e34=e14, + e35=e15 +] +endmodule + + + + + +// labels and formulae for property specification +formula finished = (status=5); +label "end" = (status=6); + + +formula task1_completed = finished + & ((t1_r1=1)=>((m1_t1=1&resource1=1)|(m2_t1=1&resource2=1)|(m3_t1=1&resource3=1))) + & ((t1_r2=1)=>((m1_t1=1&resource1=2)|(m2_t1=1&resource2=2)|(m3_t1=1&resource3=2))) + & ((t1_r3=1)=>((m1_t1=1&resource1=3)|(m2_t1=1&resource2=3)|(m3_t1=1&resource3=3))); + +formula task2_completed = finished + & ((t2_r1=1)=>((m1_t2=1&resource1=1)|(m2_t2=1&resource2=1)|(m3_t2=1&resource3=1))) + & ((t2_r2=1)=>((m1_t2=1&resource1=2)|(m2_t2=1&resource2=2)|(m3_t2=1&resource3=2))) + & ((t2_r3=1)=>((m1_t2=1&resource1=3)|(m2_t2=1&resource2=3)|(m3_t2=1&resource3=3))); + + + +formula agent1_joins_successful_team = (task1_completed & m1_t1=1) | (task2_completed & m1_t2=1); +formula agent1_joins_successful_team_of_1 = (task1_completed & m1_t1=1 & team_size_t1=1) | (task2_completed & m1_t2=1 & team_size_t2=1); +formula agent1_joins_successful_team_of_2 = (task1_completed & m1_t1=1 & team_size_t1=2) | (task2_completed & m1_t2=1 & team_size_t2=2); +formula agent1_joins_successful_team_of_3 = (task1_completed & m1_t1=1 & team_size_t1=3) | (task2_completed & m1_t2=1 & team_size_t2=3); + +formula agent2_joins_successful_team = (task1_completed & m2_t1=1) | (task2_completed & m2_t2=1); +formula agent2_joins_successful_team_of_1 = (task1_completed & m2_t1=1 & team_size_t1=1) | (task2_completed & m2_t2=1 & team_size_t2=1); +formula agent2_joins_successful_team_of_2 = (task1_completed & m2_t1=1 & team_size_t1=2) | (task2_completed & m2_t2=1 & team_size_t2=2); +formula agent2_joins_successful_team_of_3 = (task1_completed & m2_t1=1 & team_size_t1=3) | (task2_completed & m2_t2=1 & team_size_t2=3); + +formula agent3_joins_successful_team = (task1_completed & m3_t1=1) | (task2_completed & m3_t2=1); +formula agent3_joins_successful_team_of_1 = (task1_completed & m3_t1=1 & team_size_t1=1) | (task2_completed & m3_t2=1 & team_size_t2=1); +formula agent3_joins_successful_team_of_2 = (task1_completed & m3_t1=1 & team_size_t1=2) | (task2_completed & m3_t2=1 & team_size_t2=2); +formula agent3_joins_successful_team_of_3 = (task1_completed & m3_t1=1 & team_size_t1=3) | (task2_completed & m3_t2=1 & team_size_t2=3); + +// rewards +rewards "w_1_total" + [] agent1_joins_successful_team : 1; + [] agent2_joins_successful_team : 1; + [] agent3_joins_successful_team : 1; +endrewards + +rewards "w_2_total" + [] task1_completed : 1; + [] task2_completed : 1; +endrewards + + + + diff --git a/examples/multiobjective/mdp/team/team2obj_3_numerical.pctl b/examples/multiobjective/mdp/team/team2obj_3_numerical.pctl new file mode 100644 index 000000000..f6a4553cb --- /dev/null +++ b/examples/multiobjective/mdp/team/team2obj_3_numerical.pctl @@ -0,0 +1 @@ + multi(Pmax=? [ F task1_completed ], R{"w_1_total"}>=2.210204082 [ C ]) diff --git a/examples/multiobjective/mdp/team/team2obj_3_pareto.pctl b/examples/multiobjective/mdp/team/team2obj_3_pareto.pctl new file mode 100644 index 000000000..636cd1772 --- /dev/null +++ b/examples/multiobjective/mdp/team/team2obj_3_pareto.pctl @@ -0,0 +1 @@ + multi(Pmax=? [ F task1_completed ], R{"w_1_total"}max=? [ C ]) diff --git a/examples/multiobjective/mdp/team/team2obj_4.nm b/examples/multiobjective/mdp/team/team2obj_4.nm new file mode 100644 index 000000000..2ad4536e3 --- /dev/null +++ b/examples/multiobjective/mdp/team/team2obj_4.nm @@ -0,0 +1,368 @@ +mdp + +// parameters +const int n_resources = 3; +const int n_tasks = 2; +const int n_sensors = 4; + + +// sensor resources +const int resource1=1; +const int resource2=2; +const int resource3=3; +const int resource4=1; + +// network configuration +const int e12=1; +const int e13=1; +const int e14=1; + +const int e21=e12; +const int e23=1; +const int e24=1; + +const int e31=e13; +const int e32=e23; +const int e34=1; + +const int e41=e14; +const int e42=e24; +const int e43=e34; + + + + +// agent is committed to some team +formula committed = (m1_t1+m1_t2) > 0; + +// formulae to compute team sizes +formula team_size_t1 = m1_t1+m2_t1+m3_t1+m4_t1; +formula team_size_t2 = m1_t2+m2_t2+m3_t2+m4_t2; + +// formulae to check whether the agent can join the team +formula can_join_t1 = e12*m2_t1 + e13*m3_t1 + e14*m4_t1 > 0; +formula can_join_t2 = e12*m2_t2 + e13*m3_t2 + e14*m4_t2 > 0; + +// formulae to check whether agent has the resource required by the task +formula has_resource_t1 = ( (t1_r1=1&resource1=1) | (t1_r2=1&resource1=2) | (t1_r3=1&resource1=3) ); +formula has_resource_t2 = ( (t2_r1=1&resource1=1) | (t2_r2=1&resource1=2) | (t2_r3=1&resource1=3) ); + +// formulae to check whether the resource of an agent has been already filled in the team +formula resource_filled_t1 = (m2_t1=1 & resource1=resource2) | (m3_t1=1 & resource1=resource3) | (m4_t1=1 & resource1=resource4); +formula resource_filled_t2 = (m2_t2=1 & resource1=resource2) | (m3_t2=1 & resource1=resource3) | (m4_t2=1 & resource1=resource4); + +// formula to compute team initiation probability (assuming each agent has at least one connection) +formula IP = (e12*(1-((m2_t1+m2_t2)=0?0:1))+e13*(1-((m3_t1+m3_t2)=0?0:1))+e14*(1-((m4_t1+m4_t2)=0?0:1))) / (e12+e13+e14); + + + +module controller // schedules the algorithm + + // algorithm status + status : [0..7]; + + // task resource indicator variables + t1_r1 : [0..1]; + t1_r2 : [0..1]; + t1_r3 : [0..1]; + + t2_r1 : [0..1]; + t2_r2 : [0..1]; + t2_r3 : [0..1]; + + // schedule placeholders + turn1 : [0..n_sensors]; + turn2 : [0..n_sensors]; + turn3 : [0..n_sensors]; + turn4 : [0..n_sensors]; + turn5 : [0..n_sensors]; + + // selecting schedule uniformly at random + [] status=0 -> 1/24 : (turn1'=1) & (turn2'=2) & (turn3'=3) & (turn4'=4) & (status'=1) + + 1/24 : (turn1'=1) & (turn2'=2) & (turn3'=4) & (turn4'=3) & (status'=1) + + 1/24 : (turn1'=1) & (turn2'=3) & (turn3'=2) & (turn4'=4) & (status'=1) + + 1/24 : (turn1'=1) & (turn2'=3) & (turn3'=4) & (turn4'=2) & (status'=1) + + 1/24 : (turn1'=1) & (turn2'=4) & (turn3'=2) & (turn4'=3) & (status'=1) + + 1/24 : (turn1'=1) & (turn2'=4) & (turn3'=3) & (turn4'=2) & (status'=1) + + 1/24 : (turn1'=2) & (turn2'=1) & (turn3'=3) & (turn4'=4) & (status'=1) + + 1/24 : (turn1'=2) & (turn2'=1) & (turn3'=4) & (turn4'=3) & (status'=1) + + 1/24 : (turn1'=2) & (turn2'=3) & (turn3'=1) & (turn4'=4) & (status'=1) + + 1/24 : (turn1'=2) & (turn2'=3) & (turn3'=4) & (turn4'=1) & (status'=1) + + 1/24 : (turn1'=2) & (turn2'=4) & (turn3'=1) & (turn4'=3) & (status'=1) + + 1/24 : (turn1'=2) & (turn2'=4) & (turn3'=3) & (turn4'=1) & (status'=1) + + 1/24 : (turn1'=3) & (turn2'=1) & (turn3'=2) & (turn4'=4) & (status'=1) + + 1/24 : (turn1'=3) & (turn2'=1) & (turn3'=4) & (turn4'=2) & (status'=1) + + 1/24 : (turn1'=3) & (turn2'=2) & (turn3'=1) & (turn4'=4) & (status'=1) + + 1/24 : (turn1'=3) & (turn2'=2) & (turn3'=4) & (turn4'=1) & (status'=1) + + 1/24 : (turn1'=3) & (turn2'=4) & (turn3'=1) & (turn4'=2) & (status'=1) + + 1/24 : (turn1'=3) & (turn2'=4) & (turn3'=2) & (turn4'=1) & (status'=1) + + 1/24 : (turn1'=4) & (turn2'=1) & (turn3'=2) & (turn4'=3) & (status'=1) + + 1/24 : (turn1'=4) & (turn2'=1) & (turn3'=3) & (turn4'=2) & (status'=1) + + 1/24 : (turn1'=4) & (turn2'=2) & (turn3'=1) & (turn4'=3) & (status'=1) + + 1/24 : (turn1'=4) & (turn2'=2) & (turn3'=3) & (turn4'=1) & (status'=1) + + 1/24 : (turn1'=4) & (turn2'=3) & (turn3'=1) & (turn4'=2) & (status'=1) + + 1/24 : (turn1'=4) & (turn2'=3) & (turn3'=2) & (turn4'=1) & (status'=1); + + + // initialising non-empty tasks uniformly at random + [] status=1 -> 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2); + + // executing the schedule + + // 1st round + [str1] status=2 & turn1=1 -> (status'=2); + [fin1] status=2 & turn1=1 -> (status'=3); + [str2] status=2 & turn1=2 -> (status'=2); + [fin2] status=2 & turn1=2 -> (status'=3); + [str3] status=2 & turn1=3 -> (status'=2); + [fin3] status=2 & turn1=3 -> (status'=3); + [str4] status=2 & turn1=4 -> (status'=2); + [fin4] status=2 & turn1=4 -> (status'=3); + + // 2nd round + [str1] status=3 & turn2=1 -> (status'=3); + [fin1] status=3 & turn2=1 -> (status'=4); + [str2] status=3 & turn2=2 -> (status'=3); + [fin2] status=3 & turn2=2 -> (status'=4); + [str3] status=3 & turn2=3 -> (status'=3); + [fin3] status=3 & turn2=3 -> (status'=4); + [str4] status=3 & turn2=4 -> (status'=3); + [fin4] status=3 & turn2=4 -> (status'=4); + + // 3rd round + [str1] status=4 & turn3=1 -> (status'=4); + [fin1] status=4 & turn3=1 -> (status'=5); + [str2] status=4 & turn3=2 -> (status'=4); + [fin2] status=4 & turn3=2 -> (status'=5); + [str3] status=4 & turn3=3 -> (status'=4); + [fin3] status=4 & turn3=3 -> (status'=5); + [str4] status=4 & turn3=4 -> (status'=4); + [fin4] status=4 & turn3=4 -> (status'=5); + + // 4th round + [str1] status=5 & turn4=1 -> (status'=5); + [fin1] status=5 & turn4=1 -> (status'=6); + [str2] status=5 & turn4=2 -> (status'=5); + [fin2] status=5 & turn4=2 -> (status'=6); + [str3] status=5 & turn4=3 -> (status'=5); + [fin3] status=5 & turn4=3 -> (status'=6); + [str4] status=5 & turn4=4 -> (status'=5); + [fin4] status=5 & turn4=4 -> (status'=6); + + [] status=6 -> (status'=7); + + [] status=7 -> (status'=7); + +endmodule + +module sensor1 + + state1 : [0..1]; + + // team membership indicators + m1_t1 : [0..1]; + m1_t2 : [0..1]; + + // starting turn, selecting order of tasks + [str1] state1=0 -> (state1'=1); + + // if there is no team and has required skill - initiating the team + [] state1=1 & !committed & team_size_t1=0 & has_resource_t1 -> (m1_t1'=1); + [] state1=1 & !committed & team_size_t2=0 & has_resource_t2 -> (m1_t2'=1); + + // if team already exists and one of the neighbours is in it - joining the team + [] state1=1 & !committed & team_size_t1>0 & can_join_t1 & has_resource_t1 & !resource_filled_t1 -> (m1_t1'=1); + [] state1=1 & !committed & team_size_t2>0 & can_join_t2 & has_resource_t2 & !resource_filled_t2 -> (m1_t2'=1); + + [fin1] state1>0 -> (state1'=0); + +endmodule + +module sensor2 = sensor1 +[ + state1=state2, + + str1=str2, + fin1=fin2, + + m1_t1=m2_t1, + m1_t2=m2_t2, + + m2_t1=m1_t1, + m2_t2=m1_t2, + + resource1=resource2, + resource2=resource1, + + e12=e21, + e13=e23, + e14=e24, + e15=e25, + + e21=e12, + e23=e13, + e24=e14, + e25=e15 +] +endmodule + +module sensor3 = sensor1 +[ + state1=state3, + + str1=str3, + fin1=fin3, + + m1_t1=m3_t1, + m1_t2=m3_t2, + m3_t1=m1_t1, + m3_t2=m1_t2, + + resource1=resource3, + resource3=resource1, + + e12=e32, + e13=e31, + e14=e34, + e15=e35, + + e31=e13, + e32=e12, + e34=e14, + e35=e15 +] +endmodule + +module sensor4 = sensor1 +[ + state1=state4, + + str1=str4, + fin1=fin4, + + m1_t1=m4_t1, + m1_t2=m4_t2, + + m4_t1=m1_t1, + m4_t2=m1_t2, + + resource1=resource4, + resource4=resource1, + + e12=e42, + e13=e43, + e14=e41, + e15=e45, + + e41=e14, + e42=e12, + e43=e13, + e45=e15 +] +endmodule + + + + + +// labels and formulae for property specification +formula finished = (status=6); +label "end" = (status=7); + + +formula task1_completed = finished + & ((t1_r1=1)=>((m1_t1=1&resource1=1)|(m2_t1=1&resource2=1)|(m3_t1=1&resource3=1)|(m4_t1=1&resource4=1))) + & ((t1_r2=1)=>((m1_t1=1&resource1=2)|(m2_t1=1&resource2=2)|(m3_t1=1&resource3=2)|(m4_t1=1&resource4=2))) + & ((t1_r3=1)=>((m1_t1=1&resource1=3)|(m2_t1=1&resource2=3)|(m3_t1=1&resource3=3)|(m4_t1=1&resource4=3))); + +formula task2_completed = finished + & ((t2_r1=1)=>((m1_t2=1&resource1=1)|(m2_t2=1&resource2=1)|(m3_t2=1&resource3=1)|(m4_t2=1&resource4=1))) + & ((t2_r2=1)=>((m1_t2=1&resource1=2)|(m2_t2=1&resource2=2)|(m3_t2=1&resource3=2)|(m4_t2=1&resource4=2))) + & ((t2_r3=1)=>((m1_t2=1&resource1=3)|(m2_t2=1&resource2=3)|(m3_t2=1&resource3=3)|(m4_t2=1&resource4=3))); + + +formula agent1_joins_successful_team = (task1_completed & m1_t1=1) | (task2_completed & m1_t2=1); +formula agent1_joins_successful_team_of_1 = (task1_completed & m1_t1=1 & team_size_t1=1) | (task2_completed & m1_t2=1 & team_size_t2=1); +formula agent1_joins_successful_team_of_2 = (task1_completed & m1_t1=1 & team_size_t1=2) | (task2_completed & m1_t2=1 & team_size_t2=2); +formula agent1_joins_successful_team_of_3 = (task1_completed & m1_t1=1 & team_size_t1=3) | (task2_completed & m1_t2=1 & team_size_t2=3); + +formula agent2_joins_successful_team = (task1_completed & m2_t1=1) | (task2_completed & m2_t2=1); +formula agent2_joins_successful_team_of_1 = (task1_completed & m2_t1=1 & team_size_t1=1) | (task2_completed & m2_t2=1 & team_size_t2=1); +formula agent2_joins_successful_team_of_2 = (task1_completed & m2_t1=1 & team_size_t1=2) | (task2_completed & m2_t2=1 & team_size_t2=2); +formula agent2_joins_successful_team_of_3 = (task1_completed & m2_t1=1 & team_size_t1=3) | (task2_completed & m2_t2=1 & team_size_t2=3); + +formula agent3_joins_successful_team = (task1_completed & m3_t1=1) | (task2_completed & m3_t2=1); +formula agent3_joins_successful_team_of_1 = (task1_completed & m3_t1=1 & team_size_t1=1) | (task2_completed & m3_t2=1 & team_size_t2=1); +formula agent3_joins_successful_team_of_2 = (task1_completed & m3_t1=1 & team_size_t1=2) | (task2_completed & m3_t2=1 & team_size_t2=2); +formula agent3_joins_successful_team_of_3 = (task1_completed & m3_t1=1 & team_size_t1=3) | (task2_completed & m3_t2=1 & team_size_t2=3); + +formula agent4_joins_successful_team = (task1_completed & m4_t1=1) | (task2_completed & m4_t2=1); +formula agent4_joins_successful_team_of_1 = (task1_completed & m4_t1=1 & team_size_t1=1) | (task2_completed & m4_t2=1 & team_size_t2=1); +formula agent4_joins_successful_team_of_2 = (task1_completed & m4_t1=1 & team_size_t1=2) | (task2_completed & m4_t2=1 & team_size_t2=2); +formula agent4_joins_successful_team_of_3 = (task1_completed & m4_t1=1 & team_size_t1=3) | (task2_completed & m4_t2=1 & team_size_t2=3); + +// rewards +rewards "w_1_total" + [] agent1_joins_successful_team : 1; + [] agent2_joins_successful_team : 1; + [] agent3_joins_successful_team : 1; + [] agent4_joins_successful_team : 1; +endrewards + +rewards "w_2_total" + [] task1_completed : 1; + [] task2_completed : 1; +endrewards + + + + + diff --git a/examples/multiobjective/mdp/team/team2obj_4_numerical.pctl b/examples/multiobjective/mdp/team/team2obj_4_numerical.pctl new file mode 100644 index 000000000..9cb290800 --- /dev/null +++ b/examples/multiobjective/mdp/team/team2obj_4_numerical.pctl @@ -0,0 +1 @@ + multi(Pmax=? [ F task1_completed ], R{"w_1_total"}>=2.423469388 [ C ]) diff --git a/examples/multiobjective/mdp/team/team2obj_4_pareto.pctl b/examples/multiobjective/mdp/team/team2obj_4_pareto.pctl new file mode 100644 index 000000000..636cd1772 --- /dev/null +++ b/examples/multiobjective/mdp/team/team2obj_4_pareto.pctl @@ -0,0 +1 @@ + multi(Pmax=? [ F task1_completed ], R{"w_1_total"}max=? [ C ]) diff --git a/examples/multiobjective/mdp/team/team2obj_5.nm b/examples/multiobjective/mdp/team/team2obj_5.nm new file mode 100644 index 000000000..2bab4d7f4 --- /dev/null +++ b/examples/multiobjective/mdp/team/team2obj_5.nm @@ -0,0 +1,532 @@ +mdp + +// parameters +const int n_resources = 3; +const int n_tasks = 2; +const int n_sensors = 5; + + +// sensor resources +const int resource1=1; +const int resource2=2; +const int resource3=3; +const int resource4=1; +const int resource5=2; + +// network configuration +const int e12=1; +const int e13=1; +const int e14=1; +const int e15=1; + +const int e21=e12; +const int e23=1; +const int e24=1; +const int e25=1; + +const int e31=e13; +const int e32=e23; +const int e34=1; +const int e35=1; + +const int e41=e14; +const int e42=e24; +const int e43=e34; +const int e45=1; + +const int e51=e15; +const int e52=e25; +const int e53=e35; +const int e54=e45; + + +// agent is committed to some team +formula committed = (m1_t1+m1_t2) > 0; + +// formulae to compute team sizes +formula team_size_t1 = m1_t1+m2_t1+m3_t1+m4_t1+m5_t1; +formula team_size_t2 = m1_t2+m2_t2+m3_t2+m4_t2+m5_t2; + +// formulae to check whether the agent can join the team +formula can_join_t1 = e12*m2_t1 + e13*m3_t1 + e14*m4_t1 + e15*m5_t1 > 0; +formula can_join_t2 = e12*m2_t2 + e13*m3_t2 + e14*m4_t2 + e15*m5_t2 > 0; + +// formulae to check whether agent has the resource required by the task +formula has_resource_t1 = ( (t1_r1=1&resource1=1) | (t1_r2=1&resource1=2) | (t1_r3=1&resource1=3) ); +formula has_resource_t2 = ( (t2_r1=1&resource1=1) | (t2_r2=1&resource1=2) | (t2_r3=1&resource1=3) ); + +// formulae to check whether the resource of an agent has been already filled in the team +formula resource_filled_t1 = (m2_t1=1 & resource1=resource2) | (m3_t1=1 & resource1=resource3) | (m4_t1=1 & resource1=resource4) | (m5_t1=1 & resource1=resource5); +formula resource_filled_t2 = (m2_t2=1 & resource1=resource2) | (m3_t2=1 & resource1=resource3) | (m4_t2=1 & resource1=resource4) | (m5_t2=1 & resource1=resource5); + +// formula to compute team initiation probability (assuming each agent has at least one connection) +formula IP = (e12*(1-((m2_t1+m2_t2)=0?0:1))+e13*(1-((m3_t1+m3_t2)=0?0:1))+e14*(1-((m4_t1+m4_t2)=0?0:1))+e15*(1-((m5_t1+m5_t2)=0?0:1))) / (e12+e13+e14+e15); + + + +module controller // schedules the algorithm + + // algorithm status + status : [0..8]; + + // task resource indicator variables + t1_r1 : [0..1]; + t1_r2 : [0..1]; + t1_r3 : [0..1]; + + t2_r1 : [0..1]; + t2_r2 : [0..1]; + t2_r3 : [0..1]; + + // schedule placeholders + turn1 : [0..n_sensors]; + turn2 : [0..n_sensors]; + turn3 : [0..n_sensors]; + turn4 : [0..n_sensors]; + turn5 : [0..n_sensors]; + + // selecting schedule uniformly at random + [] status=0 -> 1/120 : (turn1'=1) & (turn2'=2) & (turn3'=3) & (turn4'=4) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=2) & (turn3'=3) & (turn4'=5) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=2) & (turn3'=4) & (turn4'=3) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=2) & (turn3'=4) & (turn4'=5) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=2) & (turn3'=5) & (turn4'=3) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=2) & (turn3'=5) & (turn4'=4) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=3) & (turn3'=2) & (turn4'=4) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=3) & (turn3'=2) & (turn4'=5) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=3) & (turn3'=4) & (turn4'=2) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=3) & (turn3'=4) & (turn4'=5) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=3) & (turn3'=5) & (turn4'=2) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=3) & (turn3'=5) & (turn4'=4) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=4) & (turn3'=2) & (turn4'=3) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=4) & (turn3'=2) & (turn4'=5) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=4) & (turn3'=3) & (turn4'=2) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=4) & (turn3'=3) & (turn4'=5) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=4) & (turn3'=5) & (turn4'=2) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=4) & (turn3'=5) & (turn4'=3) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=5) & (turn3'=2) & (turn4'=3) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=5) & (turn3'=2) & (turn4'=4) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=5) & (turn3'=3) & (turn4'=2) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=5) & (turn3'=3) & (turn4'=4) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=5) & (turn3'=4) & (turn4'=2) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=5) & (turn3'=4) & (turn4'=3) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=1) & (turn3'=3) & (turn4'=4) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=1) & (turn3'=3) & (turn4'=5) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=1) & (turn3'=4) & (turn4'=3) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=1) & (turn3'=4) & (turn4'=5) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=1) & (turn3'=5) & (turn4'=3) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=1) & (turn3'=5) & (turn4'=4) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=3) & (turn3'=1) & (turn4'=4) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=3) & (turn3'=1) & (turn4'=5) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=3) & (turn3'=4) & (turn4'=1) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=3) & (turn3'=4) & (turn4'=5) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=3) & (turn3'=5) & (turn4'=1) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=3) & (turn3'=5) & (turn4'=4) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=4) & (turn3'=1) & (turn4'=3) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=4) & (turn3'=1) & (turn4'=5) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=4) & (turn3'=3) & (turn4'=1) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=4) & (turn3'=3) & (turn4'=5) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=4) & (turn3'=5) & (turn4'=1) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=4) & (turn3'=5) & (turn4'=3) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=5) & (turn3'=1) & (turn4'=3) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=5) & (turn3'=1) & (turn4'=4) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=5) & (turn3'=3) & (turn4'=1) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=5) & (turn3'=3) & (turn4'=4) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=5) & (turn3'=4) & (turn4'=1) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=5) & (turn3'=4) & (turn4'=3) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=1) & (turn3'=2) & (turn4'=4) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=1) & (turn3'=2) & (turn4'=5) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=1) & (turn3'=4) & (turn4'=2) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=1) & (turn3'=4) & (turn4'=5) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=1) & (turn3'=5) & (turn4'=2) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=1) & (turn3'=5) & (turn4'=4) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=2) & (turn3'=1) & (turn4'=4) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=2) & (turn3'=1) & (turn4'=5) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=2) & (turn3'=4) & (turn4'=1) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=2) & (turn3'=4) & (turn4'=5) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=2) & (turn3'=5) & (turn4'=1) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=2) & (turn3'=5) & (turn4'=4) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=4) & (turn3'=1) & (turn4'=2) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=4) & (turn3'=1) & (turn4'=5) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=4) & (turn3'=2) & (turn4'=1) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=4) & (turn3'=2) & (turn4'=5) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=4) & (turn3'=5) & (turn4'=1) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=4) & (turn3'=5) & (turn4'=2) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=5) & (turn3'=1) & (turn4'=2) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=5) & (turn3'=1) & (turn4'=4) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=5) & (turn3'=2) & (turn4'=1) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=5) & (turn3'=2) & (turn4'=4) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=5) & (turn3'=4) & (turn4'=1) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=5) & (turn3'=4) & (turn4'=2) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=1) & (turn3'=2) & (turn4'=3) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=1) & (turn3'=2) & (turn4'=5) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=1) & (turn3'=3) & (turn4'=2) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=1) & (turn3'=3) & (turn4'=5) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=1) & (turn3'=5) & (turn4'=2) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=1) & (turn3'=5) & (turn4'=3) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=2) & (turn3'=1) & (turn4'=3) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=2) & (turn3'=1) & (turn4'=5) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=2) & (turn3'=3) & (turn4'=1) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=2) & (turn3'=3) & (turn4'=5) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=2) & (turn3'=5) & (turn4'=1) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=2) & (turn3'=5) & (turn4'=3) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=3) & (turn3'=1) & (turn4'=2) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=3) & (turn3'=1) & (turn4'=5) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=3) & (turn3'=2) & (turn4'=1) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=3) & (turn3'=2) & (turn4'=5) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=3) & (turn3'=5) & (turn4'=1) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=3) & (turn3'=5) & (turn4'=2) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=5) & (turn3'=1) & (turn4'=2) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=5) & (turn3'=1) & (turn4'=3) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=5) & (turn3'=2) & (turn4'=1) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=5) & (turn3'=2) & (turn4'=3) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=5) & (turn3'=3) & (turn4'=1) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=5) & (turn3'=3) & (turn4'=2) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=1) & (turn3'=2) & (turn4'=3) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=1) & (turn3'=2) & (turn4'=4) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=1) & (turn3'=3) & (turn4'=2) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=1) & (turn3'=3) & (turn4'=4) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=1) & (turn3'=4) & (turn4'=2) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=1) & (turn3'=4) & (turn4'=3) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=2) & (turn3'=1) & (turn4'=3) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=2) & (turn3'=1) & (turn4'=4) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=2) & (turn3'=3) & (turn4'=1) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=2) & (turn3'=3) & (turn4'=4) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=2) & (turn3'=4) & (turn4'=1) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=2) & (turn3'=4) & (turn4'=3) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=3) & (turn3'=1) & (turn4'=2) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=3) & (turn3'=1) & (turn4'=4) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=3) & (turn3'=2) & (turn4'=1) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=3) & (turn3'=2) & (turn4'=4) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=3) & (turn3'=4) & (turn4'=1) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=3) & (turn3'=4) & (turn4'=2) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=4) & (turn3'=1) & (turn4'=2) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=4) & (turn3'=1) & (turn4'=3) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=4) & (turn3'=2) & (turn4'=1) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=4) & (turn3'=2) & (turn4'=3) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=4) & (turn3'=3) & (turn4'=1) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=4) & (turn3'=3) & (turn4'=2) & (turn5'=1) & (status'=1); + + + // initialising non-empty tasks uniformly at random + [] status=1 -> 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2); + + // executing the schedule + + // 1st round + [str1] status=2 & turn1=1 -> (status'=2); + [fin1] status=2 & turn1=1 -> (status'=3); + [str2] status=2 & turn1=2 -> (status'=2); + [fin2] status=2 & turn1=2 -> (status'=3); + [str3] status=2 & turn1=3 -> (status'=2); + [fin3] status=2 & turn1=3 -> (status'=3); + [str4] status=2 & turn1=4 -> (status'=2); + [fin4] status=2 & turn1=4 -> (status'=3); + [str5] status=2 & turn1=5 -> (status'=2); + [fin5] status=2 & turn1=5 -> (status'=3); + + // 2nd round + [str1] status=3 & turn2=1 -> (status'=3); + [fin1] status=3 & turn2=1 -> (status'=4); + [str2] status=3 & turn2=2 -> (status'=3); + [fin2] status=3 & turn2=2 -> (status'=4); + [str3] status=3 & turn2=3 -> (status'=3); + [fin3] status=3 & turn2=3 -> (status'=4); + [str4] status=3 & turn2=4 -> (status'=3); + [fin4] status=3 & turn2=4 -> (status'=4); + [str5] status=3 & turn2=5 -> (status'=3); + [fin5] status=3 & turn2=5 -> (status'=4); + + // 3rd round + [str1] status=4 & turn3=1 -> (status'=4); + [fin1] status=4 & turn3=1 -> (status'=5); + [str2] status=4 & turn3=2 -> (status'=4); + [fin2] status=4 & turn3=2 -> (status'=5); + [str3] status=4 & turn3=3 -> (status'=4); + [fin3] status=4 & turn3=3 -> (status'=5); + [str4] status=4 & turn3=4 -> (status'=4); + [fin4] status=4 & turn3=4 -> (status'=5); + [str5] status=4 & turn3=5 -> (status'=4); + [fin5] status=4 & turn3=5 -> (status'=5); + + // 4th round + [str1] status=5 & turn4=1 -> (status'=5); + [fin1] status=5 & turn4=1 -> (status'=6); + [str2] status=5 & turn4=2 -> (status'=5); + [fin2] status=5 & turn4=2 -> (status'=6); + [str3] status=5 & turn4=3 -> (status'=5); + [fin3] status=5 & turn4=3 -> (status'=6); + [str4] status=5 & turn4=4 -> (status'=5); + [fin4] status=5 & turn4=4 -> (status'=6); + [str5] status=5 & turn4=5 -> (status'=5); + [fin5] status=5 & turn4=5 -> (status'=6); + + // 5th round + [str1] status=6 & turn5=1 -> (status'=6); + [fin1] status=6 & turn5=1 -> (status'=7); + [str2] status=6 & turn5=2 -> (status'=6); + [fin2] status=6 & turn5=2 -> (status'=7); + [str3] status=6 & turn5=3 -> (status'=6); + [fin3] status=6 & turn5=3 -> (status'=7); + [str4] status=6 & turn5=4 -> (status'=6); + [fin4] status=6 & turn5=4 -> (status'=7); + [str5] status=6 & turn5=5 -> (status'=6); + [fin5] status=6 & turn5=5 -> (status'=7); + + [] status=7 -> (status'=8); + + [] status=8 -> (status'=8); + +endmodule + +module sensor1 + + state1 : [0..1]; + + // team membership indicators + m1_t1 : [0..1]; + m1_t2 : [0..1]; + + // starting turn, selecting order of tasks + [str1] state1=0 -> (state1'=1); + + // if there is no team and has required skill - initiating the team + [] state1=1 & !committed & team_size_t1=0 & has_resource_t1 -> (m1_t1'=1); + [] state1=1 & !committed & team_size_t2=0 & has_resource_t2 -> (m1_t2'=1); + + // if team already exists and one of the neighbours is in it - joining the team + [] state1=1 & !committed & team_size_t1>0 & can_join_t1 & has_resource_t1 & !resource_filled_t1 -> (m1_t1'=1); + [] state1=1 & !committed & team_size_t2>0 & can_join_t2 & has_resource_t2 & !resource_filled_t2 -> (m1_t2'=1); + + [fin1] state1>0 -> (state1'=0); + +endmodule + +module sensor2 = sensor1 +[ + state1=state2, + + str1=str2, + fin1=fin2, + + m1_t1=m2_t1, + m1_t2=m2_t2, + + m2_t1=m1_t1, + m2_t2=m1_t2, + + resource1=resource2, + resource2=resource1, + + e12=e21, + e13=e23, + e14=e24, + e15=e25, + + e21=e12, + e23=e13, + e24=e14, + e25=e15 +] +endmodule + +module sensor3 = sensor1 +[ + state1=state3, + + str1=str3, + fin1=fin3, + + m1_t1=m3_t1, + m1_t2=m3_t2, + m3_t1=m1_t1, + m3_t2=m1_t2, + + resource1=resource3, + resource3=resource1, + + e12=e32, + e13=e31, + e14=e34, + e15=e35, + + e31=e13, + e32=e12, + e34=e14, + e35=e15 +] +endmodule + +module sensor4 = sensor1 +[ + state1=state4, + + str1=str4, + fin1=fin4, + + m1_t1=m4_t1, + m1_t2=m4_t2, + + m4_t1=m1_t1, + m4_t2=m1_t2, + + resource1=resource4, + resource4=resource1, + + e12=e42, + e13=e43, + e14=e41, + e15=e45, + + e41=e14, + e42=e12, + e43=e13, + e45=e15 +] +endmodule + +module sensor5 = sensor1 +[ + state1=state5, + + str1=str5, + fin1=fin5, + + m1_t1=m5_t1, + m1_t2=m5_t2, + + m5_t1=m1_t1, + m5_t2=m1_t2, + + resource1=resource5, + resource5=resource1, + + e12=e52, + e13=e53, + e14=e54, + e15=e51, + + e51=e15, + e52=e12, + e53=e13, + e54=e14 +] +endmodule + +// formulae for scheduling +formula s1_sched = (turn1=1 | turn2=1 | turn3=1 | turn4=1 | turn5=1); +formula s2_sched = (turn1=2 | turn2=2 | turn3=2 | turn4=2 | turn5=2); +formula s3_sched = (turn1=3 | turn2=3 | turn3=3 | turn4=3 | turn5=3); +formula s4_sched = (turn1=4 | turn2=4 | turn3=4 | turn4=4 | turn5=4); +formula s5_sched = (turn1=5 | turn2=5 | turn3=5 | turn4=5 | turn5=5); +formula all_not_sched = !(s1_sched | s2_sched | s3_sched | s4_sched | s5_sched); +formula all_sched = (s1_sched & s2_sched & s3_sched & s4_sched & s5_sched); + + +// labels and formulae for property specification +formula finished = (status=7); +label "end" = (status=8); + + +formula task1_completed = finished + & ((t1_r1=1)=>((m1_t1=1&resource1=1)|(m2_t1=1&resource2=1)|(m3_t1=1&resource3=1)|(m4_t1=1&resource4=1)|(m5_t1=1&resource5=1))) + & ((t1_r2=1)=>((m1_t1=1&resource1=2)|(m2_t1=1&resource2=2)|(m3_t1=1&resource3=2)|(m4_t1=1&resource4=2)|(m5_t1=1&resource5=2))) + & ((t1_r3=1)=>((m1_t1=1&resource1=3)|(m2_t1=1&resource2=3)|(m3_t1=1&resource3=3)|(m4_t1=1&resource4=3)|(m5_t1=1&resource5=3))); + +formula task2_completed = finished + & ((t2_r1=1)=>((m1_t2=1&resource1=1)|(m2_t2=1&resource2=1)|(m3_t2=1&resource3=1)|(m4_t2=1&resource4=1)|(m5_t2=1&resource5=1))) + & ((t2_r2=1)=>((m1_t2=1&resource1=2)|(m2_t2=1&resource2=2)|(m3_t2=1&resource3=2)|(m4_t2=1&resource4=2)|(m5_t2=1&resource5=2))) + & ((t2_r3=1)=>((m1_t2=1&resource1=3)|(m2_t2=1&resource2=3)|(m3_t2=1&resource3=3)|(m4_t2=1&resource4=3)|(m5_t2=1&resource5=3))); + + +formula agent1_joins_successful_team = (task1_completed & m1_t1=1) | (task2_completed & m1_t2=1); +formula agent1_joins_successful_team_of_1 = (task1_completed & m1_t1=1 & team_size_t1=1) | (task2_completed & m1_t2=1 & team_size_t2=1); +formula agent1_joins_successful_team_of_2 = (task1_completed & m1_t1=1 & team_size_t1=2) | (task2_completed & m1_t2=1 & team_size_t2=2); +formula agent1_joins_successful_team_of_3 = (task1_completed & m1_t1=1 & team_size_t1=3) | (task2_completed & m1_t2=1 & team_size_t2=3); + +formula agent2_joins_successful_team = (task1_completed & m2_t1=1) | (task2_completed & m2_t2=1); +formula agent2_joins_successful_team_of_1 = (task1_completed & m2_t1=1 & team_size_t1=1) | (task2_completed & m2_t2=1 & team_size_t2=1); +formula agent2_joins_successful_team_of_2 = (task1_completed & m2_t1=1 & team_size_t1=2) | (task2_completed & m2_t2=1 & team_size_t2=2); +formula agent2_joins_successful_team_of_3 = (task1_completed & m2_t1=1 & team_size_t1=3) | (task2_completed & m2_t2=1 & team_size_t2=3); + +formula agent3_joins_successful_team = (task1_completed & m3_t1=1) | (task2_completed & m3_t2=1); +formula agent3_joins_successful_team_of_1 = (task1_completed & m3_t1=1 & team_size_t1=1) | (task2_completed & m3_t2=1 & team_size_t2=1); +formula agent3_joins_successful_team_of_2 = (task1_completed & m3_t1=1 & team_size_t1=2) | (task2_completed & m3_t2=1 & team_size_t2=2); +formula agent3_joins_successful_team_of_3 = (task1_completed & m3_t1=1 & team_size_t1=3) | (task2_completed & m3_t2=1 & team_size_t2=3); + +formula agent4_joins_successful_team = (task1_completed & m4_t1=1) | (task2_completed & m4_t2=1); +formula agent4_joins_successful_team_of_1 = (task1_completed & m4_t1=1 & team_size_t1=1) | (task2_completed & m4_t2=1 & team_size_t2=1); +formula agent4_joins_successful_team_of_2 = (task1_completed & m4_t1=1 & team_size_t1=2) | (task2_completed & m4_t2=1 & team_size_t2=2); +formula agent4_joins_successful_team_of_3 = (task1_completed & m4_t1=1 & team_size_t1=3) | (task2_completed & m4_t2=1 & team_size_t2=3); + +formula agent5_joins_successful_team = (task1_completed & m5_t1=1) | (task2_completed & m5_t2=1); +formula agent5_joins_successful_team_of_1 = (task1_completed & m5_t1=1 & team_size_t1=1) | (task2_completed & m5_t2=1 & team_size_t2=1); +formula agent5_joins_successful_team_of_2 = (task1_completed & m5_t1=1 & team_size_t1=2) | (task2_completed & m5_t2=1 & team_size_t2=2); +formula agent5_joins_successful_team_of_3 = (task1_completed & m5_t1=1 & team_size_t1=3) | (task2_completed & m5_t2=1 & team_size_t2=3); + +// rewards +rewards "w_1_total" + [] agent1_joins_successful_team : 1; + [] agent2_joins_successful_team : 1; + [] agent3_joins_successful_team : 1; + [] agent4_joins_successful_team : 1; + [] agent5_joins_successful_team : 1; +endrewards + +rewards "w_2_total" + [] task1_completed : 1; + [] task2_completed : 1; +endrewards + + + + + diff --git a/examples/multiobjective/mdp/team/team2obj_5_numerical.pctl b/examples/multiobjective/mdp/team/team2obj_5_numerical.pctl new file mode 100644 index 000000000..027c6ef09 --- /dev/null +++ b/examples/multiobjective/mdp/team/team2obj_5_numerical.pctl @@ -0,0 +1 @@ + multi(Pmax=? [ F task1_completed ], R{"w_1_total"}>=2.753061224 [ C ]) diff --git a/examples/multiobjective/mdp/team/team2obj_5_pareto.pctl b/examples/multiobjective/mdp/team/team2obj_5_pareto.pctl new file mode 100644 index 000000000..636cd1772 --- /dev/null +++ b/examples/multiobjective/mdp/team/team2obj_5_pareto.pctl @@ -0,0 +1 @@ + multi(Pmax=? [ F task1_completed ], R{"w_1_total"}max=? [ C ]) diff --git a/examples/multiobjective/mdp/team/team3obj_3.nm b/examples/multiobjective/mdp/team/team3obj_3.nm new file mode 100644 index 000000000..9e677312d --- /dev/null +++ b/examples/multiobjective/mdp/team/team3obj_3.nm @@ -0,0 +1,288 @@ +mdp + +// parameters +const int n_resources = 3; +const int n_tasks = 2; +const int n_sensors = 3; + + +// sensor resources +const int resource1=1; +const int resource2=2; +const int resource3=3; + +// network configuration +const int e12=1; +const int e13=1; + +const int e21=e12; +const int e23=1; + +const int e31=e13; +const int e32=e23; + + + + +// agent is committed to some team +formula committed = (m1_t1+m1_t2) > 0; + +// formulae to compute team sizes +formula team_size_t1 = m1_t1+m2_t1+m3_t1; +formula team_size_t2 = m1_t2+m2_t2+m3_t2; + +// formulae to check whether the agent can join the team +formula can_join_t1 = e12*m2_t1 + e13*m3_t1 > 0; +formula can_join_t2 = e12*m2_t2 + e13*m3_t2 > 0; + +// formulae to check whether agent has the resource required by the task +formula has_resource_t1 = ( (t1_r1=1&resource1=1) | (t1_r2=1&resource1=2) | (t1_r3=1&resource1=3) ); +formula has_resource_t2 = ( (t2_r1=1&resource1=1) | (t2_r2=1&resource1=2) | (t2_r3=1&resource1=3) ); + +// formulae to check whether the resource of an agent has been already filled in the team +formula resource_filled_t1 = (m2_t1=1 & resource1=resource2) | (m3_t1=1 & resource1=resource3); +formula resource_filled_t2 = (m2_t2=1 & resource1=resource2) | (m3_t2=1 & resource1=resource3); + +// formula to compute team initiation probability (assuming each agent has at least one connection) +formula IP = (e12*(1-((m2_t1+m2_t2)=0?0:1))+e13*(1-((m3_t1+m3_t2)=0?0:1))) / (e12+e13); + + +module controller // schedules the algorithm + + // algorithm status + status : [0..6]; + + // task resource indicator variables + t1_r1 : [0..1]; + t1_r2 : [0..1]; + t1_r3 : [0..1]; + + t2_r1 : [0..1]; + t2_r2 : [0..1]; + t2_r3 : [0..1]; + + // schedule placeholders + turn1 : [0..n_sensors]; + turn2 : [0..n_sensors]; + turn3 : [0..n_sensors]; + + // selecting schedule uniformly at random + [] status=0 -> 1/6 : (turn1'=1) & (turn2'=2) & (turn3'=3) & (status'=1) + + 1/6 : (turn1'=1) & (turn2'=3) & (turn3'=2) & (status'=1) + + 1/6 : (turn1'=2) & (turn2'=1) & (turn3'=3) & (status'=1) + + 1/6 : (turn1'=2) & (turn2'=3) & (turn3'=1) & (status'=1) + + 1/6 : (turn1'=3) & (turn2'=1) & (turn3'=2) & (status'=1) + + 1/6 : (turn1'=3) & (turn2'=2) & (turn3'=1) & (status'=1); + + + // initialising non-empty tasks uniformly at random + [] status=1 -> 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2); + + // executing the schedule + + // 1st round + [str1] status=2 & turn1=1 -> (status'=2); + [fin1] status=2 & turn1=1 -> (status'=3); + [str2] status=2 & turn1=2 -> (status'=2); + [fin2] status=2 & turn1=2 -> (status'=3); + [str3] status=2 & turn1=3 -> (status'=2); + [fin3] status=2 & turn1=3 -> (status'=3); + + // 2nd round + [str1] status=3 & turn2=1 -> (status'=3); + [fin1] status=3 & turn2=1 -> (status'=4); + [str2] status=3 & turn2=2 -> (status'=3); + [fin2] status=3 & turn2=2 -> (status'=4); + [str3] status=3 & turn2=3 -> (status'=3); + [fin3] status=3 & turn2=3 -> (status'=4); + + // 3rd round + [str1] status=4 & turn3=1 -> (status'=4); + [fin1] status=4 & turn3=1 -> (status'=5); + [str2] status=4 & turn3=2 -> (status'=4); + [fin2] status=4 & turn3=2 -> (status'=5); + [str3] status=4 & turn3=3 -> (status'=4); + [fin3] status=4 & turn3=3 -> (status'=5); + + [] status=5 -> (status'=6); + + [] status=6 -> true; + +endmodule + +module sensor1 + + state1 : [0..1]; + + // team membership indicators + m1_t1 : [0..1]; + m1_t2 : [0..1]; + + // starting turn, selecting order of tasks + [str1] state1=0 -> (state1'=1); + + // if there is no team and has required skill - initiating the team + [] state1=1 & !committed & team_size_t1=0 & has_resource_t1 -> (m1_t1'=1); + [] state1=1 & !committed & team_size_t2=0 & has_resource_t2 -> (m1_t2'=1); + + // if team already exists and one of the neighbours is in it - joining the team + [] state1=1 & !committed & team_size_t1>0 & can_join_t1 & has_resource_t1 & !resource_filled_t1 -> (m1_t1'=1); + [] state1=1 & !committed & team_size_t2>0 & can_join_t2 & has_resource_t2 & !resource_filled_t2 -> (m1_t2'=1); + + [fin1] state1>0 -> (state1'=0); + +endmodule + +module sensor2 = sensor1 +[ + state1=state2, + + str1=str2, + fin1=fin2, + + m1_t1=m2_t1, + m1_t2=m2_t2, + + m2_t1=m1_t1, + m2_t2=m1_t2, + + resource1=resource2, + resource2=resource1, + + e12=e21, + e13=e23, + e14=e24, + e15=e25, + + e21=e12, + e23=e13, + e24=e14, + e25=e15 +] +endmodule + +module sensor3 = sensor1 +[ + state1=state3, + + str1=str3, + fin1=fin3, + + m1_t1=m3_t1, + m1_t2=m3_t2, + m3_t1=m1_t1, + m3_t2=m1_t2, + + resource1=resource3, + resource3=resource1, + + e12=e32, + e13=e31, + e14=e34, + e15=e35, + + e31=e13, + e32=e12, + e34=e14, + e35=e15 +] +endmodule + + + + +// labels and formulae for property specification +formula finished = (status=5); +label "end" = (status=6); + + +formula task1_completed = finished + & ((t1_r1=1)=>((m1_t1=1&resource1=1)|(m2_t1=1&resource2=1)|(m3_t1=1&resource3=1))) + & ((t1_r2=1)=>((m1_t1=1&resource1=2)|(m2_t1=1&resource2=2)|(m3_t1=1&resource3=2))) + & ((t1_r3=1)=>((m1_t1=1&resource1=3)|(m2_t1=1&resource2=3)|(m3_t1=1&resource3=3))); + +formula task2_completed = finished + & ((t2_r1=1)=>((m1_t2=1&resource1=1)|(m2_t2=1&resource2=1)|(m3_t2=1&resource3=1))) + & ((t2_r2=1)=>((m1_t2=1&resource1=2)|(m2_t2=1&resource2=2)|(m3_t2=1&resource3=2))) + & ((t2_r3=1)=>((m1_t2=1&resource1=3)|(m2_t2=1&resource2=3)|(m3_t2=1&resource3=3))); + + + +formula agent1_joins_successful_team = (task1_completed & m1_t1=1) | (task2_completed & m1_t2=1); +formula agent1_joins_successful_team_of_1 = (task1_completed & m1_t1=1 & team_size_t1=1) | (task2_completed & m1_t2=1 & team_size_t2=1); +formula agent1_joins_successful_team_of_2 = (task1_completed & m1_t1=1 & team_size_t1=2) | (task2_completed & m1_t2=1 & team_size_t2=2); +formula agent1_joins_successful_team_of_3 = (task1_completed & m1_t1=1 & team_size_t1=3) | (task2_completed & m1_t2=1 & team_size_t2=3); + +formula agent2_joins_successful_team = (task1_completed & m2_t1=1) | (task2_completed & m2_t2=1); +formula agent2_joins_successful_team_of_1 = (task1_completed & m2_t1=1 & team_size_t1=1) | (task2_completed & m2_t2=1 & team_size_t2=1); +formula agent2_joins_successful_team_of_2 = (task1_completed & m2_t1=1 & team_size_t1=2) | (task2_completed & m2_t2=1 & team_size_t2=2); +formula agent2_joins_successful_team_of_3 = (task1_completed & m2_t1=1 & team_size_t1=3) | (task2_completed & m2_t2=1 & team_size_t2=3); + +formula agent3_joins_successful_team = (task1_completed & m3_t1=1) | (task2_completed & m3_t2=1); +formula agent3_joins_successful_team_of_1 = (task1_completed & m3_t1=1 & team_size_t1=1) | (task2_completed & m3_t2=1 & team_size_t2=1); +formula agent3_joins_successful_team_of_2 = (task1_completed & m3_t1=1 & team_size_t1=2) | (task2_completed & m3_t2=1 & team_size_t2=2); +formula agent3_joins_successful_team_of_3 = (task1_completed & m3_t1=1 & team_size_t1=3) | (task2_completed & m3_t2=1 & team_size_t2=3); + +// rewards +rewards "w_1_total" + [] agent1_joins_successful_team : 1; + [] agent2_joins_successful_team : 1; + [] agent3_joins_successful_team : 1; +endrewards + +rewards "w_2_total" + [] task1_completed : 1; + [] task2_completed : 1; +endrewards + + + + diff --git a/examples/multiobjective/mdp/team/team3obj_3_numerical.pctl b/examples/multiobjective/mdp/team/team3obj_3_numerical.pctl new file mode 100644 index 000000000..4a02276ce --- /dev/null +++ b/examples/multiobjective/mdp/team/team3obj_3_numerical.pctl @@ -0,0 +1 @@ + multi(Pmax=? [ F task1_completed ], R{"w_1_total"}>=2.210204082 [ C ], P>=0.5 [ F task2_completed ]) diff --git a/examples/multiobjective/mdp/team/team3obj_3_pareto.pctl b/examples/multiobjective/mdp/team/team3obj_3_pareto.pctl new file mode 100644 index 000000000..8343ec77e --- /dev/null +++ b/examples/multiobjective/mdp/team/team3obj_3_pareto.pctl @@ -0,0 +1 @@ + multi(Pmax=? [ F task1_completed ], R{"w_1_total"}max=? [ C ], Pmax=? [ F task2_completed ]) diff --git a/examples/multiobjective/mdp/team/team3obj_4.nm b/examples/multiobjective/mdp/team/team3obj_4.nm new file mode 100644 index 000000000..77a74d3f1 --- /dev/null +++ b/examples/multiobjective/mdp/team/team3obj_4.nm @@ -0,0 +1,366 @@ +mdp + +// parameters +const int n_resources = 3; +const int n_tasks = 2; +const int n_sensors = 4; + + +// sensor resources +const int resource1=1; +const int resource2=2; +const int resource3=3; +const int resource4=1; + +// network configuration +const int e12=1; +const int e13=1; +const int e14=1; + +const int e21=e12; +const int e23=1; +const int e24=1; + +const int e31=e13; +const int e32=e23; +const int e34=1; + +const int e41=e14; +const int e42=e24; +const int e43=e34; + + + +// agent is committed to some team +formula committed = (m1_t1+m1_t2) > 0; + +// formulae to compute team sizes +formula team_size_t1 = m1_t1+m2_t1+m3_t1+m4_t1; +formula team_size_t2 = m1_t2+m2_t2+m3_t2+m4_t2; + +// formulae to check whether the agent can join the team +formula can_join_t1 = e12*m2_t1 + e13*m3_t1 + e14*m4_t1 > 0; +formula can_join_t2 = e12*m2_t2 + e13*m3_t2 + e14*m4_t2 > 0; + +// formulae to check whether agent has the resource required by the task +formula has_resource_t1 = ( (t1_r1=1&resource1=1) | (t1_r2=1&resource1=2) | (t1_r3=1&resource1=3) ); +formula has_resource_t2 = ( (t2_r1=1&resource1=1) | (t2_r2=1&resource1=2) | (t2_r3=1&resource1=3) ); + +// formulae to check whether the resource of an agent has been already filled in the team +formula resource_filled_t1 = (m2_t1=1 & resource1=resource2) | (m3_t1=1 & resource1=resource3) | (m4_t1=1 & resource1=resource4); +formula resource_filled_t2 = (m2_t2=1 & resource1=resource2) | (m3_t2=1 & resource1=resource3) | (m4_t2=1 & resource1=resource4); + +// formula to compute team initiation probability (assuming each agent has at least one connection) +formula IP = (e12*(1-((m2_t1+m2_t2)=0?0:1))+e13*(1-((m3_t1+m3_t2)=0?0:1))+e14*(1-((m4_t1+m4_t2)=0?0:1))) / (e12+e13+e14); + + +module controller // schedules the algorithm + + // algorithm status + status : [0..7]; + + // task resource indicator variables + t1_r1 : [0..1]; + t1_r2 : [0..1]; + t1_r3 : [0..1]; + + t2_r1 : [0..1]; + t2_r2 : [0..1]; + t2_r3 : [0..1]; + + // schedule placeholders + turn1 : [0..n_sensors]; + turn2 : [0..n_sensors]; + turn3 : [0..n_sensors]; + turn4 : [0..n_sensors]; + turn5 : [0..n_sensors]; + + // selecting schedule uniformly at random + [] status=0 -> 1/24 : (turn1'=1) & (turn2'=2) & (turn3'=3) & (turn4'=4) & (status'=1) + + 1/24 : (turn1'=1) & (turn2'=2) & (turn3'=4) & (turn4'=3) & (status'=1) + + 1/24 : (turn1'=1) & (turn2'=3) & (turn3'=2) & (turn4'=4) & (status'=1) + + 1/24 : (turn1'=1) & (turn2'=3) & (turn3'=4) & (turn4'=2) & (status'=1) + + 1/24 : (turn1'=1) & (turn2'=4) & (turn3'=2) & (turn4'=3) & (status'=1) + + 1/24 : (turn1'=1) & (turn2'=4) & (turn3'=3) & (turn4'=2) & (status'=1) + + 1/24 : (turn1'=2) & (turn2'=1) & (turn3'=3) & (turn4'=4) & (status'=1) + + 1/24 : (turn1'=2) & (turn2'=1) & (turn3'=4) & (turn4'=3) & (status'=1) + + 1/24 : (turn1'=2) & (turn2'=3) & (turn3'=1) & (turn4'=4) & (status'=1) + + 1/24 : (turn1'=2) & (turn2'=3) & (turn3'=4) & (turn4'=1) & (status'=1) + + 1/24 : (turn1'=2) & (turn2'=4) & (turn3'=1) & (turn4'=3) & (status'=1) + + 1/24 : (turn1'=2) & (turn2'=4) & (turn3'=3) & (turn4'=1) & (status'=1) + + 1/24 : (turn1'=3) & (turn2'=1) & (turn3'=2) & (turn4'=4) & (status'=1) + + 1/24 : (turn1'=3) & (turn2'=1) & (turn3'=4) & (turn4'=2) & (status'=1) + + 1/24 : (turn1'=3) & (turn2'=2) & (turn3'=1) & (turn4'=4) & (status'=1) + + 1/24 : (turn1'=3) & (turn2'=2) & (turn3'=4) & (turn4'=1) & (status'=1) + + 1/24 : (turn1'=3) & (turn2'=4) & (turn3'=1) & (turn4'=2) & (status'=1) + + 1/24 : (turn1'=3) & (turn2'=4) & (turn3'=2) & (turn4'=1) & (status'=1) + + 1/24 : (turn1'=4) & (turn2'=1) & (turn3'=2) & (turn4'=3) & (status'=1) + + 1/24 : (turn1'=4) & (turn2'=1) & (turn3'=3) & (turn4'=2) & (status'=1) + + 1/24 : (turn1'=4) & (turn2'=2) & (turn3'=1) & (turn4'=3) & (status'=1) + + 1/24 : (turn1'=4) & (turn2'=2) & (turn3'=3) & (turn4'=1) & (status'=1) + + 1/24 : (turn1'=4) & (turn2'=3) & (turn3'=1) & (turn4'=2) & (status'=1) + + 1/24 : (turn1'=4) & (turn2'=3) & (turn3'=2) & (turn4'=1) & (status'=1); + + + // initialising non-empty tasks uniformly at random + [] status=1 -> 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2); + + // executing the schedule + + // 1st round + [str1] status=2 & turn1=1 -> (status'=2); + [fin1] status=2 & turn1=1 -> (status'=3); + [str2] status=2 & turn1=2 -> (status'=2); + [fin2] status=2 & turn1=2 -> (status'=3); + [str3] status=2 & turn1=3 -> (status'=2); + [fin3] status=2 & turn1=3 -> (status'=3); + [str4] status=2 & turn1=4 -> (status'=2); + [fin4] status=2 & turn1=4 -> (status'=3); + + // 2nd round + [str1] status=3 & turn2=1 -> (status'=3); + [fin1] status=3 & turn2=1 -> (status'=4); + [str2] status=3 & turn2=2 -> (status'=3); + [fin2] status=3 & turn2=2 -> (status'=4); + [str3] status=3 & turn2=3 -> (status'=3); + [fin3] status=3 & turn2=3 -> (status'=4); + [str4] status=3 & turn2=4 -> (status'=3); + [fin4] status=3 & turn2=4 -> (status'=4); + + // 3rd round + [str1] status=4 & turn3=1 -> (status'=4); + [fin1] status=4 & turn3=1 -> (status'=5); + [str2] status=4 & turn3=2 -> (status'=4); + [fin2] status=4 & turn3=2 -> (status'=5); + [str3] status=4 & turn3=3 -> (status'=4); + [fin3] status=4 & turn3=3 -> (status'=5); + [str4] status=4 & turn3=4 -> (status'=4); + [fin4] status=4 & turn3=4 -> (status'=5); + + // 4th round + [str1] status=5 & turn4=1 -> (status'=5); + [fin1] status=5 & turn4=1 -> (status'=6); + [str2] status=5 & turn4=2 -> (status'=5); + [fin2] status=5 & turn4=2 -> (status'=6); + [str3] status=5 & turn4=3 -> (status'=5); + [fin3] status=5 & turn4=3 -> (status'=6); + [str4] status=5 & turn4=4 -> (status'=5); + [fin4] status=5 & turn4=4 -> (status'=6); + + [] status=6 -> (status'=7); + + [] status=7 -> (status'=7); + +endmodule + +module sensor1 + + state1 : [0..1]; + + // team membership indicators + m1_t1 : [0..1]; + m1_t2 : [0..1]; + + // starting turn, selecting order of tasks + [str1] state1=0 -> (state1'=1); + + // if there is no team and has required skill - initiating the team + [] state1=1 & !committed & team_size_t1=0 & has_resource_t1 -> (m1_t1'=1); + [] state1=1 & !committed & team_size_t2=0 & has_resource_t2 -> (m1_t2'=1); + + // if team already exists and one of the neighbours is in it - joining the team + [] state1=1 & !committed & team_size_t1>0 & can_join_t1 & has_resource_t1 & !resource_filled_t1 -> (m1_t1'=1); + [] state1=1 & !committed & team_size_t2>0 & can_join_t2 & has_resource_t2 & !resource_filled_t2 -> (m1_t2'=1); + + [fin1] state1>0 -> (state1'=0); + +endmodule + +module sensor2 = sensor1 +[ + state1=state2, + + str1=str2, + fin1=fin2, + + m1_t1=m2_t1, + m1_t2=m2_t2, + + m2_t1=m1_t1, + m2_t2=m1_t2, + + resource1=resource2, + resource2=resource1, + + e12=e21, + e13=e23, + e14=e24, + e15=e25, + + e21=e12, + e23=e13, + e24=e14, + e25=e15 +] +endmodule + +module sensor3 = sensor1 +[ + state1=state3, + + str1=str3, + fin1=fin3, + + m1_t1=m3_t1, + m1_t2=m3_t2, + m3_t1=m1_t1, + m3_t2=m1_t2, + + resource1=resource3, + resource3=resource1, + + e12=e32, + e13=e31, + e14=e34, + e15=e35, + + e31=e13, + e32=e12, + e34=e14, + e35=e15 +] +endmodule + +module sensor4 = sensor1 +[ + state1=state4, + + str1=str4, + fin1=fin4, + + m1_t1=m4_t1, + m1_t2=m4_t2, + + m4_t1=m1_t1, + m4_t2=m1_t2, + + resource1=resource4, + resource4=resource1, + + e12=e42, + e13=e43, + e14=e41, + e15=e45, + + e41=e14, + e42=e12, + e43=e13, + e45=e15 +] +endmodule + + + + + + +// labels and formulae for property specification +formula finished = (status=6); +label "end" = (status=7); + +formula task1_completed = finished + & ((t1_r1=1)=>((m1_t1=1&resource1=1)|(m2_t1=1&resource2=1)|(m3_t1=1&resource3=1)|(m4_t1=1&resource4=1))) + & ((t1_r2=1)=>((m1_t1=1&resource1=2)|(m2_t1=1&resource2=2)|(m3_t1=1&resource3=2)|(m4_t1=1&resource4=2))) + & ((t1_r3=1)=>((m1_t1=1&resource1=3)|(m2_t1=1&resource2=3)|(m3_t1=1&resource3=3)|(m4_t1=1&resource4=3))); + +formula task2_completed = finished + & ((t2_r1=1)=>((m1_t2=1&resource1=1)|(m2_t2=1&resource2=1)|(m3_t2=1&resource3=1)|(m4_t2=1&resource4=1))) + & ((t2_r2=1)=>((m1_t2=1&resource1=2)|(m2_t2=1&resource2=2)|(m3_t2=1&resource3=2)|(m4_t2=1&resource4=2))) + & ((t2_r3=1)=>((m1_t2=1&resource1=3)|(m2_t2=1&resource2=3)|(m3_t2=1&resource3=3)|(m4_t2=1&resource4=3))); + +formula agent1_joins_successful_team = (task1_completed & m1_t1=1) | (task2_completed & m1_t2=1); +formula agent1_joins_successful_team_of_1 = (task1_completed & m1_t1=1 & team_size_t1=1) | (task2_completed & m1_t2=1 & team_size_t2=1); +formula agent1_joins_successful_team_of_2 = (task1_completed & m1_t1=1 & team_size_t1=2) | (task2_completed & m1_t2=1 & team_size_t2=2); +formula agent1_joins_successful_team_of_3 = (task1_completed & m1_t1=1 & team_size_t1=3) | (task2_completed & m1_t2=1 & team_size_t2=3); + +formula agent2_joins_successful_team = (task1_completed & m2_t1=1) | (task2_completed & m2_t2=1); +formula agent2_joins_successful_team_of_1 = (task1_completed & m2_t1=1 & team_size_t1=1) | (task2_completed & m2_t2=1 & team_size_t2=1); +formula agent2_joins_successful_team_of_2 = (task1_completed & m2_t1=1 & team_size_t1=2) | (task2_completed & m2_t2=1 & team_size_t2=2); +formula agent2_joins_successful_team_of_3 = (task1_completed & m2_t1=1 & team_size_t1=3) | (task2_completed & m2_t2=1 & team_size_t2=3); + +formula agent3_joins_successful_team = (task1_completed & m3_t1=1) | (task2_completed & m3_t2=1); +formula agent3_joins_successful_team_of_1 = (task1_completed & m3_t1=1 & team_size_t1=1) | (task2_completed & m3_t2=1 & team_size_t2=1); +formula agent3_joins_successful_team_of_2 = (task1_completed & m3_t1=1 & team_size_t1=2) | (task2_completed & m3_t2=1 & team_size_t2=2); +formula agent3_joins_successful_team_of_3 = (task1_completed & m3_t1=1 & team_size_t1=3) | (task2_completed & m3_t2=1 & team_size_t2=3); + +formula agent4_joins_successful_team = (task1_completed & m4_t1=1) | (task2_completed & m4_t2=1); +formula agent4_joins_successful_team_of_1 = (task1_completed & m4_t1=1 & team_size_t1=1) | (task2_completed & m4_t2=1 & team_size_t2=1); +formula agent4_joins_successful_team_of_2 = (task1_completed & m4_t1=1 & team_size_t1=2) | (task2_completed & m4_t2=1 & team_size_t2=2); +formula agent4_joins_successful_team_of_3 = (task1_completed & m4_t1=1 & team_size_t1=3) | (task2_completed & m4_t2=1 & team_size_t2=3); + + +// rewards +rewards "w_1_total" + [] agent1_joins_successful_team : 1; + [] agent2_joins_successful_team : 1; + [] agent3_joins_successful_team : 1; + [] agent4_joins_successful_team : 1; +endrewards + +rewards "w_2_total" + [] task1_completed : 1; + [] task2_completed : 1; +endrewards + + + + + diff --git a/examples/multiobjective/mdp/team/team3obj_4_numerical.pctl b/examples/multiobjective/mdp/team/team3obj_4_numerical.pctl new file mode 100644 index 000000000..3712e6d0d --- /dev/null +++ b/examples/multiobjective/mdp/team/team3obj_4_numerical.pctl @@ -0,0 +1 @@ + multi(Pmax=? [ F task1_completed ], R{"w_1_total"}>=2.423469388 [ C ], P>=0.5 [ F task2_completed ]) diff --git a/examples/multiobjective/mdp/team/team3obj_4_pareto.pctl b/examples/multiobjective/mdp/team/team3obj_4_pareto.pctl new file mode 100644 index 000000000..8343ec77e --- /dev/null +++ b/examples/multiobjective/mdp/team/team3obj_4_pareto.pctl @@ -0,0 +1 @@ + multi(Pmax=? [ F task1_completed ], R{"w_1_total"}max=? [ C ], Pmax=? [ F task2_completed ]) diff --git a/examples/multiobjective/mdp/team/team3obj_5.nm b/examples/multiobjective/mdp/team/team3obj_5.nm new file mode 100644 index 000000000..85214bc8c --- /dev/null +++ b/examples/multiobjective/mdp/team/team3obj_5.nm @@ -0,0 +1,532 @@ +mdp + +// parameters +const int n_resources = 3; +const int n_tasks = 2; +const int n_sensors = 5; + + +// sensor resources +const int resource1=1; +const int resource2=2; +const int resource3=3; +const int resource4=1; +const int resource5=2; + +// network configuration +const int e12=1; +const int e13=1; +const int e14=1; +const int e15=1; + +const int e21=e12; +const int e23=1; +const int e24=1; +const int e25=1; + +const int e31=e13; +const int e32=e23; +const int e34=1; +const int e35=1; + +const int e41=e14; +const int e42=e24; +const int e43=e34; +const int e45=1; + +const int e51=e15; +const int e52=e25; +const int e53=e35; +const int e54=e45; + + +// agent is committed to some team +formula committed = (m1_t1+m1_t2) > 0; + +// formulae to compute team sizes +formula team_size_t1 = m1_t1+m2_t1+m3_t1+m4_t1+m5_t1; +formula team_size_t2 = m1_t2+m2_t2+m3_t2+m4_t2+m5_t2; + +// formulae to check whether the agent can join the team +formula can_join_t1 = e12*m2_t1 + e13*m3_t1 + e14*m4_t1 + e15*m5_t1 > 0; +formula can_join_t2 = e12*m2_t2 + e13*m3_t2 + e14*m4_t2 + e15*m5_t2 > 0; + +// formulae to check whether agent has the resource required by the task +formula has_resource_t1 = ( (t1_r1=1&resource1=1) | (t1_r2=1&resource1=2) | (t1_r3=1&resource1=3) ); +formula has_resource_t2 = ( (t2_r1=1&resource1=1) | (t2_r2=1&resource1=2) | (t2_r3=1&resource1=3) ); + +// formulae to check whether the resource of an agent has been already filled in the team +formula resource_filled_t1 = (m2_t1=1 & resource1=resource2) | (m3_t1=1 & resource1=resource3) | (m4_t1=1 & resource1=resource4) | (m5_t1=1 & resource1=resource5); +formula resource_filled_t2 = (m2_t2=1 & resource1=resource2) | (m3_t2=1 & resource1=resource3) | (m4_t2=1 & resource1=resource4) | (m5_t2=1 & resource1=resource5); + +// formula to compute team initiation probability (assuming each agent has at least one connection) +formula IP = (e12*(1-((m2_t1+m2_t2)=0?0:1))+e13*(1-((m3_t1+m3_t2)=0?0:1))+e14*(1-((m4_t1+m4_t2)=0?0:1))+e15*(1-((m5_t1+m5_t2)=0?0:1))) / (e12+e13+e14+e15); + + +module controller // schedules the algorithm + + // algorithm status + status : [0..8]; + + // task resource indicator variables + t1_r1 : [0..1]; + t1_r2 : [0..1]; + t1_r3 : [0..1]; + + t2_r1 : [0..1]; + t2_r2 : [0..1]; + t2_r3 : [0..1]; + + // schedule placeholders + turn1 : [0..n_sensors]; + turn2 : [0..n_sensors]; + turn3 : [0..n_sensors]; + turn4 : [0..n_sensors]; + turn5 : [0..n_sensors]; + + // selecting schedule uniformly at random + [] status=0 -> 1/120 : (turn1'=1) & (turn2'=2) & (turn3'=3) & (turn4'=4) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=2) & (turn3'=3) & (turn4'=5) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=2) & (turn3'=4) & (turn4'=3) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=2) & (turn3'=4) & (turn4'=5) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=2) & (turn3'=5) & (turn4'=3) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=2) & (turn3'=5) & (turn4'=4) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=3) & (turn3'=2) & (turn4'=4) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=3) & (turn3'=2) & (turn4'=5) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=3) & (turn3'=4) & (turn4'=2) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=3) & (turn3'=4) & (turn4'=5) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=3) & (turn3'=5) & (turn4'=2) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=3) & (turn3'=5) & (turn4'=4) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=4) & (turn3'=2) & (turn4'=3) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=4) & (turn3'=2) & (turn4'=5) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=4) & (turn3'=3) & (turn4'=2) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=4) & (turn3'=3) & (turn4'=5) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=4) & (turn3'=5) & (turn4'=2) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=4) & (turn3'=5) & (turn4'=3) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=5) & (turn3'=2) & (turn4'=3) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=5) & (turn3'=2) & (turn4'=4) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=5) & (turn3'=3) & (turn4'=2) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=5) & (turn3'=3) & (turn4'=4) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=5) & (turn3'=4) & (turn4'=2) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=1) & (turn2'=5) & (turn3'=4) & (turn4'=3) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=1) & (turn3'=3) & (turn4'=4) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=1) & (turn3'=3) & (turn4'=5) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=1) & (turn3'=4) & (turn4'=3) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=1) & (turn3'=4) & (turn4'=5) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=1) & (turn3'=5) & (turn4'=3) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=1) & (turn3'=5) & (turn4'=4) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=3) & (turn3'=1) & (turn4'=4) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=3) & (turn3'=1) & (turn4'=5) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=3) & (turn3'=4) & (turn4'=1) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=3) & (turn3'=4) & (turn4'=5) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=3) & (turn3'=5) & (turn4'=1) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=3) & (turn3'=5) & (turn4'=4) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=4) & (turn3'=1) & (turn4'=3) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=4) & (turn3'=1) & (turn4'=5) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=4) & (turn3'=3) & (turn4'=1) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=4) & (turn3'=3) & (turn4'=5) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=4) & (turn3'=5) & (turn4'=1) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=4) & (turn3'=5) & (turn4'=3) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=5) & (turn3'=1) & (turn4'=3) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=5) & (turn3'=1) & (turn4'=4) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=5) & (turn3'=3) & (turn4'=1) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=5) & (turn3'=3) & (turn4'=4) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=5) & (turn3'=4) & (turn4'=1) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=2) & (turn2'=5) & (turn3'=4) & (turn4'=3) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=1) & (turn3'=2) & (turn4'=4) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=1) & (turn3'=2) & (turn4'=5) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=1) & (turn3'=4) & (turn4'=2) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=1) & (turn3'=4) & (turn4'=5) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=1) & (turn3'=5) & (turn4'=2) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=1) & (turn3'=5) & (turn4'=4) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=2) & (turn3'=1) & (turn4'=4) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=2) & (turn3'=1) & (turn4'=5) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=2) & (turn3'=4) & (turn4'=1) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=2) & (turn3'=4) & (turn4'=5) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=2) & (turn3'=5) & (turn4'=1) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=2) & (turn3'=5) & (turn4'=4) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=4) & (turn3'=1) & (turn4'=2) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=4) & (turn3'=1) & (turn4'=5) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=4) & (turn3'=2) & (turn4'=1) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=4) & (turn3'=2) & (turn4'=5) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=4) & (turn3'=5) & (turn4'=1) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=4) & (turn3'=5) & (turn4'=2) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=5) & (turn3'=1) & (turn4'=2) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=5) & (turn3'=1) & (turn4'=4) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=5) & (turn3'=2) & (turn4'=1) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=5) & (turn3'=2) & (turn4'=4) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=5) & (turn3'=4) & (turn4'=1) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=3) & (turn2'=5) & (turn3'=4) & (turn4'=2) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=1) & (turn3'=2) & (turn4'=3) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=1) & (turn3'=2) & (turn4'=5) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=1) & (turn3'=3) & (turn4'=2) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=1) & (turn3'=3) & (turn4'=5) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=1) & (turn3'=5) & (turn4'=2) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=1) & (turn3'=5) & (turn4'=3) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=2) & (turn3'=1) & (turn4'=3) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=2) & (turn3'=1) & (turn4'=5) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=2) & (turn3'=3) & (turn4'=1) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=2) & (turn3'=3) & (turn4'=5) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=2) & (turn3'=5) & (turn4'=1) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=2) & (turn3'=5) & (turn4'=3) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=3) & (turn3'=1) & (turn4'=2) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=3) & (turn3'=1) & (turn4'=5) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=3) & (turn3'=2) & (turn4'=1) & (turn5'=5) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=3) & (turn3'=2) & (turn4'=5) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=3) & (turn3'=5) & (turn4'=1) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=3) & (turn3'=5) & (turn4'=2) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=5) & (turn3'=1) & (turn4'=2) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=5) & (turn3'=1) & (turn4'=3) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=5) & (turn3'=2) & (turn4'=1) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=5) & (turn3'=2) & (turn4'=3) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=5) & (turn3'=3) & (turn4'=1) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=4) & (turn2'=5) & (turn3'=3) & (turn4'=2) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=1) & (turn3'=2) & (turn4'=3) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=1) & (turn3'=2) & (turn4'=4) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=1) & (turn3'=3) & (turn4'=2) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=1) & (turn3'=3) & (turn4'=4) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=1) & (turn3'=4) & (turn4'=2) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=1) & (turn3'=4) & (turn4'=3) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=2) & (turn3'=1) & (turn4'=3) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=2) & (turn3'=1) & (turn4'=4) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=2) & (turn3'=3) & (turn4'=1) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=2) & (turn3'=3) & (turn4'=4) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=2) & (turn3'=4) & (turn4'=1) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=2) & (turn3'=4) & (turn4'=3) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=3) & (turn3'=1) & (turn4'=2) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=3) & (turn3'=1) & (turn4'=4) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=3) & (turn3'=2) & (turn4'=1) & (turn5'=4) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=3) & (turn3'=2) & (turn4'=4) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=3) & (turn3'=4) & (turn4'=1) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=3) & (turn3'=4) & (turn4'=2) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=4) & (turn3'=1) & (turn4'=2) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=4) & (turn3'=1) & (turn4'=3) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=4) & (turn3'=2) & (turn4'=1) & (turn5'=3) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=4) & (turn3'=2) & (turn4'=3) & (turn5'=1) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=4) & (turn3'=3) & (turn4'=1) & (turn5'=2) & (status'=1) + + 1/120 : (turn1'=5) & (turn2'=4) & (turn3'=3) & (turn4'=2) & (turn5'=1) & (status'=1); + + + // initialising non-empty tasks uniformly at random + [] status=1 -> 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=0) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=0) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=0) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=0) & (t2_r2'=1) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=0) & (t2_r3'=1) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=0) & (status'=2) + + 1/49 : (t1_r1'=1) & (t1_r2'=1) & (t1_r3'=1) & (t2_r1'=1) & (t2_r2'=1) & (t2_r3'=1) & (status'=2); + + // executing the schedule + + // 1st round + [str1] status=2 & turn1=1 -> (status'=2); + [fin1] status=2 & turn1=1 -> (status'=3); + [str2] status=2 & turn1=2 -> (status'=2); + [fin2] status=2 & turn1=2 -> (status'=3); + [str3] status=2 & turn1=3 -> (status'=2); + [fin3] status=2 & turn1=3 -> (status'=3); + [str4] status=2 & turn1=4 -> (status'=2); + [fin4] status=2 & turn1=4 -> (status'=3); + [str5] status=2 & turn1=5 -> (status'=2); + [fin5] status=2 & turn1=5 -> (status'=3); + + // 2nd round + [str1] status=3 & turn2=1 -> (status'=3); + [fin1] status=3 & turn2=1 -> (status'=4); + [str2] status=3 & turn2=2 -> (status'=3); + [fin2] status=3 & turn2=2 -> (status'=4); + [str3] status=3 & turn2=3 -> (status'=3); + [fin3] status=3 & turn2=3 -> (status'=4); + [str4] status=3 & turn2=4 -> (status'=3); + [fin4] status=3 & turn2=4 -> (status'=4); + [str5] status=3 & turn2=5 -> (status'=3); + [fin5] status=3 & turn2=5 -> (status'=4); + + // 3rd round + [str1] status=4 & turn3=1 -> (status'=4); + [fin1] status=4 & turn3=1 -> (status'=5); + [str2] status=4 & turn3=2 -> (status'=4); + [fin2] status=4 & turn3=2 -> (status'=5); + [str3] status=4 & turn3=3 -> (status'=4); + [fin3] status=4 & turn3=3 -> (status'=5); + [str4] status=4 & turn3=4 -> (status'=4); + [fin4] status=4 & turn3=4 -> (status'=5); + [str5] status=4 & turn3=5 -> (status'=4); + [fin5] status=4 & turn3=5 -> (status'=5); + + // 4th round + [str1] status=5 & turn4=1 -> (status'=5); + [fin1] status=5 & turn4=1 -> (status'=6); + [str2] status=5 & turn4=2 -> (status'=5); + [fin2] status=5 & turn4=2 -> (status'=6); + [str3] status=5 & turn4=3 -> (status'=5); + [fin3] status=5 & turn4=3 -> (status'=6); + [str4] status=5 & turn4=4 -> (status'=5); + [fin4] status=5 & turn4=4 -> (status'=6); + [str5] status=5 & turn4=5 -> (status'=5); + [fin5] status=5 & turn4=5 -> (status'=6); + + // 5th round + [str1] status=6 & turn5=1 -> (status'=6); + [fin1] status=6 & turn5=1 -> (status'=7); + [str2] status=6 & turn5=2 -> (status'=6); + [fin2] status=6 & turn5=2 -> (status'=7); + [str3] status=6 & turn5=3 -> (status'=6); + [fin3] status=6 & turn5=3 -> (status'=7); + [str4] status=6 & turn5=4 -> (status'=6); + [fin4] status=6 & turn5=4 -> (status'=7); + [str5] status=6 & turn5=5 -> (status'=6); + [fin5] status=6 & turn5=5 -> (status'=7); + + [] status=7 -> (status'=8); + + [] status=8 -> (status'=8); + +endmodule + +module sensor1 + + state1 : [0..1]; + + // team membership indicators + m1_t1 : [0..1]; + m1_t2 : [0..1]; + + // starting turn, selecting order of tasks + [str1] state1=0 -> (state1'=1); + + // if there is no team and has required skill - initiating the team + [] state1=1 & !committed & team_size_t1=0 & has_resource_t1 -> (m1_t1'=1); + [] state1=1 & !committed & team_size_t2=0 & has_resource_t2 -> (m1_t2'=1); + + // if team already exists and one of the neighbours is in it - joining the team + [] state1=1 & !committed & team_size_t1>0 & can_join_t1 & has_resource_t1 & !resource_filled_t1 -> (m1_t1'=1); + [] state1=1 & !committed & team_size_t2>0 & can_join_t2 & has_resource_t2 & !resource_filled_t2 -> (m1_t2'=1); + + [fin1] state1>0 -> (state1'=0); + +endmodule + +module sensor2 = sensor1 +[ + state1=state2, + + str1=str2, + fin1=fin2, + + m1_t1=m2_t1, + m1_t2=m2_t2, + + m2_t1=m1_t1, + m2_t2=m1_t2, + + resource1=resource2, + resource2=resource1, + + e12=e21, + e13=e23, + e14=e24, + e15=e25, + + e21=e12, + e23=e13, + e24=e14, + e25=e15 +] +endmodule + +module sensor3 = sensor1 +[ + state1=state3, + + str1=str3, + fin1=fin3, + + m1_t1=m3_t1, + m1_t2=m3_t2, + m3_t1=m1_t1, + m3_t2=m1_t2, + + resource1=resource3, + resource3=resource1, + + e12=e32, + e13=e31, + e14=e34, + e15=e35, + + e31=e13, + e32=e12, + e34=e14, + e35=e15 +] +endmodule + +module sensor4 = sensor1 +[ + state1=state4, + + str1=str4, + fin1=fin4, + + m1_t1=m4_t1, + m1_t2=m4_t2, + + m4_t1=m1_t1, + m4_t2=m1_t2, + + resource1=resource4, + resource4=resource1, + + e12=e42, + e13=e43, + e14=e41, + e15=e45, + + e41=e14, + e42=e12, + e43=e13, + e45=e15 +] +endmodule + +module sensor5 = sensor1 +[ + state1=state5, + + str1=str5, + fin1=fin5, + + m1_t1=m5_t1, + m1_t2=m5_t2, + + m5_t1=m1_t1, + m5_t2=m1_t2, + + resource1=resource5, + resource5=resource1, + + e12=e52, + e13=e53, + e14=e54, + e15=e51, + + e51=e15, + e52=e12, + e53=e13, + e54=e14 +] +endmodule + +// formulae for scheduling +formula s1_sched = (turn1=1 | turn2=1 | turn3=1 | turn4=1 | turn5=1); +formula s2_sched = (turn1=2 | turn2=2 | turn3=2 | turn4=2 | turn5=2); +formula s3_sched = (turn1=3 | turn2=3 | turn3=3 | turn4=3 | turn5=3); +formula s4_sched = (turn1=4 | turn2=4 | turn3=4 | turn4=4 | turn5=4); +formula s5_sched = (turn1=5 | turn2=5 | turn3=5 | turn4=5 | turn5=5); +formula all_not_sched = !(s1_sched | s2_sched | s3_sched | s4_sched | s5_sched); +formula all_sched = (s1_sched & s2_sched & s3_sched & s4_sched & s5_sched); + + + + +// labels and formulae for property specification +formula finished = (status=7); +label "end" = (status=8); + + +formula task1_completed = finished + & ((t1_r1=1)=>((m1_t1=1&resource1=1)|(m2_t1=1&resource2=1)|(m3_t1=1&resource3=1)|(m4_t1=1&resource4=1)|(m5_t1=1&resource5=1))) + & ((t1_r2=1)=>((m1_t1=1&resource1=2)|(m2_t1=1&resource2=2)|(m3_t1=1&resource3=2)|(m4_t1=1&resource4=2)|(m5_t1=1&resource5=2))) + & ((t1_r3=1)=>((m1_t1=1&resource1=3)|(m2_t1=1&resource2=3)|(m3_t1=1&resource3=3)|(m4_t1=1&resource4=3)|(m5_t1=1&resource5=3))); + +formula task2_completed = finished + & ((t2_r1=1)=>((m1_t2=1&resource1=1)|(m2_t2=1&resource2=1)|(m3_t2=1&resource3=1)|(m4_t2=1&resource4=1)|(m5_t2=1&resource5=1))) + & ((t2_r2=1)=>((m1_t2=1&resource1=2)|(m2_t2=1&resource2=2)|(m3_t2=1&resource3=2)|(m4_t2=1&resource4=2)|(m5_t2=1&resource5=2))) + & ((t2_r3=1)=>((m1_t2=1&resource1=3)|(m2_t2=1&resource2=3)|(m3_t2=1&resource3=3)|(m4_t2=1&resource4=3)|(m5_t2=1&resource5=3))); + +formula agent1_joins_successful_team = (task1_completed & m1_t1=1) | (task2_completed & m1_t2=1); +formula agent1_joins_successful_team_of_1 = (task1_completed & m1_t1=1 & team_size_t1=1) | (task2_completed & m1_t2=1 & team_size_t2=1); +formula agent1_joins_successful_team_of_2 = (task1_completed & m1_t1=1 & team_size_t1=2) | (task2_completed & m1_t2=1 & team_size_t2=2); +formula agent1_joins_successful_team_of_3 = (task1_completed & m1_t1=1 & team_size_t1=3) | (task2_completed & m1_t2=1 & team_size_t2=3); + +formula agent2_joins_successful_team = (task1_completed & m2_t1=1) | (task2_completed & m2_t2=1); +formula agent2_joins_successful_team_of_1 = (task1_completed & m2_t1=1 & team_size_t1=1) | (task2_completed & m2_t2=1 & team_size_t2=1); +formula agent2_joins_successful_team_of_2 = (task1_completed & m2_t1=1 & team_size_t1=2) | (task2_completed & m2_t2=1 & team_size_t2=2); +formula agent2_joins_successful_team_of_3 = (task1_completed & m2_t1=1 & team_size_t1=3) | (task2_completed & m2_t2=1 & team_size_t2=3); + +formula agent3_joins_successful_team = (task1_completed & m3_t1=1) | (task2_completed & m3_t2=1); +formula agent3_joins_successful_team_of_1 = (task1_completed & m3_t1=1 & team_size_t1=1) | (task2_completed & m3_t2=1 & team_size_t2=1); +formula agent3_joins_successful_team_of_2 = (task1_completed & m3_t1=1 & team_size_t1=2) | (task2_completed & m3_t2=1 & team_size_t2=2); +formula agent3_joins_successful_team_of_3 = (task1_completed & m3_t1=1 & team_size_t1=3) | (task2_completed & m3_t2=1 & team_size_t2=3); + +formula agent4_joins_successful_team = (task1_completed & m4_t1=1) | (task2_completed & m4_t2=1); +formula agent4_joins_successful_team_of_1 = (task1_completed & m4_t1=1 & team_size_t1=1) | (task2_completed & m4_t2=1 & team_size_t2=1); +formula agent4_joins_successful_team_of_2 = (task1_completed & m4_t1=1 & team_size_t1=2) | (task2_completed & m4_t2=1 & team_size_t2=2); +formula agent4_joins_successful_team_of_3 = (task1_completed & m4_t1=1 & team_size_t1=3) | (task2_completed & m4_t2=1 & team_size_t2=3); + +formula agent5_joins_successful_team = (task1_completed & m5_t1=1) | (task2_completed & m5_t2=1); +formula agent5_joins_successful_team_of_1 = (task1_completed & m5_t1=1 & team_size_t1=1) | (task2_completed & m5_t2=1 & team_size_t2=1); +formula agent5_joins_successful_team_of_2 = (task1_completed & m5_t1=1 & team_size_t1=2) | (task2_completed & m5_t2=1 & team_size_t2=2); +formula agent5_joins_successful_team_of_3 = (task1_completed & m5_t1=1 & team_size_t1=3) | (task2_completed & m5_t2=1 & team_size_t2=3); + +// rewards +rewards "w_1_total" + [] agent1_joins_successful_team : 1; + [] agent2_joins_successful_team : 1; + [] agent3_joins_successful_team : 1; + [] agent4_joins_successful_team : 1; + [] agent5_joins_successful_team : 1; +endrewards + +rewards "w_2_total" + [] task1_completed : 1; + [] task2_completed : 1; +endrewards + + + + + diff --git a/examples/multiobjective/mdp/team/team3obj_5_numerical.pctl b/examples/multiobjective/mdp/team/team3obj_5_numerical.pctl new file mode 100644 index 000000000..541c02e9c --- /dev/null +++ b/examples/multiobjective/mdp/team/team3obj_5_numerical.pctl @@ -0,0 +1 @@ + multi(Pmax=? [ F task1_completed ], R{"w_1_total"}>=2.753061224 [ C ], P>=0.5 [ F task2_completed ]) diff --git a/examples/multiobjective/mdp/team/team3obj_5_pareto.pctl b/examples/multiobjective/mdp/team/team3obj_5_pareto.pctl new file mode 100644 index 000000000..8343ec77e --- /dev/null +++ b/examples/multiobjective/mdp/team/team3obj_5_pareto.pctl @@ -0,0 +1 @@ + multi(Pmax=? [ F task1_completed ], R{"w_1_total"}max=? [ C ], Pmax=? [ F task2_completed ]) diff --git a/examples/multiobjective/mdp/zeroconf-tb/origFiles/zeroconf_host_multi2_time.nm b/examples/multiobjective/mdp/zeroconf-tb/origFiles/zeroconf_host_multi2_time.nm new file mode 100644 index 000000000..ad0f4a3b7 --- /dev/null +++ b/examples/multiobjective/mdp/zeroconf-tb/origFiles/zeroconf_host_multi2_time.nm @@ -0,0 +1,169 @@ +// 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 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 (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 (x'=0) & (probes'=probes+1); + [send2] l=2 & ip=2 & x=2 & probes (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 (x'=min(x+1,TIME_MAX_X)); + [time] l=3 & mess=0 & defend=1 & x (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 (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) (c1'=min(c1+1,M+1)); + [time] error=0 & env=0 & k=2 & min(c1,c2) (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'=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 diff --git a/examples/multiobjective/mdp/zeroconf-tb/origFiles/zeroconf_host_multi4_time.nm b/examples/multiobjective/mdp/zeroconf-tb/origFiles/zeroconf_host_multi4_time.nm new file mode 100644 index 000000000..71984c286 --- /dev/null +++ b/examples/multiobjective/mdp/zeroconf-tb/origFiles/zeroconf_host_multi4_time.nm @@ -0,0 +1,174 @@ +// 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 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 (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 (x'=0) & (probes'=probes+1); + [send2] l=2 & ip=2 & x=2 & probes (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 (x'=min(x+1,TIME_MAX_X)); + [time] l=3 & mess=0 & defend=1 & x (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 (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) (c1'=min(c1+1,M+1)); + [time] error=0 & env=0 & k=2 & min(c1,c2,c3,c4) (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)); + [time] error=0 & env=0 & k=3 & min(c1,c2,c3,c4) (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) (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'=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 diff --git a/examples/multiobjective/mdp/zeroconf-tb/origFiles/zeroconf_host_multi_time.pctl b/examples/multiobjective/mdp/zeroconf-tb/origFiles/zeroconf_host_multi_time.pctl new file mode 100644 index 000000000..078b34c65 --- /dev/null +++ b/examples/multiobjective/mdp/zeroconf-tb/origFiles/zeroconf_host_multi_time.pctl @@ -0,0 +1,13 @@ +// 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) ]) diff --git a/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb2_14.nm b/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb2_14.nm new file mode 100644 index 000000000..bc433a7f1 --- /dev/null +++ b/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb2_14.nm @@ -0,0 +1,169 @@ +// 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 +const int M=1; // time between sending and receiving a message +const int T=14; + + +//------------------------------------------------------------- +// 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 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 (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 (x'=0) & (probes'=probes+1); + [send2] l=2 & ip=2 & x=2 & probes (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 (x'=min(x+1,TIME_MAX_X)); + [time] l=3 & mess=0 & defend=1 & x (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 (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 + +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) (c1'=min(c1+1,M+1)); + [time] error=0 & env=0 & k=2 & min(c1,c2) (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 + +module time_error + + time_error : [0..1]; + done : [0..1]; + t : [0..T]; + + [time] t (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 diff --git a/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb2_14_numerical.pctl b/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb2_14_numerical.pctl new file mode 100644 index 000000000..e8119cae5 --- /dev/null +++ b/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb2_14_numerical.pctl @@ -0,0 +1 @@ + multi(Pmax=? [ F time_error=1 ] , P>=0.81[ G (error=0) ]) diff --git a/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb2_14_pareto.pctl b/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb2_14_pareto.pctl new file mode 100644 index 000000000..db9996245 --- /dev/null +++ b/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb2_14_pareto.pctl @@ -0,0 +1 @@ + multi(Pmax=? [ F time_error=1 ] , Pmax=? [ G (error=0) ]) diff --git a/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb4_10.nm b/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb4_10.nm new file mode 100644 index 000000000..a8485f2c9 --- /dev/null +++ b/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb4_10.nm @@ -0,0 +1,174 @@ +// 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 +const int M=1; // time between sending and receiving a message +const int T=10; + +//------------------------------------------------------------- +// 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 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 (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 (x'=0) & (probes'=probes+1); + [send2] l=2 & ip=2 & x=2 & probes (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 (x'=min(x+1,TIME_MAX_X)); + [time] l=3 & mess=0 & defend=1 & x (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 (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 + + +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) (c1'=min(c1+1,M+1)); + [time] error=0 & env=0 & k=2 & min(c1,c2,c3,c4) (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)); + [time] error=0 & env=0 & k=3 & min(c1,c2,c3,c4) (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) (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 + +module time_error + + time_error : [0..1]; + done : [0..1]; + t : [0..T]; + + [time] t (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 diff --git a/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb4_10_numerical.pctl b/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb4_10_numerical.pctl new file mode 100644 index 000000000..84762f3cc --- /dev/null +++ b/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb4_10_numerical.pctl @@ -0,0 +1 @@ + multi(Pmax=? [ F time_error=1 ] , P>=0.993141[ G (error=0) ]) diff --git a/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb4_10_pareto.pctl b/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb4_10_pareto.pctl new file mode 100644 index 000000000..db9996245 --- /dev/null +++ b/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb4_10_pareto.pctl @@ -0,0 +1 @@ + multi(Pmax=? [ F time_error=1 ] , Pmax=? [ G (error=0) ]) diff --git a/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb4_14.nm b/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb4_14.nm new file mode 100644 index 000000000..4e56d344f --- /dev/null +++ b/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb4_14.nm @@ -0,0 +1,173 @@ +// 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 +const int M=1; // time between sending and receiving a message +const int T=14; + +//------------------------------------------------------------- +// 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 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 (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 (x'=0) & (probes'=probes+1); + [send2] l=2 & ip=2 & x=2 & probes (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 (x'=min(x+1,TIME_MAX_X)); + [time] l=3 & mess=0 & defend=1 & x (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 (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 + +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) (c1'=min(c1+1,M+1)); + [time] error=0 & env=0 & k=2 & min(c1,c2,c3,c4) (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)); + [time] error=0 & env=0 & k=3 & min(c1,c2,c3,c4) (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) (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 + +module time_error + + time_error : [0..1]; + done : [0..1]; + t : [0..T]; + + [time] t (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 diff --git a/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb4_14_numerical.pctl b/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb4_14_numerical.pctl new file mode 100644 index 000000000..84762f3cc --- /dev/null +++ b/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb4_14_numerical.pctl @@ -0,0 +1 @@ + multi(Pmax=? [ F time_error=1 ] , P>=0.993141[ G (error=0) ]) diff --git a/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb4_14_pareto.pctl b/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb4_14_pareto.pctl new file mode 100644 index 000000000..db9996245 --- /dev/null +++ b/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb4_14_pareto.pctl @@ -0,0 +1 @@ + multi(Pmax=? [ F time_error=1 ] , Pmax=? [ G (error=0) ]) diff --git a/examples/multiobjective/mdp/zeroconf/origFiles/zeroconf_host_multi.pctl b/examples/multiobjective/mdp/zeroconf/origFiles/zeroconf_host_multi.pctl new file mode 100644 index 000000000..b047c3818 --- /dev/null +++ b/examples/multiobjective/mdp/zeroconf/origFiles/zeroconf_host_multi.pctl @@ -0,0 +1,13 @@ +// 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 l=4 & ip=1 ] , P>=1-p_fail [ G (error=0) ]) + +// Pareto query for assume-guarantee check +"pareto": multi(Pmax=? [ F l=4 & ip=1 ] , Pmax=? [ G (error=0) ]) diff --git a/examples/multiobjective/mdp/zeroconf/origFiles/zeroconf_host_multi2.nm b/examples/multiobjective/mdp/zeroconf/origFiles/zeroconf_host_multi2.nm new file mode 100644 index 000000000..82ade9881 --- /dev/null +++ b/examples/multiobjective/mdp/zeroconf/origFiles/zeroconf_host_multi2.nm @@ -0,0 +1,149 @@ +// 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 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 (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 (x'=0) & (probes'=probes+1); + [send2] l=2 & ip=2 & x=2 & probes (x'=0) & (probes'=probes+1); + // sent K probes and waited 2 seconds + [] 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 (x'=min(x+1,TIME_MAX_X)); + [time] l=3 & mess=0 & defend=1 & x (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 (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) (c1'=min(c1+1,M+1)); + [time] error=0 & env=0 & k=2 & min(c1,c2) (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 + diff --git a/examples/multiobjective/mdp/zeroconf/origFiles/zeroconf_host_multi4.nm b/examples/multiobjective/mdp/zeroconf/origFiles/zeroconf_host_multi4.nm new file mode 100644 index 000000000..1c3bafd53 --- /dev/null +++ b/examples/multiobjective/mdp/zeroconf/origFiles/zeroconf_host_multi4.nm @@ -0,0 +1,153 @@ +// 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 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 (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 (x'=0) & (probes'=probes+1); + [send2] l=2 & ip=2 & x=2 & probes (x'=0) & (probes'=probes+1); + // sent K probes and waited 2 seconds + [] 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 (x'=min(x+1,TIME_MAX_X)); + [time] l=3 & mess=0 & defend=1 & x (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 (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) (c1'=min(c1+1,M+1)); + [time] error=0 & env=0 & k=2 & min(c1,c2,c3,c4) (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)); + [time] error=0 & env=0 & k=3 & min(c1,c2,c3,c4) (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) (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 diff --git a/examples/multiobjective/mdp/zeroconf/origFiles/zeroconf_host_multi6.nm b/examples/multiobjective/mdp/zeroconf/origFiles/zeroconf_host_multi6.nm new file mode 100644 index 000000000..cde22a032 --- /dev/null +++ b/examples/multiobjective/mdp/zeroconf/origFiles/zeroconf_host_multi6.nm @@ -0,0 +1,157 @@ +// 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=6; // 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 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 (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 (x'=0) & (probes'=probes+1); + [send2] l=2 & ip=2 & x=2 & probes (x'=0) & (probes'=probes+1); + // sent K probes and waited 2 seconds + [] 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 (x'=min(x+1,TIME_MAX_X)); + [time] l=3 & mess=0 & defend=1 & x (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 (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_error6 + + env : [0..1]; // 0 active and 1 done + k : [0..6]; // 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 + c5 : [0..M+1]; // time since fifth message + c6 : [0..M+1]; // time since sixth 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,c5,c6) (c1'=min(c1+1,M+1)); + [time] error=0 & env=0 & k=2 & min(c1,c2,c3,c4,c5,c6) (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)); + [time] error=0 & env=0 & k=3 & min(c1,c2,c3,c4,c5,c6) (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,c5,c6) (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)) & (c3'=min(c3+1,M+1)) & (c4'=min(c4+1,M+1)); + [time] error=0 & env=0 & k=5 & min(c1,c2,c3,c4,c5,c6) (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)) & (c3'=min(c3+1,M+1)) & (c4'=min(c4+1,M+1)) & (c5'=min(c5+1,M+1)); + [time] error=0 & env=0 & k=6 & min(c1,c2,c3,c4,c5,c6) (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)) & (c3'=min(c3+1,M+1)) & (c4'=min(c4+1,M+1)) & (c5'=min(c5+1,M+1)) & (c6'=min(c6+1,M+1)); + // all clocks reached their bound so an error + [time] error=0 & env=0 & min(c1,c2,c3,c4,c5,c6)=M -> (error'=1); + // send a reply (then done) + [rec1] error=0 & env=0 & k>0 & min(c1,c2,c3,c4,c5,c6)<=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 + diff --git a/examples/multiobjective/mdp/zeroconf/origFiles/zeroconf_host_multi8.nm b/examples/multiobjective/mdp/zeroconf/origFiles/zeroconf_host_multi8.nm new file mode 100644 index 000000000..e843a8c12 --- /dev/null +++ b/examples/multiobjective/mdp/zeroconf/origFiles/zeroconf_host_multi8.nm @@ -0,0 +1,161 @@ +// 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=8; // 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 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 (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 (x'=0) & (probes'=probes+1); + [send2] l=2 & ip=2 & x=2 & probes (x'=0) & (probes'=probes+1); + // sent K probes and waited 2 seconds + [] 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 (x'=min(x+1,TIME_MAX_X)); + [time] l=3 & mess=0 & defend=1 & x (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 (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_error8 + + env : [0..1]; // 0 active and 1 done + k : [0..8]; // 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 + c5 : [0..M+1]; // time since fifth message + c6 : [0..M+1]; // time since sixth message + c7 : [0..M+1]; // time since seventh message + c8 : [0..M+1]; // time since eighth 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,c5,c6,c7,c8) (c1'=min(c1+1,M+1)); + [time] error=0 & env=0 & k=2 & min(c1,c2,c3,c4,c5,c6,c7,c8) (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)); + [time] error=0 & env=0 & k=3 & min(c1,c2,c3,c4,c5,c6,c7,c8) (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,c5,c6,c7,c8) (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)) & (c3'=min(c3+1,M+1)) & (c4'=min(c4+1,M+1)); + [time] error=0 & env=0 & k=5 & min(c1,c2,c3,c4,c5,c6,c7,c8) (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)) & (c3'=min(c3+1,M+1)) & (c4'=min(c4+1,M+1)) & (c5'=min(c5+1,M+1)); + [time] error=0 & env=0 & k=6 & min(c1,c2,c3,c4,c5,c6,c7,c8) (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)) & (c3'=min(c3+1,M+1)) & (c4'=min(c4+1,M+1)) & (c5'=min(c5+1,M+1)) & (c6'=min(c6+1,M+1)); + [time] error=0 & env=0 & k=7 & min(c1,c2,c3,c4,c5,c6,c7,c8) (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)) & (c3'=min(c3+1,M+1)) & (c4'=min(c4+1,M+1)) & (c5'=min(c5+1,M+1)) & (c6'=min(c6+1,M+1)) & (c7'=min(c7+1,M+1)); + [time] error=0 & env=0 & k=8 & min(c1,c2,c3,c4,c5,c6,c7,c8) (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)) & (c3'=min(c3+1,M+1)) & (c4'=min(c4+1,M+1)) & (c5'=min(c5+1,M+1)) & (c6'=min(c6+1,M+1)) & (c7'=min(c7+1,M+1)) & (c8'=min(c8+1,M+1)); + // all clocks reached their bound so an error + [time] error=0 & env=0 & min(c1,c2,c3,c4,c5,c6,c7,c8)=M -> (error'=1); + // send a reply (then done) + [rec1] error=0 & env=0 & k>0 & min(c1,c2,c3,c4,c5,c6,c7,c8)<=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 + diff --git a/examples/multiobjective/mdp/zeroconf/zeroconf4.nm b/examples/multiobjective/mdp/zeroconf/zeroconf4.nm new file mode 100644 index 000000000..3d5988df2 --- /dev/null +++ b/examples/multiobjective/mdp/zeroconf/zeroconf4.nm @@ -0,0 +1,153 @@ +// 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 +const int M=1; // time between sending and receiving a message + + +//------------------------------------------------------------- +// 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 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 (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 (x'=0) & (probes'=probes+1); + [send2] l=2 & ip=2 & x=2 & probes (x'=0) & (probes'=probes+1); + // sent K probes and waited 2 seconds + [] 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 (x'=min(x+1,TIME_MAX_X)); + [time] l=3 & mess=0 & defend=1 & x (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 (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 + +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) (c1'=min(c1+1,M+1)); + [time] error=0 & env=0 & k=2 & min(c1,c2,c3,c4) (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)); + [time] error=0 & env=0 & k=3 & min(c1,c2,c3,c4) (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) (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 diff --git a/examples/multiobjective/mdp/zeroconf/zeroconf4_numerical.pctl b/examples/multiobjective/mdp/zeroconf/zeroconf4_numerical.pctl new file mode 100644 index 000000000..646a77159 --- /dev/null +++ b/examples/multiobjective/mdp/zeroconf/zeroconf4_numerical.pctl @@ -0,0 +1 @@ + multi(Pmax=? [ F l=4 & ip=1 ] , P>=0.993141[ G (error=0) ]) diff --git a/examples/multiobjective/mdp/zeroconf/zeroconf4_pareto.pctl b/examples/multiobjective/mdp/zeroconf/zeroconf4_pareto.pctl new file mode 100644 index 000000000..694c19e14 --- /dev/null +++ b/examples/multiobjective/mdp/zeroconf/zeroconf4_pareto.pctl @@ -0,0 +1 @@ + multi(Pmax=? [ F l=4 & ip=1 ] , Pmax=? [ G (error=0) ]) diff --git a/examples/multiobjective/mdp/zeroconf/zeroconf6.nm b/examples/multiobjective/mdp/zeroconf/zeroconf6.nm new file mode 100644 index 000000000..99ce361a5 --- /dev/null +++ b/examples/multiobjective/mdp/zeroconf/zeroconf6.nm @@ -0,0 +1,157 @@ +// 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=6; // 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 +const int M=1; // time between sending and receiving a message + + +//------------------------------------------------------------- +// 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 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 (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 (x'=0) & (probes'=probes+1); + [send2] l=2 & ip=2 & x=2 & probes (x'=0) & (probes'=probes+1); + // sent K probes and waited 2 seconds + [] 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 (x'=min(x+1,TIME_MAX_X)); + [time] l=3 & mess=0 & defend=1 & x (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 (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 + +module env_error6 + + env : [0..1]; // 0 active and 1 done + k : [0..6]; // 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 + c5 : [0..M+1]; // time since fifth message + c6 : [0..M+1]; // time since sixth 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,c5,c6) (c1'=min(c1+1,M+1)); + [time] error=0 & env=0 & k=2 & min(c1,c2,c3,c4,c5,c6) (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)); + [time] error=0 & env=0 & k=3 & min(c1,c2,c3,c4,c5,c6) (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,c5,c6) (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)) & (c3'=min(c3+1,M+1)) & (c4'=min(c4+1,M+1)); + [time] error=0 & env=0 & k=5 & min(c1,c2,c3,c4,c5,c6) (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)) & (c3'=min(c3+1,M+1)) & (c4'=min(c4+1,M+1)) & (c5'=min(c5+1,M+1)); + [time] error=0 & env=0 & k=6 & min(c1,c2,c3,c4,c5,c6) (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)) & (c3'=min(c3+1,M+1)) & (c4'=min(c4+1,M+1)) & (c5'=min(c5+1,M+1)) & (c6'=min(c6+1,M+1)); + // all clocks reached their bound so an error + [time] error=0 & env=0 & min(c1,c2,c3,c4,c5,c6)=M -> (error'=1); + // send a reply (then done) + [rec1] error=0 & env=0 & k>0 & min(c1,c2,c3,c4,c5,c6)<=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 + diff --git a/examples/multiobjective/mdp/zeroconf/zeroconf6_numerical.pctl b/examples/multiobjective/mdp/zeroconf/zeroconf6_numerical.pctl new file mode 100644 index 000000000..016da75a6 --- /dev/null +++ b/examples/multiobjective/mdp/zeroconf/zeroconf6_numerical.pctl @@ -0,0 +1 @@ + multi(Pmax=? [ F l=4 & ip=1 ] , P>=0.9997523901[ G (error=0) ]) diff --git a/examples/multiobjective/mdp/zeroconf/zeroconf6_pareto.pctl b/examples/multiobjective/mdp/zeroconf/zeroconf6_pareto.pctl new file mode 100644 index 000000000..694c19e14 --- /dev/null +++ b/examples/multiobjective/mdp/zeroconf/zeroconf6_pareto.pctl @@ -0,0 +1 @@ + multi(Pmax=? [ F l=4 & ip=1 ] , Pmax=? [ G (error=0) ]) diff --git a/examples/multiobjective/mdp/zeroconf/zeroconf8.nm b/examples/multiobjective/mdp/zeroconf/zeroconf8.nm new file mode 100644 index 000000000..871e06e2b --- /dev/null +++ b/examples/multiobjective/mdp/zeroconf/zeroconf8.nm @@ -0,0 +1,161 @@ +// 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=8; // 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 +const int M=1; // time between sending and receiving a message + + +//------------------------------------------------------------- +// 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 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 (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 (x'=0) & (probes'=probes+1); + [send2] l=2 & ip=2 & x=2 & probes (x'=0) & (probes'=probes+1); + // sent K probes and waited 2 seconds + [] 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 (x'=min(x+1,TIME_MAX_X)); + [time] l=3 & mess=0 & defend=1 & x (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 (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 + +module env_error8 + + env : [0..1]; // 0 active and 1 done + k : [0..8]; // 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 + c5 : [0..M+1]; // time since fifth message + c6 : [0..M+1]; // time since sixth message + c7 : [0..M+1]; // time since seventh message + c8 : [0..M+1]; // time since eighth 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,c5,c6,c7,c8) (c1'=min(c1+1,M+1)); + [time] error=0 & env=0 & k=2 & min(c1,c2,c3,c4,c5,c6,c7,c8) (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)); + [time] error=0 & env=0 & k=3 & min(c1,c2,c3,c4,c5,c6,c7,c8) (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,c5,c6,c7,c8) (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)) & (c3'=min(c3+1,M+1)) & (c4'=min(c4+1,M+1)); + [time] error=0 & env=0 & k=5 & min(c1,c2,c3,c4,c5,c6,c7,c8) (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)) & (c3'=min(c3+1,M+1)) & (c4'=min(c4+1,M+1)) & (c5'=min(c5+1,M+1)); + [time] error=0 & env=0 & k=6 & min(c1,c2,c3,c4,c5,c6,c7,c8) (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)) & (c3'=min(c3+1,M+1)) & (c4'=min(c4+1,M+1)) & (c5'=min(c5+1,M+1)) & (c6'=min(c6+1,M+1)); + [time] error=0 & env=0 & k=7 & min(c1,c2,c3,c4,c5,c6,c7,c8) (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)) & (c3'=min(c3+1,M+1)) & (c4'=min(c4+1,M+1)) & (c5'=min(c5+1,M+1)) & (c6'=min(c6+1,M+1)) & (c7'=min(c7+1,M+1)); + [time] error=0 & env=0 & k=8 & min(c1,c2,c3,c4,c5,c6,c7,c8) (c1'=min(c1+1,M+1)) & (c2'=min(c2+1,M+1)) & (c3'=min(c3+1,M+1)) & (c4'=min(c4+1,M+1)) & (c5'=min(c5+1,M+1)) & (c6'=min(c6+1,M+1)) & (c7'=min(c7+1,M+1)) & (c8'=min(c8+1,M+1)); + // all clocks reached their bound so an error + [time] error=0 & env=0 & min(c1,c2,c3,c4,c5,c6,c7,c8)=M -> (error'=1); + // send a reply (then done) + [rec1] error=0 & env=0 & k>0 & min(c1,c2,c3,c4,c5,c6,c7,c8)<=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 + diff --git a/examples/multiobjective/mdp/zeroconf/zeroconf8_numerical.pctl b/examples/multiobjective/mdp/zeroconf/zeroconf8_numerical.pctl new file mode 100644 index 000000000..61b79cd13 --- /dev/null +++ b/examples/multiobjective/mdp/zeroconf/zeroconf8_numerical.pctl @@ -0,0 +1 @@ + multi(Pmax=? [ F l=4 & ip=1 ] , P>=0.9999910613[ G (error=0) ]) diff --git a/examples/multiobjective/mdp/zeroconf/zeroconf8_pareto.pctl b/examples/multiobjective/mdp/zeroconf/zeroconf8_pareto.pctl new file mode 100644 index 000000000..694c19e14 --- /dev/null +++ b/examples/multiobjective/mdp/zeroconf/zeroconf8_pareto.pctl @@ -0,0 +1 @@ + multi(Pmax=? [ F l=4 & ip=1 ] , Pmax=? [ G (error=0) ]) diff --git a/examples/plot.sh b/examples/plot.sh deleted file mode 100755 index 33788422f..000000000 --- a/examples/plot.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash -executable="timeout 3600 ../build/src/storm" -arguments="-i 1000000 --parametric --parametricRegion --region:samplemode off" -mkdir plotresults - -declare -a brpPars=("16" "128" "256" "384" "512" "640" "768" "896" "1024" "1152" "1280" "1408" "1536" "1664" "1792" "1920" "2048" "2176" "2304" "2432" "2560" "2688" "2816" "2944" "3072" "3200" "3328" "3456" "3584" "3712" "3840" "3968" "4096") - - -declare -a repPars=("3" "6" "9" "12" "15" "18" "21" "24" "27" "30" "33" "36" "39" "42" "45" "48" "51" "54" "57" "60" "63" "66" "69" "72" "75" "78" "81" "84" "87" "90" "93" "96" "99" "100") - - - - - -plot_brp2 () { -for brp2par in "${brpPars[@]}" -do -$executable -s ./pdtmc/brp_rewards2/brp_rewards2.pm -const N="$brp2par",MAX=5 --prop ./pdtmc/brp_rewards2/brp_rewards2.prctl --region:regionfile ./pdtmc/brp_rewards2/brp_rewards2_regions.txt $arguments | tee -a ./plotresults/pdtmc_brp_rewards2.log -done -} - -plot_brp4 () { -for brp4par in "${brpPars[@]}" -do -$executable -s ./pdtmc/brp_rewards4/brp_rewards4.pm -const N="$brp4par",MAX=5 --prop ./pdtmc/brp_rewards4/brp_rewards4.prctl --region:regionfile ./pdtmc/brp_rewards4/brp_rewards4_regions.txt $arguments | tee -a ./plotresults/pdtmc_brp_rewards4.log -done -} - - -plot_rep2 () { -for rep2par in "${repPars[@]}" -do -$executable -s ./pmdp/reporter2/reporter2.pm -const Xsize="$rep2par",Ysize="$rep2par",MAXTRIES=2,B=2 --prop ./pmdp/reporter2/reporter2.prctl --region:regionfile ./pmdp/reporter2/reporter2_regions.txt $arguments | tee -a ./plotresults/pmdp_reporter2.log -done -} - - -plot_rep4 () { -for rep4par in "${repPars[@]}" -do -$executable -s ./pmdp/reporter4/reporter4.pm -const Xsize="$rep4par",Ysize="$rep4par",MAXTRIES=2,B=2 --prop ./pmdp/reporter4/reporter4.prctl --region:regionfile ./pmdp/reporter4/reporter4_regions.txt $arguments | tee -a ./plotresults/pmdp_reporter4.log -done -} - -plot_brp2 & -plot_brp4 & -plot_rep2 & -plot_rep4 & -wait - -echo "done!" diff --git a/resources/3rdparty/CMakeLists.txt b/resources/3rdparty/CMakeLists.txt index 57d36770f..29e67afd0 100644 --- a/resources/3rdparty/CMakeLists.txt +++ b/resources/3rdparty/CMakeLists.txt @@ -210,24 +210,49 @@ if(USE_CARL) endif() endif() + ############################################################# ## ## SMT-RAT ## ############################################################# -# No find routine yet -#find_package(smtrat QUIET) -# Not yet supported -set(smtrat_FOUND OFF) set(STORM_HAVE_SMTRAT OFF) -if(smtrat_FOUND) - set(STORM_HAVE_SMTRAT ON) - message(STATUS "StoRM - Linking with smtrat.") - include_directories("${smtrat_INCLUDE_DIR}") - list(APPEND STORM_LINK_LIBRARIES ${smtrat_LIBRARIES}) +if(USE_SMTRAT) + find_package(smtrat QUIET REQUIRED) + if(smtrat_FOUND) + set(STORM_HAVE_SMTRAT ON) + message(STATUS "StoRM - Linking with smtrat.") + include_directories("${smtrat_INCLUDE_DIR}") + list(APPEND STORM_LINK_LIBRARIES ${smtrat_LIBRARIES}) + else() + message(FATAL_ERROR "StoRM - SMT-RAT was requested but not found") + endif() endif() + +############################################################# +## +## HyPro +## +############################################################# + +set(STORM_HAVE_HYPRO OFF) +if(USE_HYPRO) + find_package(hypro QUIET REQUIRED) + if(hypro_FOUND) + set(STORM_HAVE_HYPRO ON) + message(STATUS "StoRM - Linking with hypro ${hypro_VERSION_STRING}") + include_directories("${hypro_INCLUDE_DIR}") + link_directories( /Users/tim/hypro/build ) + list(APPEND STORM_LINK_LIBRARIES ${hypro_LIBRARIES}) + else() + message(FATAL_ERROR "StoRM - HyPro was requested but not found") + endif() +endif() + + + ############################################################# ## ## GiNaC diff --git a/resources/3rdparty/carl b/resources/3rdparty/carl deleted file mode 160000 index d67f98622..000000000 --- a/resources/3rdparty/carl +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d67f986226cf846ba6366cdbe2abc21dff375542 diff --git a/src/adapters/HyproAdapter.h b/src/adapters/HyproAdapter.h new file mode 100644 index 000000000..f1ffc8a95 --- /dev/null +++ b/src/adapters/HyproAdapter.h @@ -0,0 +1,47 @@ +#ifndef STORM_ADAPTERS_HYPROADAPTER_H_ +#define STORM_ADAPTERS_HYPROADAPTER_H_ + +// Include config to know whether HyPro is available or not. +#include "storm-config.h" + +#ifdef STORM_HAVE_HYPRO + +#include +#include +#include +#include + +#include "src/adapters/CarlAdapter.h" +#include "src/storage/geometry/HalfSpace.h" + +namespace storm { + namespace adapters { + + + template + std::vector fromHypro(hypro::vector_t const& v) { + return std::vector(v.data(), v.data() + v.rows()); + } + + template + hypro::vector_t toHypro(std::vector const& v) { + return hypro::vector_t::Map(v.data(), v.size()); + } + + template + hypro::Halfspace toHypro(storm::storage::geometry::Halfspace const& h){ + T offset = h.offset(); + return hypro::Halfspace(toHypro(h.normalVector()), std::move(offset)); + } + + template + storm::storage::geometry::Halfspace fromHypro(hypro::Halfspace const& h){ + T offset = h.offset(); + return storm::storage::geometry::Halfspace(fromHypro(h.normal()), std::move(offset)); + } + + } +} +#endif //STORM_HAVE_HYPRO + +#endif /* STORM_ADAPTERS_HYPROADAPTER_H_ */ diff --git a/src/logic/CloneVisitor.cpp b/src/logic/CloneVisitor.cpp index fe7182bf7..89fe36cd3 100644 --- a/src/logic/CloneVisitor.cpp +++ b/src/logic/CloneVisitor.cpp @@ -76,6 +76,14 @@ namespace storm { return std::static_pointer_cast(std::make_shared(f)); } + boost::any CloneVisitor::visit(MultiObjectiveFormula const& f, boost::any const& data) const { + std::vector> subformulas; + for(auto const& subF : f.getSubformulas()){ + subformulas.push_back(boost::any_cast>(subF->accept(*this, data))); + } + return std::static_pointer_cast(std::make_shared(subformulas)); + } + boost::any CloneVisitor::visit(NextFormula const& f, boost::any const& data) const { std::shared_ptr subformula = boost::any_cast>(f.getSubformula().accept(*this, data)); return std::static_pointer_cast(std::make_shared(subformula)); @@ -91,6 +99,10 @@ namespace storm { return std::static_pointer_cast(std::make_shared(subformula, f.getOptionalRewardModelName(), f.getOperatorInformation())); } + boost::any CloneVisitor::visit(TotalRewardFormula const& f, boost::any const& data) const { + return std::static_pointer_cast(std::make_shared()); + } + boost::any CloneVisitor::visit(UnaryBooleanStateFormula const& f, boost::any const& data) const { std::shared_ptr subformula = boost::any_cast>(f.getSubformula().accept(*this, data)); return std::static_pointer_cast(std::make_shared(f.getOperator(), subformula)); diff --git a/src/logic/CloneVisitor.h b/src/logic/CloneVisitor.h index 23fc3ffdc..437278e97 100644 --- a/src/logic/CloneVisitor.h +++ b/src/logic/CloneVisitor.h @@ -25,9 +25,11 @@ namespace storm { virtual boost::any visit(InstantaneousRewardFormula const& f, boost::any const& data) const override; virtual boost::any visit(LongRunAverageOperatorFormula const& f, boost::any const& data) const override; virtual boost::any visit(LongRunAverageRewardFormula const& f, boost::any const& data) const override; + virtual boost::any visit(MultiObjectiveFormula const& f, boost::any const& data) const override; virtual boost::any visit(NextFormula const& f, boost::any const& data) const override; virtual boost::any visit(ProbabilityOperatorFormula const& f, boost::any const& data) const override; virtual boost::any visit(RewardOperatorFormula const& f, boost::any const& data) const override; + virtual boost::any visit(TotalRewardFormula const& f, boost::any const& data) const override; virtual boost::any visit(UnaryBooleanStateFormula const& f, boost::any const& data) const override; virtual boost::any visit(UntilFormula const& f, boost::any const& data) const override; }; @@ -36,4 +38,4 @@ namespace storm { } -#endif /* STORM_LOGIC_CLONEVISITOR_H_ */ \ No newline at end of file +#endif /* STORM_LOGIC_CLONEVISITOR_H_ */ diff --git a/src/logic/ComparisonType.h b/src/logic/ComparisonType.h index b91b2432b..bf932fc2b 100644 --- a/src/logic/ComparisonType.h +++ b/src/logic/ComparisonType.h @@ -14,7 +14,20 @@ namespace storm { inline bool isLowerBound(ComparisonType t) { return (t == ComparisonType::Greater || t == ComparisonType::GreaterEqual); } - + + inline ComparisonType invert(ComparisonType t) { + switch(t) { + case ComparisonType::Less: + return ComparisonType::GreaterEqual; + case ComparisonType::LessEqual: + return ComparisonType::Greater; + case ComparisonType::Greater: + return ComparisonType::LessEqual; + case ComparisonType::GreaterEqual: + return ComparisonType::Less; + } + } + std::ostream& operator<<(std::ostream& out, ComparisonType const& comparisonType); } } diff --git a/src/logic/Formula.cpp b/src/logic/Formula.cpp index de915eaa1..12a3a7212 100644 --- a/src/logic/Formula.cpp +++ b/src/logic/Formula.cpp @@ -17,6 +17,10 @@ namespace storm { return false; } + bool Formula::isMultiObjectiveFormula() const { + return false; + } + bool Formula::isBinaryStateFormula() const { return false; } @@ -129,6 +133,10 @@ namespace storm { return false; } + bool Formula::isTotalRewardFormula() const { + return false; + } + bool Formula::isReachabilityTimeFormula() const { return false; } @@ -183,6 +191,14 @@ namespace storm { return dynamic_cast(*this); } + MultiObjectiveFormula& Formula::asMultiObjectiveFormula() { + return dynamic_cast(*this); + } + + MultiObjectiveFormula const& Formula::asMultiObjectiveFormula() const { + return dynamic_cast(*this); + } + BinaryStateFormula& Formula::asBinaryStateFormula() { return dynamic_cast(*this); } @@ -351,6 +367,14 @@ namespace storm { return dynamic_cast(*this); } + TotalRewardFormula& Formula::asTotalRewardFormula() { + return dynamic_cast(*this); + } + + TotalRewardFormula const& Formula::asTotalRewardFormula() const { + return dynamic_cast(*this); + } + InstantaneousRewardFormula& Formula::asInstantaneousRewardFormula() { return dynamic_cast(*this); } @@ -458,4 +482,4 @@ namespace storm { return formula.writeToStream(out); } } -} \ No newline at end of file +} diff --git a/src/logic/Formula.h b/src/logic/Formula.h index d5625a296..9c7a62009 100644 --- a/src/logic/Formula.h +++ b/src/logic/Formula.h @@ -46,6 +46,8 @@ namespace storm { virtual bool isBinaryBooleanStateFormula() const; virtual bool isUnaryBooleanStateFormula() const; + + virtual bool isMultiObjectiveFormula() const; // Operator formulas. virtual bool isOperatorFormula() const; @@ -74,6 +76,7 @@ namespace storm { virtual bool isInstantaneousRewardFormula() const; virtual bool isReachabilityRewardFormula() const; virtual bool isLongRunAverageRewardFormula() const; + virtual bool isTotalRewardFormula() const; // Expected time formulas. virtual bool isReachabilityTimeFormula() const; @@ -101,6 +104,9 @@ namespace storm { StateFormula& asStateFormula(); StateFormula const& asStateFormula() const; + MultiObjectiveFormula& asMultiObjectiveFormula(); + MultiObjectiveFormula const& asMultiObjectiveFormula() const; + BinaryStateFormula& asBinaryStateFormula(); BinaryStateFormula const& asBinaryStateFormula() const; @@ -164,6 +170,9 @@ namespace storm { CumulativeRewardFormula& asCumulativeRewardFormula(); CumulativeRewardFormula const& asCumulativeRewardFormula() const; + TotalRewardFormula& asTotalRewardFormula(); + TotalRewardFormula const& asTotalRewardFormula() const; + InstantaneousRewardFormula& asInstantaneousRewardFormula(); InstantaneousRewardFormula const& asInstantaneousRewardFormula() const; @@ -215,4 +224,4 @@ namespace storm { } } -#endif /* STORM_LOGIC_FORMULA_H_ */ \ No newline at end of file +#endif /* STORM_LOGIC_FORMULA_H_ */ diff --git a/src/logic/FormulaInformationVisitor.cpp b/src/logic/FormulaInformationVisitor.cpp index b44045766..ad588eac1 100644 --- a/src/logic/FormulaInformationVisitor.cpp +++ b/src/logic/FormulaInformationVisitor.cpp @@ -61,6 +61,14 @@ namespace storm { return FormulaInformation(); } + boost::any FormulaInformationVisitor::visit(MultiObjectiveFormula const& f, boost::any const& data) const { + FormulaInformation result; + for(auto const& subF : f.getSubformulas()){ + result.join(boost::any_cast(subF->accept(*this))); + } + return result; + } + boost::any FormulaInformationVisitor::visit(NextFormula const& f, boost::any const& data) const { return boost::any_cast(f.getSubformula().accept(*this)).setContainsNextFormula(); } @@ -73,6 +81,10 @@ namespace storm { return boost::any_cast(f.getSubformula().accept(*this)).setContainsRewardOperator(); } + boost::any FormulaInformationVisitor::visit(TotalRewardFormula const& f, boost::any const& data) const { + return FormulaInformation(); + } + boost::any FormulaInformationVisitor::visit(UnaryBooleanStateFormula const& f, boost::any const& data) const { return f.getSubformula().accept(*this); } @@ -82,4 +94,4 @@ namespace storm { } } -} \ No newline at end of file +} diff --git a/src/logic/FormulaInformationVisitor.h b/src/logic/FormulaInformationVisitor.h index 8e706cabf..5ce306c47 100644 --- a/src/logic/FormulaInformationVisitor.h +++ b/src/logic/FormulaInformationVisitor.h @@ -24,9 +24,11 @@ namespace storm { virtual boost::any visit(InstantaneousRewardFormula const& f, boost::any const& data) const override; virtual boost::any visit(LongRunAverageOperatorFormula const& f, boost::any const& data) const override; virtual boost::any visit(LongRunAverageRewardFormula const& f, boost::any const& data) const override; + virtual boost::any visit(MultiObjectiveFormula const& f, boost::any const& data) const override; virtual boost::any visit(NextFormula const& f, boost::any const& data) const override; virtual boost::any visit(ProbabilityOperatorFormula const& f, boost::any const& data) const override; virtual boost::any visit(RewardOperatorFormula const& f, boost::any const& data) const override; + virtual boost::any visit(TotalRewardFormula const& f, boost::any const& data) const override; virtual boost::any visit(UnaryBooleanStateFormula const& f, boost::any const& data) const override; virtual boost::any visit(UntilFormula const& f, boost::any const& data) const override; }; @@ -35,4 +37,4 @@ namespace storm { } -#endif /* STORM_LOGIC_FORMULAINFORMATIONVISITOR_H_ */ \ No newline at end of file +#endif /* STORM_LOGIC_FORMULAINFORMATIONVISITOR_H_ */ diff --git a/src/logic/FormulaVisitor.h b/src/logic/FormulaVisitor.h index 1208b63a6..8909a5427 100644 --- a/src/logic/FormulaVisitor.h +++ b/src/logic/FormulaVisitor.h @@ -23,9 +23,11 @@ namespace storm { virtual boost::any visit(InstantaneousRewardFormula const& f, boost::any const& data) const = 0; virtual boost::any visit(LongRunAverageOperatorFormula const& f, boost::any const& data) const = 0; virtual boost::any visit(LongRunAverageRewardFormula const& f, boost::any const& data) const = 0; + virtual boost::any visit(MultiObjectiveFormula const& f, boost::any const& data) const = 0; virtual boost::any visit(NextFormula const& f, boost::any const& data) const = 0; virtual boost::any visit(ProbabilityOperatorFormula const& f, boost::any const& data) const = 0; virtual boost::any visit(RewardOperatorFormula const& f, boost::any const& data) const = 0; + virtual boost::any visit(TotalRewardFormula const& f, boost::any const& data) const = 0; virtual boost::any visit(UnaryBooleanStateFormula const& f, boost::any const& data) const = 0; virtual boost::any visit(UntilFormula const& f, boost::any const& data) const = 0; }; @@ -33,4 +35,4 @@ namespace storm { } } -#endif /* STORM_LOGIC_FORMULAVISITOR_H_ */ \ No newline at end of file +#endif /* STORM_LOGIC_FORMULAVISITOR_H_ */ diff --git a/src/logic/Formulas.h b/src/logic/Formulas.h index 7183dcb01..0a39b0788 100644 --- a/src/logic/Formulas.h +++ b/src/logic/Formulas.h @@ -18,7 +18,9 @@ #include "src/logic/RewardOperatorFormula.h" #include "src/logic/StateFormula.h" #include "src/logic/LongRunAverageOperatorFormula.h" +#include "src/logic/MultiObjectiveFormula.h" #include "src/logic/TimeOperatorFormula.h" +#include "src/logic/TotalRewardFormula.h" #include "src/logic/UnaryBooleanStateFormula.h" #include "src/logic/UnaryPathFormula.h" #include "src/logic/UnaryStateFormula.h" @@ -26,4 +28,4 @@ #include "src/logic/ConditionalFormula.h" #include "src/logic/ProbabilityOperatorFormula.h" #include "src/logic/RewardOperatorFormula.h" -#include "src/logic/ComparisonType.h" \ No newline at end of file +#include "src/logic/ComparisonType.h" diff --git a/src/logic/FormulasForwardDeclarations.h b/src/logic/FormulasForwardDeclarations.h index 43fcf0ab9..fcd24d1ed 100644 --- a/src/logic/FormulasForwardDeclarations.h +++ b/src/logic/FormulasForwardDeclarations.h @@ -20,12 +20,14 @@ namespace storm { class InstantaneousRewardFormula; class LongRunAverageOperatorFormula; class LongRunAverageRewardFormula; + class MultiObjectiveFormula; class NextFormula; class OperatorFormula; class PathFormula; class ProbabilityOperatorFormula; class RewardOperatorFormula; class StateFormula; + class TotalRewardFormula; class UnaryBooleanStateFormula; class UnaryPathFormula; class UnaryStateFormula; @@ -33,4 +35,4 @@ namespace storm { } } -#endif /* STORM_LOGIC_FORMULASFORWARDDECLARATIONS_H_ */ \ No newline at end of file +#endif /* STORM_LOGIC_FORMULASFORWARDDECLARATIONS_H_ */ diff --git a/src/logic/FragmentChecker.cpp b/src/logic/FragmentChecker.cpp index b86a96693..8fc26c315 100644 --- a/src/logic/FragmentChecker.cpp +++ b/src/logic/FragmentChecker.cpp @@ -159,6 +159,27 @@ namespace storm { return inherited.getSpecification().areLongRunAverageRewardFormulasAllowed(); } + boost::any FragmentChecker::visit(MultiObjectiveFormula const& f, boost::any const& data) const { + InheritedInformation const& inherited = boost::any_cast(data); + + FragmentSpecification subFormulaFragment(inherited.getSpecification()); + if(!inherited.getSpecification().areNestedMultiObjectiveFormulasAllowed()){ + subFormulaFragment.setMultiObjectiveFormulasAllowed(false); + } + if(!inherited.getSpecification().areNestedOperatorsInsideMultiObjectiveFormulasAllowed()){ + subFormulaFragment.setNestedOperatorsAllowed(false); + } + + bool result = inherited.getSpecification().areMultiObjectiveFormulasAllowed(); + for(auto const& subF : f.getSubformulas()){ + if(inherited.getSpecification().areOperatorsAtTopLevelOfMultiObjectiveFormulasRequired()){ + result = result && subF->isOperatorFormula(); + } + result = result && boost::any_cast(subF->accept(*this, InheritedInformation(subFormulaFragment))); + } + return result; + } + boost::any FragmentChecker::visit(NextFormula const& f, boost::any const& data) const { InheritedInformation const& inherited = boost::any_cast(data); bool result = inherited.getSpecification().areNextFormulasAllowed(); @@ -198,6 +219,11 @@ namespace storm { return result; } + boost::any FragmentChecker::visit(TotalRewardFormula const& f, boost::any const& data) const { + InheritedInformation const& inherited = boost::any_cast(data); + return inherited.getSpecification().areTotalRewardFormulasAllowed(); + } + boost::any FragmentChecker::visit(UnaryBooleanStateFormula const& f, boost::any const& data) const { InheritedInformation const& inherited = boost::any_cast(data); bool result = inherited.getSpecification().areUnaryBooleanStateFormulasAllowed(); @@ -217,4 +243,4 @@ namespace storm { return result; } } -} \ No newline at end of file +} diff --git a/src/logic/FragmentChecker.h b/src/logic/FragmentChecker.h index 9b0377aba..8410e4ee7 100644 --- a/src/logic/FragmentChecker.h +++ b/src/logic/FragmentChecker.h @@ -25,9 +25,11 @@ namespace storm { virtual boost::any visit(InstantaneousRewardFormula const& f, boost::any const& data) const override; virtual boost::any visit(LongRunAverageOperatorFormula const& f, boost::any const& data) const override; virtual boost::any visit(LongRunAverageRewardFormula const& f, boost::any const& data) const override; + virtual boost::any visit(MultiObjectiveFormula const& f, boost::any const& data) const override; virtual boost::any visit(NextFormula const& f, boost::any const& data) const override; virtual boost::any visit(ProbabilityOperatorFormula const& f, boost::any const& data) const override; virtual boost::any visit(RewardOperatorFormula const& f, boost::any const& data) const override; + virtual boost::any visit(TotalRewardFormula const& f, boost::any const& data) const override; virtual boost::any visit(UnaryBooleanStateFormula const& f, boost::any const& data) const override; virtual boost::any visit(UntilFormula const& f, boost::any const& data) const override; }; @@ -36,4 +38,4 @@ namespace storm { } -#endif /* STORM_LOGIC_FRAGMENTCHECKER_H_ */ \ No newline at end of file +#endif /* STORM_LOGIC_FRAGMENTCHECKER_H_ */ diff --git a/src/logic/FragmentSpecification.cpp b/src/logic/FragmentSpecification.cpp index 3dd2bf12b..8602b10ea 100644 --- a/src/logic/FragmentSpecification.cpp +++ b/src/logic/FragmentSpecification.cpp @@ -83,12 +83,32 @@ namespace storm { return csrl; } + FragmentSpecification multiObjective() { + FragmentSpecification multiObjective = propositional(); + + multiObjective.setMultiObjectiveFormulasAllowed(true); + multiObjective.setOperatorsAtTopLevelOfMultiObjectiveFormulasRequired(true); + multiObjective.setProbabilityOperatorsAllowed(true); + multiObjective.setUntilFormulasAllowed(true); + multiObjective.setGloballyFormulasAllowed(true); + multiObjective.setReachabilityProbabilityFormulasAllowed(true); + multiObjective.setRewardOperatorsAllowed(true); + multiObjective.setReachabilityRewardFormulasAllowed(true); + multiObjective.setTotalRewardFormulasAllowed(true); + multiObjective.setBoundedUntilFormulasAllowed(true); + multiObjective.setStepBoundedUntilFormulasAllowed(true); + + return multiObjective; + } + FragmentSpecification::FragmentSpecification() { probabilityOperator = false; rewardOperator = false; expectedTimeOperator = false; longRunAverageOperator = false; + multiObjectiveFormula = false; + globallyFormula = false; reachabilityProbabilityFormula = false; nextFormula = false; @@ -105,6 +125,7 @@ namespace storm { instantaneousRewardFormula = false; reachabilityRewardFormula = false; longRunAverageRewardFormula = false; + totalRewardFormula = false; conditionalProbabilityFormula = false; conditionalRewardFormula = false; @@ -113,6 +134,8 @@ namespace storm { nestedOperators = true; nestedPathFormulas = false; + nestedMultiObjectiveFormulas = false; + nestedOperatorsInsideMultiObjectiveFormulas = false; onlyEventuallyFormuluasInConditionalFormulas = true; stepBoundedUntilFormulas = false; timeBoundedUntilFormulas = false; @@ -122,6 +145,7 @@ namespace storm { quantitativeOperatorResults = true; operatorAtTopLevelRequired = false; + operatorsAtTopLevelOfMultiObjectiveFormulasRequired = false; } FragmentSpecification FragmentSpecification::copy() const { @@ -164,6 +188,15 @@ namespace storm { return *this; } + bool FragmentSpecification::areMultiObjectiveFormulasAllowed() const { + return multiObjectiveFormula; + } + + FragmentSpecification& FragmentSpecification::setMultiObjectiveFormulasAllowed( bool newValue) { + this->multiObjectiveFormula = newValue; + return *this; + } + bool FragmentSpecification::areGloballyFormulasAllowed() const { return globallyFormula; } @@ -290,6 +323,16 @@ namespace storm { return *this; } + + bool FragmentSpecification::areTotalRewardFormulasAllowed() const { + return totalRewardFormula; + } + + FragmentSpecification& FragmentSpecification::setTotalRewardFormulasAllowed(bool newValue) { + this->totalRewardFormula = newValue; + return *this; + } + bool FragmentSpecification::areConditionalProbabilityFormulasAllowed() const { return conditionalProbabilityFormula; } @@ -334,7 +377,25 @@ namespace storm { this->nestedPathFormulas = newValue; return *this; } - + + bool FragmentSpecification::areNestedMultiObjectiveFormulasAllowed() const { + return this->nestedMultiObjectiveFormulas; + } + + FragmentSpecification& FragmentSpecification::setNestedMultiObjectiveFormulasAllowed(bool newValue) { + this->nestedMultiObjectiveFormulas = newValue; + return *this; + } + + bool FragmentSpecification::areNestedOperatorsInsideMultiObjectiveFormulasAllowed() const { + return this->nestedOperatorsInsideMultiObjectiveFormulas; + } + + FragmentSpecification& FragmentSpecification::setNestedOperatorsInsideMultiObjectiveFormulasAllowed(bool newValue) { + this->nestedOperatorsInsideMultiObjectiveFormulas = newValue; + return *this; + } + bool FragmentSpecification::areOnlyEventuallyFormuluasInConditionalFormulasAllowed() const { return this->onlyEventuallyFormuluasInConditionalFormulas; } @@ -416,6 +477,15 @@ namespace storm { operatorAtTopLevelRequired = newValue; return *this; } + + bool FragmentSpecification::areOperatorsAtTopLevelOfMultiObjectiveFormulasRequired() const { + return operatorsAtTopLevelOfMultiObjectiveFormulasRequired; + } + + FragmentSpecification& FragmentSpecification::setOperatorsAtTopLevelOfMultiObjectiveFormulasRequired(bool newValue) { + operatorsAtTopLevelOfMultiObjectiveFormulasRequired = newValue; + return *this; + } } -} \ No newline at end of file +} diff --git a/src/logic/FragmentSpecification.h b/src/logic/FragmentSpecification.h index 6637d0750..587d49bf3 100644 --- a/src/logic/FragmentSpecification.h +++ b/src/logic/FragmentSpecification.h @@ -24,6 +24,9 @@ namespace storm { bool areLongRunAverageOperatorsAllowed() const; FragmentSpecification& setLongRunAverageOperatorsAllowed(bool newValue); + + bool areMultiObjectiveFormulasAllowed() const; + FragmentSpecification& setMultiObjectiveFormulasAllowed( bool newValue); bool areGloballyFormulasAllowed() const; FragmentSpecification& setGloballyFormulasAllowed(bool newValue); @@ -66,6 +69,9 @@ namespace storm { bool areLongRunAverageRewardFormulasAllowed() const; FragmentSpecification& setLongRunAverageRewardFormulasAllowed(bool newValue); + + bool areTotalRewardFormulasAllowed() const; + FragmentSpecification& setTotalRewardFormulasAllowed(bool newValue); bool areConditionalProbabilityFormulasAllowed() const; FragmentSpecification& setConditionalProbabilityFormulasAllowed(bool newValue); @@ -78,10 +84,16 @@ namespace storm { bool areNestedOperatorsAllowed() const; FragmentSpecification& setNestedOperatorsAllowed(bool newValue); - + bool areNestedPathFormulasAllowed() const; FragmentSpecification& setNestedPathFormulasAllowed(bool newValue); + bool areNestedMultiObjectiveFormulasAllowed() const; + FragmentSpecification& setNestedMultiObjectiveFormulasAllowed(bool newValue); + + bool areNestedOperatorsInsideMultiObjectiveFormulasAllowed() const; + FragmentSpecification& setNestedOperatorsInsideMultiObjectiveFormulasAllowed(bool newValue); + bool areOnlyEventuallyFormuluasInConditionalFormulasAllowed() const; FragmentSpecification& setOnlyEventuallyFormuluasInConditionalFormulasAllowed(bool newValue); @@ -103,6 +115,9 @@ namespace storm { bool isOperatorAtTopLevelRequired() const; FragmentSpecification& setOperatorAtTopLevelRequired(bool newValue); + bool areOperatorsAtTopLevelOfMultiObjectiveFormulasRequired() const; + FragmentSpecification& setOperatorsAtTopLevelOfMultiObjectiveFormulasRequired(bool newValue); + FragmentSpecification& setOperatorsAllowed(bool newValue); FragmentSpecification& setTimeAllowed(bool newValue); FragmentSpecification& setLongRunAverageProbabilitiesAllowed(bool newValue); @@ -114,6 +129,8 @@ namespace storm { bool expectedTimeOperator; bool longRunAverageOperator; + bool multiObjectiveFormula; + bool globallyFormula; bool reachabilityProbabilityFormula; bool nextFormula; @@ -130,6 +147,7 @@ namespace storm { bool instantaneousRewardFormula; bool reachabilityRewardFormula; bool longRunAverageRewardFormula; + bool totalRewardFormula; bool conditionalProbabilityFormula; bool conditionalRewardFormula; @@ -139,6 +157,8 @@ namespace storm { // Members that indicate certain restrictions. bool nestedOperators; bool nestedPathFormulas; + bool nestedMultiObjectiveFormulas; + bool nestedOperatorsInsideMultiObjectiveFormulas; bool onlyEventuallyFormuluasInConditionalFormulas; bool stepBoundedUntilFormulas; bool timeBoundedUntilFormulas; @@ -146,6 +166,7 @@ namespace storm { bool quantitativeOperatorResults; bool qualitativeOperatorResults; bool operatorAtTopLevelRequired; + bool operatorsAtTopLevelOfMultiObjectiveFormulasRequired; }; // Propositional. @@ -168,8 +189,11 @@ namespace storm { // CSL + cumulative, instantaneous, reachability and long-run rewards. FragmentSpecification csrl(); + + // Multi-Objective formulas. + FragmentSpecification multiObjective(); } } -#endif /* STORM_LOGIC_FRAGMENTSPECIFICATION_H_ */ \ No newline at end of file +#endif /* STORM_LOGIC_FRAGMENTSPECIFICATION_H_ */ diff --git a/src/logic/MultiObjectiveFormula.cpp b/src/logic/MultiObjectiveFormula.cpp new file mode 100644 index 000000000..861628e69 --- /dev/null +++ b/src/logic/MultiObjectiveFormula.cpp @@ -0,0 +1,99 @@ +#include "src/logic/MultiObjectiveFormula.h" + +#include "src/logic/FormulaVisitor.h" +#include "src/utility/macros.h" +#include "src/exceptions/InvalidArgumentException.h" + +namespace storm { + namespace logic { + + MultiObjectiveFormula::MultiObjectiveFormula(std::vector> const& subformulas) : subformulas(subformulas) { + //Intentionally left empty + } + + MultiObjectiveFormula::~MultiObjectiveFormula() { + //Intentionally left empty + } + + bool MultiObjectiveFormula::isMultiObjectiveFormula() const { + return true; + } + + bool MultiObjectiveFormula::hasQualitativeResult() const { + for(auto const& subformula : this->subformulas){ + if(subformula->hasQuantitativeResult()){ + return false; + } + } + return true; + } + + bool MultiObjectiveFormula::hasQuantitativeResult() const { + return !hasQualitativeResult(); + } + + bool MultiObjectiveFormula::hasNumericalResult() const { + bool hasExactlyOneQuantitativeSubformula = false; + for(auto const& subformula : this->subformulas){ + if(subformula->hasQuantitativeResult()){ + if(hasExactlyOneQuantitativeSubformula){ + return false; + } + hasExactlyOneQuantitativeSubformula=true; + } + } + return hasExactlyOneQuantitativeSubformula; + } + + bool MultiObjectiveFormula::hasParetoCurveResult() const { + return hasQuantitativeResult() && !hasNumericalResult(); + } + + Formula const& MultiObjectiveFormula::getSubformula(uint_fast64_t index) const { + STORM_LOG_THROW(index < getNumberOfSubformulas(), storm::exceptions::InvalidArgumentException, "Tried to access subformula with index " << index << " but there are only " << this->getNumberOfSubformulas() << " subformulas."); + return *this->subformulas[index]; + } + + uint_fast64_t MultiObjectiveFormula::getNumberOfSubformulas() const { + return this->subformulas.size(); + } + + std::vector> const& MultiObjectiveFormula::getSubformulas() const { + return this->subformulas; + } + + boost::any MultiObjectiveFormula::accept(FormulaVisitor const& visitor, boost::any const& data) const { + return visitor.visit(*this, data); + } + + void MultiObjectiveFormula::gatherAtomicExpressionFormulas(std::vector>& atomicExpressionFormulas) const { + for(auto const& subformula : this->subformulas){ + subformula->gatherAtomicExpressionFormulas(atomicExpressionFormulas); + } + } + + void MultiObjectiveFormula::gatherAtomicLabelFormulas(std::vector>& atomicLabelFormulas) const { + for(auto const& subformula : this->subformulas){ + subformula->gatherAtomicLabelFormulas(atomicLabelFormulas); + } + } + + void MultiObjectiveFormula::gatherReferencedRewardModels(std::set& referencedRewardModels) const { + for(auto const& subformula : this->subformulas){ + subformula->gatherReferencedRewardModels(referencedRewardModels); + } + } + + std::ostream& MultiObjectiveFormula::writeToStream(std::ostream &out) const { + out << "multi("; + for(uint_fast64_t index = 0; index < this->getNumberOfSubformulas(); ++index){ + if(index>0){ + out << ", "; + } + this->getSubformula(index).writeToStream(out); + } + out << ")"; + return out; + } + } +} diff --git a/src/logic/MultiObjectiveFormula.h b/src/logic/MultiObjectiveFormula.h new file mode 100644 index 000000000..e5e1dea2a --- /dev/null +++ b/src/logic/MultiObjectiveFormula.h @@ -0,0 +1,37 @@ +#ifndef STORM_LOGIC_MULTIOBJECTIVEFORMULA_H_ +#define STORM_LOGIC_MULTIOBJECTIVEFORMULA_H_ + +#include "src/logic/Formula.h" + +namespace storm { + namespace logic { + class MultiObjectiveFormula : public Formula { + public: + MultiObjectiveFormula(std::vector> const& subformulas); + + virtual ~MultiObjectiveFormula(); + + virtual bool isMultiObjectiveFormula() const override; + + virtual bool hasQualitativeResult() const override; // Result is true or false + virtual bool hasQuantitativeResult() const override; // Result is numerical or a pareto curve + virtual bool hasNumericalResult() const; // Result is numerical + virtual bool hasParetoCurveResult() const; // Result is a pareto curve + + Formula const& getSubformula(uint_fast64_t index) const; + uint_fast64_t getNumberOfSubformulas() const; + std::vector> const& getSubformulas() const; + + virtual boost::any accept(FormulaVisitor const& visitor, boost::any const& data) const override; + virtual void gatherAtomicExpressionFormulas(std::vector>& atomicExpressionFormulas) const override; + virtual void gatherAtomicLabelFormulas(std::vector>& atomicLabelFormulas) const override; + virtual void gatherReferencedRewardModels(std::set& referencedRewardModels) const override; + + virtual std::ostream& writeToStream(std::ostream& out) const override; + private: + std::vector> subformulas; + }; + } +} + +#endif /* STORM_LOGIC_MULTIOBJECTIVEFORMULA_H_ */ diff --git a/src/logic/OperatorFormula.h b/src/logic/OperatorFormula.h index a1247d431..633e0dda3 100644 --- a/src/logic/OperatorFormula.h +++ b/src/logic/OperatorFormula.h @@ -59,4 +59,4 @@ namespace storm { } } -#endif /* STORM_LOGIC_OPERATORFORMULA_H_ */ \ No newline at end of file +#endif /* STORM_LOGIC_OPERATORFORMULA_H_ */ diff --git a/src/logic/ToExpressionVisitor.cpp b/src/logic/ToExpressionVisitor.cpp index 53dfedeca..928275047 100644 --- a/src/logic/ToExpressionVisitor.cpp +++ b/src/logic/ToExpressionVisitor.cpp @@ -82,6 +82,10 @@ namespace storm { STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Cannot assemble expression from formula that contains illegal elements."); } + boost::any ToExpressionVisitor::visit(MultiObjectiveFormula const& f, boost::any const& data) const { + STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Cannot assemble expression from formula that contains illegal elements."); + } + boost::any ToExpressionVisitor::visit(NextFormula const& f, boost::any const& data) const { STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Cannot assemble expression from formula that contains illegal elements."); } @@ -94,6 +98,10 @@ namespace storm { STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Cannot assemble expression from formula that contains illegal elements."); } + boost::any ToExpressionVisitor::visit(TotalRewardFormula const& f, boost::any const& data) const { + STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Cannot assemble expression from formula that contains illegal elements."); + } + boost::any ToExpressionVisitor::visit(UnaryBooleanStateFormula const& f, boost::any const& data) const { storm::expressions::Expression subexpression = boost::any_cast(f.getSubformula().accept(*this, data)); switch (f.getOperator()) { diff --git a/src/logic/ToExpressionVisitor.h b/src/logic/ToExpressionVisitor.h index ee4a81d8f..ee816b6a6 100644 --- a/src/logic/ToExpressionVisitor.h +++ b/src/logic/ToExpressionVisitor.h @@ -25,9 +25,11 @@ namespace storm { virtual boost::any visit(InstantaneousRewardFormula const& f, boost::any const& data) const override; virtual boost::any visit(LongRunAverageOperatorFormula const& f, boost::any const& data) const override; virtual boost::any visit(LongRunAverageRewardFormula const& f, boost::any const& data) const override; + virtual boost::any visit(MultiObjectiveFormula const& f, boost::any const& data) const override; virtual boost::any visit(NextFormula const& f, boost::any const& data) const override; virtual boost::any visit(ProbabilityOperatorFormula const& f, boost::any const& data) const override; virtual boost::any visit(RewardOperatorFormula const& f, boost::any const& data) const override; + virtual boost::any visit(TotalRewardFormula const& f, boost::any const& data) const override; virtual boost::any visit(UnaryBooleanStateFormula const& f, boost::any const& data) const override; virtual boost::any visit(UntilFormula const& f, boost::any const& data) const override; }; @@ -36,4 +38,4 @@ namespace storm { } -#endif /* STORM_LOGIC_TOEXPRESSIONVISITOR_H_ */ \ No newline at end of file +#endif /* STORM_LOGIC_TOEXPRESSIONVISITOR_H_ */ diff --git a/src/logic/TotalRewardFormula.cpp b/src/logic/TotalRewardFormula.cpp new file mode 100644 index 000000000..10f669f64 --- /dev/null +++ b/src/logic/TotalRewardFormula.cpp @@ -0,0 +1,28 @@ +#include "src/logic/TotalRewardFormula.h" + +#include "src/logic/FormulaVisitor.h" + +namespace storm { + namespace logic { + TotalRewardFormula::TotalRewardFormula() { + // Intentionally left empty. + } + + bool TotalRewardFormula::isTotalRewardFormula() const { + return true; + } + + bool TotalRewardFormula::isRewardPathFormula() const { + return true; + } + + boost::any TotalRewardFormula::accept(FormulaVisitor const& visitor, boost::any const& data) const { + return visitor.visit(*this, data); + } + + std::ostream& TotalRewardFormula::writeToStream(std::ostream& out) const { + out << "C"; + return out; + } + } +} diff --git a/src/logic/TotalRewardFormula.h b/src/logic/TotalRewardFormula.h new file mode 100644 index 000000000..cc5f6b1a2 --- /dev/null +++ b/src/logic/TotalRewardFormula.h @@ -0,0 +1,29 @@ +#ifndef STORM_LOGIC_TOTALREWARDFORMULA_H_ +#define STORM_LOGIC_TOTALREWARDFORMULA_H_ + +#include + +#include "src/logic/PathFormula.h" + +namespace storm { + namespace logic { + class TotalRewardFormula : public PathFormula { + public: + TotalRewardFormula(); + + virtual ~TotalRewardFormula() { + // Intentionally left empty. + } + + virtual bool isTotalRewardFormula() const override; + virtual bool isRewardPathFormula() const override; + + virtual boost::any accept(FormulaVisitor const& visitor, boost::any const& data) const override; + + virtual std::ostream& writeToStream(std::ostream& out) const override; + + }; + } +} + +#endif /* STORM_LOGIC_TOTALREWARDFORMULA_H_ */ diff --git a/src/modelchecker/AbstractModelChecker.cpp b/src/modelchecker/AbstractModelChecker.cpp index 660906acb..122bb37b5 100644 --- a/src/modelchecker/AbstractModelChecker.cpp +++ b/src/modelchecker/AbstractModelChecker.cpp @@ -30,6 +30,8 @@ namespace storm { STORM_LOG_THROW(this->canHandle(checkTask), storm::exceptions::InvalidArgumentException, "The model checker is not able to check the formula '" << formula << "'."); if (formula.isStateFormula()) { return this->checkStateFormula(checkTask.substituteFormula(formula.asStateFormula())); + } else if (formula.isMultiObjectiveFormula()){ + return this->checkMultiObjectiveFormula(checkTask.substituteFormula(formula.asMultiObjectiveFormula())); } STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "The given formula '" << formula << "' is invalid."); } @@ -281,6 +283,11 @@ namespace storm { return subResult; } + template + std::unique_ptr AbstractModelChecker::checkMultiObjectiveFormula(CheckTask const& checkTask) { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker does not support the formula: " << checkTask.getFormula() << "."); + } + /////////////////////////////////////////////// // Explicitly instantiate the template class. /////////////////////////////////////////////// diff --git a/src/modelchecker/AbstractModelChecker.h b/src/modelchecker/AbstractModelChecker.h index d65e9aad7..0a6633ec9 100644 --- a/src/modelchecker/AbstractModelChecker.h +++ b/src/modelchecker/AbstractModelChecker.h @@ -72,8 +72,12 @@ namespace storm { virtual std::unique_ptr checkTimeOperatorFormula(CheckTask const& checkTask); virtual std::unique_ptr checkLongRunAverageOperatorFormula(CheckTask const& checkTask); virtual std::unique_ptr checkUnaryBooleanStateFormula(CheckTask const& checkTask); + + // The methods to check multi-objective formulas. + virtual std::unique_ptr checkMultiObjectiveFormula(CheckTask const& checkTask); + }; } } -#endif /* STORM_MODELCHECKER_ABSTRACTMODELCHECKER_H_ */ \ No newline at end of file +#endif /* STORM_MODELCHECKER_ABSTRACTMODELCHECKER_H_ */ diff --git a/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp b/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp index b12fb11ce..5fca3f319 100644 --- a/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp +++ b/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp @@ -2,6 +2,8 @@ #include "src/modelchecker/csl/helper/SparseMarkovAutomatonCslHelper.h" +#include "src/modelchecker/multiobjective/pcaa.h" + #include "src/models/sparse/StandardRewardModel.h" #include "src/utility/macros.h" @@ -32,9 +34,15 @@ namespace storm { template bool SparseMarkovAutomatonCslModelChecker::canHandle(CheckTask const& checkTask) const { storm::logic::Formula const& formula = checkTask.getFormula(); - storm::logic::FragmentSpecification fragment = storm::logic::csl().setGloballyFormulasAllowed(false).setNextFormulasAllowed(false).setReachabilityRewardFormulasAllowed(true); - fragment.setTimeAllowed(true).setLongRunAverageProbabilitiesAllowed(true); - return formula.isInFragment(fragment); + if(formula.isInFragment(storm::logic::csl().setGloballyFormulasAllowed(false).setNextFormulasAllowed(false).setReachabilityRewardFormulasAllowed(true).setTimeAllowed(true).setLongRunAverageProbabilitiesAllowed(true))) { + return true; + } else { + // Check whether we consider a multi-objective formula + // For multi-objective model checking, each initial state requires an individual scheduler (in contrast to single objective model checking). Let's exclude multiple initial states. + if(this->getModel().getInitialStates().getNumberOfSetBits() > 1) return false; + if(!checkTask.isOnlyInitialStatesRelevantSet()) return false; + return formula.isInFragment(storm::logic::multiObjective().setTimeAllowed(true).setTimeBoundedUntilFormulasAllowed(true)); + } } template @@ -107,7 +115,12 @@ namespace storm { std::vector result = storm::modelchecker::helper::SparseMarkovAutomatonCslHelper::computeTimes(checkTask.getOptimizationDirection(), this->getModel().getTransitionMatrix(), this->getModel().getBackwardTransitions(), this->getModel().getExitRates(), this->getModel().getMarkovianStates(), subResult.getTruthValuesVector(), checkTask.isQualitativeSet(), *minMaxLinearEquationSolverFactory); return std::unique_ptr(new ExplicitQuantitativeCheckResult(std::move(result))); } - + + template + std::unique_ptr SparseMarkovAutomatonCslModelChecker::checkMultiObjectiveFormula(CheckTask const& checkTask) { + return multiobjective::performPcaa(this->getModel(), checkTask.getFormula()); + } + template class SparseMarkovAutomatonCslModelChecker>; } } diff --git a/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h b/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h index 5fbfb85e4..a4b911b33 100644 --- a/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h +++ b/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h @@ -26,6 +26,7 @@ namespace storm { virtual std::unique_ptr computeReachabilityRewards(storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; virtual std::unique_ptr computeLongRunAverageProbabilities(CheckTask const& checkTask) override; virtual std::unique_ptr computeReachabilityTimes(storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; + virtual std::unique_ptr checkMultiObjectiveFormula(CheckTask const& checkTask) override; private: // An object that is used for retrieving solvers for systems of linear equations that are the result of nondeterministic choices. diff --git a/src/modelchecker/csl/helper/SparseCtmcCslHelper.cpp b/src/modelchecker/csl/helper/SparseCtmcCslHelper.cpp index c51563507..df8e06c4a 100644 --- a/src/modelchecker/csl/helper/SparseCtmcCslHelper.cpp +++ b/src/modelchecker/csl/helper/SparseCtmcCslHelper.cpp @@ -629,7 +629,6 @@ namespace storm { } std::unique_ptr> solver = linearEquationSolverFactory.create(std::move(uniformizedMatrix)); - solver->allocateAuxMemory(storm::solver::LinearEquationSolverOperation::MultiplyRepeatedly); if (!useMixedPoissonProbabilities && std::get<0>(foxGlynnResult) > 1) { // Perform the matrix-vector multiplications (without adding). diff --git a/src/modelchecker/csl/helper/SparseMarkovAutomatonCslHelper.cpp b/src/modelchecker/csl/helper/SparseMarkovAutomatonCslHelper.cpp index 90f858756..6b6ff0fa1 100644 --- a/src/modelchecker/csl/helper/SparseMarkovAutomatonCslHelper.cpp +++ b/src/modelchecker/csl/helper/SparseMarkovAutomatonCslHelper.cpp @@ -86,7 +86,6 @@ namespace storm { } std::unique_ptr> solver = minMaxLinearEquationSolverFactory.create(aProbabilistic); - solver->allocateAuxMemory(storm::solver::MinMaxLinearEquationSolverOperation::SolveEquations); // Perform the actual value iteration // * loop until the step bound has been reached diff --git a/src/modelchecker/multiobjective/Pcaa.cpp b/src/modelchecker/multiobjective/Pcaa.cpp new file mode 100644 index 000000000..5d6eacf22 --- /dev/null +++ b/src/modelchecker/multiobjective/Pcaa.cpp @@ -0,0 +1,73 @@ +#include "src/modelchecker/multiobjective/pcaa.h" + +#include "src/utility/macros.h" + +#include "src/models/sparse/Mdp.h" +#include "src/models/sparse/MarkovAutomaton.h" +#include "src/models/sparse/StandardRewardModel.h" +#include "src/modelchecker/multiobjective/pcaa/SparsePcaaPreprocessor.h" +#include "src/modelchecker/multiobjective/pcaa/SparsePcaaAchievabilityQuery.h" +#include "src/modelchecker/multiobjective/pcaa/SparsePcaaQuantitativeQuery.h" +#include "src/modelchecker/multiobjective/pcaa/SparsePcaaParetoQuery.h" +#include "src/settings//SettingsManager.h" +#include "src/settings/modules/MultiObjectiveSettings.h" + +#include "src/exceptions/InvalidArgumentException.h" + + +namespace storm { + namespace modelchecker { + namespace multiobjective { + + template + std::unique_ptr performPcaa(SparseModelType const& model, storm::logic::MultiObjectiveFormula const& formula) { + STORM_LOG_ASSERT(model.getInitialStates().getNumberOfSetBits() == 1, "Multi-objective Model checking on model with multiple initial states is not supported."); + +#ifdef STORM_HAVE_CARL + + // If we consider an MA, ensure that it is closed + if(model.isOfType(storm::models::ModelType::MarkovAutomaton)) { + STORM_LOG_THROW(dynamic_cast const *>(&model)->isClosed(), storm::exceptions::InvalidArgumentException, "Unable to check multi-objective formula on non-closed Markov automaton."); + } + + auto preprocessorResult = SparsePcaaPreprocessor::preprocess(model, formula); + STORM_LOG_DEBUG("Preprocessing done. Result: " << preprocessorResult); + + std::unique_ptr> query; + switch (preprocessorResult.queryType) { + case SparsePcaaPreprocessorReturnType::QueryType::Achievability: + query = std::unique_ptr> (new SparsePcaaAchievabilityQuery(preprocessorResult)); + break; + case SparsePcaaPreprocessorReturnType::QueryType::Quantitative: + query = std::unique_ptr> (new SparsePcaaQuantitativeQuery(preprocessorResult)); + break; + case SparsePcaaPreprocessorReturnType::QueryType::Pareto: + query = std::unique_ptr> (new SparsePcaaParetoQuery(preprocessorResult)); + break; + default: + STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Unsupported multi-objective Query Type."); + break; + } + + auto result = query->check(); + + if(settings::getModule().isExportPlotSet()) { + query->exportPlotOfCurrentApproximation(storm::settings::getModule().getExportPlotDirectory()); + } + return result; +#else + STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Multi-objective model checking requires carl."); + return nullptr; +#endif + } + + template std::unique_ptr performPcaa>(storm::models::sparse::Mdp const& model, storm::logic::MultiObjectiveFormula const& formula); + template std::unique_ptr performPcaa>(storm::models::sparse::MarkovAutomaton const& model, storm::logic::MultiObjectiveFormula const& formula); +#ifdef STORM_HAVE_CARL + template std::unique_ptr performPcaa>(storm::models::sparse::Mdp const& model, storm::logic::MultiObjectiveFormula const& formula); + // template std::unique_ptr performPcaa>(storm::models::sparse::MarkovAutomaton const& model, storm::logic::MultiObjectiveFormula const& formula); +#endif + + } + } +} diff --git a/src/modelchecker/multiobjective/Pcaa.h b/src/modelchecker/multiobjective/Pcaa.h new file mode 100644 index 000000000..c1e4a4566 --- /dev/null +++ b/src/modelchecker/multiobjective/Pcaa.h @@ -0,0 +1,20 @@ +#ifndef STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_H_ +#define STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_H_ + +#include + +#include "src/modelchecker/results/CheckResult.h" +#include "src/logic/Formulas.h" + +namespace storm { + namespace modelchecker { + namespace multiobjective { + + template + std::unique_ptr performPcaa(SparseModelType const& model, storm::logic::MultiObjectiveFormula const& formula); + + } + } +} + +#endif /* STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_H_ */ diff --git a/src/modelchecker/multiobjective/pcaa/PCAAObjective.h b/src/modelchecker/multiobjective/pcaa/PCAAObjective.h new file mode 100644 index 000000000..5ff73230c --- /dev/null +++ b/src/modelchecker/multiobjective/pcaa/PCAAObjective.h @@ -0,0 +1,70 @@ +#ifndef STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_PCAAOBJECTIVE_H_ +#define STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_PCAAOBJECTIVE_H_ + +#include +#include + +#include "src/logic/Formulas.h" + +namespace storm { + namespace modelchecker { + namespace multiobjective { + template + struct PcaaObjective { + // the original input formula + std::shared_ptr originalFormula; + + // the name of the considered reward model in the preprocessedModel + std::string rewardModelName; + + // true if all rewards for this objective are positive, false if all rewards are negative. + bool rewardsArePositive; + + // transformation from the values of the preprocessed model to the ones for the actual input model, i.e., + // x is achievable in the preprocessed model iff factor*x + offset is achievable in the original model + ValueType toOriginalValueTransformationFactor; + ValueType toOriginalValueTransformationOffset; + + // The probability/reward threshold for the preprocessed model (if originalFormula specifies one). + // This is always a lower bound. + boost::optional threshold; + // True iff the specified threshold is strict, i.e., > + bool thresholdIsStrict = false; + + // The time bound(s) for the formula (if given by the originalFormula) + boost::optional lowerTimeBound; + boost::optional upperTimeBound; + + void printToStream(std::ostream& out) const { + out << std::setw(30) << originalFormula->toString(); + out << " \t(toOrigVal:" << std::setw(3) << toOriginalValueTransformationFactor << "*x +" << std::setw(3) << toOriginalValueTransformationOffset << ", \t"; + out << "intern threshold:"; + if(threshold){ + out << (thresholdIsStrict ? " >" : ">="); + out << std::setw(5) << (*threshold) << ","; + } else { + out << " none,"; + } + out << " \t"; + out << "intern reward model: " << std::setw(10) << rewardModelName; + out << (rewardsArePositive ? " (positive)" : " (negative)") << ", \t"; + out << "time bounds:"; + if(lowerTimeBound) { + if(upperTimeBound) { + out << "[" << *lowerTimeBound << ", " << *upperTimeBound << "]"; + } else { + out << ">=" << std::setw(5) << *lowerTimeBound; + } + } else if (upperTimeBound) { + out << "<=" << std::setw(5) << *upperTimeBound; + } else { + out << " none"; + } + out << ")" << std::endl; + } + }; + } + } +} + +#endif /* STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_OBJECTIVE_H_ */ diff --git a/src/modelchecker/multiobjective/pcaa/SparseMaPcaaWeightVectorChecker.cpp b/src/modelchecker/multiobjective/pcaa/SparseMaPcaaWeightVectorChecker.cpp new file mode 100644 index 000000000..2e4e13439 --- /dev/null +++ b/src/modelchecker/multiobjective/pcaa/SparseMaPcaaWeightVectorChecker.cpp @@ -0,0 +1,422 @@ +#include "src/modelchecker/multiobjective/pcaa/SparseMaPcaaWeightVectorChecker.h" + +#include + +#include "src/adapters/CarlAdapter.h" +#include "src/models/sparse/MarkovAutomaton.h" +#include "src/models/sparse/StandardRewardModel.h" +#include "src/transformer/EndComponentEliminator.h" +#include "src/utility/macros.h" +#include "src/utility/vector.h" + +#include "src/exceptions/InvalidOperationException.h" + +namespace storm { + namespace modelchecker { + namespace multiobjective { + + template + SparseMaPcaaWeightVectorChecker::SparseMaPcaaWeightVectorChecker(SparseMaModelType const& model, + std::vector> const& objectives, + storm::storage::BitVector const& actionsWithNegativeReward, + storm::storage::BitVector const& ecActions, + storm::storage::BitVector const& possiblyRecurrentStates) : + SparsePcaaWeightVectorChecker(model, objectives, actionsWithNegativeReward, ecActions, possiblyRecurrentStates) { + // Set the (discretized) state action rewards. + this->discreteActionRewards.resize(objectives.size()); + for(auto objIndex : this->objectivesWithNoUpperTimeBound) { + typename SparseMaModelType::RewardModelType const& rewModel = this->model.getRewardModel(this->objectives[objIndex].rewardModelName); + STORM_LOG_ASSERT(!rewModel.hasTransitionRewards(), "Preprocessed Reward model has transition rewards which is not expected."); + this->discreteActionRewards[objIndex] = rewModel.hasStateActionRewards() ? rewModel.getStateActionRewardVector() : std::vector(this->model.getTransitionMatrix().getRowCount(), storm::utility::zero()); + if(rewModel.hasStateRewards()) { + // Note that state rewards are earned over time and thus play no role for probabilistic states + for(auto markovianState : this->model.getMarkovianStates()) { + this->discreteActionRewards[objIndex][this->model.getTransitionMatrix().getRowGroupIndices()[markovianState]] += rewModel.getStateReward(markovianState) / this->model.getExitRate(markovianState); + } + } + } + } + + template + void SparseMaPcaaWeightVectorChecker::boundedPhase(std::vector const& weightVector, std::vector& weightedRewardVector) { + + // Split the preprocessed model into transitions from/to probabilistic/Markovian states. + SubModel MS = createSubModel(true, weightedRewardVector); + SubModel PS = createSubModel(false, weightedRewardVector); + + // Apply digitization to Markovian transitions + ValueType digitizationConstant = getDigitizationConstant(weightVector); + digitize(MS, digitizationConstant); + + // Get for each occurring (digitized) timeBound the indices of the objectives with that bound. + TimeBoundMap lowerTimeBounds; + TimeBoundMap upperTimeBounds; + digitizeTimeBounds(lowerTimeBounds, upperTimeBounds, digitizationConstant); + + // Initialize a minMaxSolver to compute an optimal scheduler (w.r.t. PS) for each epoch + // No EC elimination is necessary as we assume non-zenoness + std::unique_ptr minMax = initMinMaxSolver(PS); + + // create a linear equation solver for the model induced by the optimal choice vector. + // the solver will be updated whenever the optimal choice vector has changed. + std::unique_ptr linEq = initLinEqSolver(PS); + + // Store the optimal choices of PS as computed by the minMax solver. + std::vector optimalChoicesAtCurrentEpoch(PS.getNumberOfStates(), std::numeric_limits::max()); + + // Stores the objectives for which we need to compute values in the current time epoch. + storm::storage::BitVector consideredObjectives = this->objectivesWithNoUpperTimeBound; + + auto lowerTimeBoundIt = lowerTimeBounds.begin(); + auto upperTimeBoundIt = upperTimeBounds.begin(); + uint_fast64_t currentEpoch = std::max(lowerTimeBounds.empty() ? 0 : lowerTimeBoundIt->first, upperTimeBounds.empty() ? 0 : upperTimeBoundIt->first); + while(true) { + // Update the objectives that are considered at the current time epoch as well as the (weighted) reward vectors. + updateDataToCurrentEpoch(MS, PS, *minMax, consideredObjectives, currentEpoch, weightVector, lowerTimeBoundIt, lowerTimeBounds, upperTimeBoundIt, upperTimeBounds); + + // Compute the values that can be obtained at probabilistic states in the current time epoch + performPSStep(PS, MS, *minMax, *linEq, optimalChoicesAtCurrentEpoch, consideredObjectives); + + // Compute values that can be obtained at Markovian states after letting one (digitized) time unit pass. + // Only perform such a step if there is time left. + if(currentEpoch>0) { + performMSStep(MS, PS, consideredObjectives); + --currentEpoch; + } else { + break; + } + } + + // compose the results from MS and PS + storm::utility::vector::setVectorValues(this->weightedResult, MS.states, MS.weightedSolutionVector); + storm::utility::vector::setVectorValues(this->weightedResult, PS.states, PS.weightedSolutionVector); + for(uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) { + storm::utility::vector::setVectorValues(this->objectiveResults[objIndex], MS.states, MS.objectiveSolutionVectors[objIndex]); + storm::utility::vector::setVectorValues(this->objectiveResults[objIndex], PS.states, PS.objectiveSolutionVectors[objIndex]); + } + } + + + template + typename SparseMaPcaaWeightVectorChecker::SubModel SparseMaPcaaWeightVectorChecker::createSubModel(bool createMS, std::vector const& weightedRewardVector) const { + SubModel result; + + storm::storage::BitVector probabilisticStates = ~this->model.getMarkovianStates(); + result.states = createMS ? this->model.getMarkovianStates() : probabilisticStates; + result.choices = this->model.getTransitionMatrix().getRowIndicesOfRowGroups(result.states); + STORM_LOG_ASSERT(!createMS || result.states.getNumberOfSetBits() == result.choices.getNumberOfSetBits(), "row groups for Markovian states should consist of exactly one row"); + + //We need to add diagonal entries for selfloops on Markovian states. + result.toMS = this->model.getTransitionMatrix().getSubmatrix(true, result.states, this->model.getMarkovianStates(), createMS); + result.toPS = this->model.getTransitionMatrix().getSubmatrix(true, result.states, probabilisticStates, false); + STORM_LOG_ASSERT(result.getNumberOfStates() == result.states.getNumberOfSetBits() && result.getNumberOfStates() == result.toMS.getRowGroupCount() && result.getNumberOfStates() == result.toPS.getRowGroupCount(), "Invalid state count for subsystem"); + STORM_LOG_ASSERT(result.getNumberOfChoices() == result.choices.getNumberOfSetBits() && result.getNumberOfChoices() == result.toMS.getRowCount() && result.getNumberOfChoices() == result.toPS.getRowCount(), "Invalid state count for subsystem"); + + result.weightedRewardVector.resize(result.getNumberOfChoices()); + storm::utility::vector::selectVectorValues(result.weightedRewardVector, result.choices, weightedRewardVector); + result.objectiveRewardVectors.resize(this->objectives.size()); + for(uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) { + std::vector& objVector = result.objectiveRewardVectors[objIndex]; + objVector = std::vector(result.weightedRewardVector.size(), storm::utility::zero()); + if(this->objectivesWithNoUpperTimeBound.get(objIndex)) { + storm::utility::vector::selectVectorValues(objVector, result.choices, this->discreteActionRewards[objIndex]); + } else { + typename SparseMaModelType::RewardModelType const& rewModel = this->model.getRewardModel(this->objectives[objIndex].rewardModelName); + STORM_LOG_ASSERT(!rewModel.hasTransitionRewards(), "Preprocessed Reward model has transition rewards which is not expected."); + STORM_LOG_ASSERT(!rewModel.hasStateRewards(), "State rewards for bounded objectives for MAs are not expected (bounded rewards are not supported)."); + if(rewModel.hasStateActionRewards()) { + storm::utility::vector::selectVectorValues(objVector, result.choices, rewModel.getStateActionRewardVector()); + } + } + } + + result.weightedSolutionVector.resize(result.getNumberOfStates()); + storm::utility::vector::selectVectorValues(result.weightedSolutionVector, result.states, this->weightedResult); + result.objectiveSolutionVectors.resize(this->objectives.size()); + for(uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) { + result.objectiveSolutionVectors[objIndex].resize(result.weightedSolutionVector.size()); + storm::utility::vector::selectVectorValues(result.objectiveSolutionVectors[objIndex], result.states, this->objectiveResults[objIndex]); + } + + result.auxChoiceValues.resize(result.getNumberOfChoices()); + + return result; + } + + template + template ::SupportsExponential, int>::type> + VT SparseMaPcaaWeightVectorChecker::getDigitizationConstant(std::vector const& weightVector) const { + STORM_LOG_DEBUG("Retrieving digitization constant"); + // We need to find a delta such that for each objective it holds that lowerbound/delta , upperbound/delta are natural numbers and + // sum_{obj_i} ( + // If obj_i has a lower and an upper bound: + // weightVector_i * (1 - e^(-maxRate lowerbound) * (1 + maxRate delta) ^ (lowerbound / delta) + 1-e^(-maxRate upperbound) * (1 + maxRate delta) ^ (upperbound / delta) + (1-e^(-maxRate delta))) + // If there is only an upper bound: + // weightVector_i * ( 1-e^(-maxRate upperbound) * (1 + maxRate delta) ^ (upperbound / delta)) + // ) <= this->maximumLowerUpperDistance + + // Initialize some data for fast and easy access + VT const maxRate = this->model.getMaximalExitRate(); + std::vector> eToPowerOfMinusMaxRateTimesBound; + VT smallestNonZeroBound = storm::utility::zero(); + for(auto const& obj : this->objectives) { + eToPowerOfMinusMaxRateTimesBound.emplace_back(); + if(obj.lowerTimeBound){ + STORM_LOG_ASSERT(!storm::utility::isZero(*obj.lowerTimeBound), "Got zero-valued lower bound."); // should have been handled in preprocessing + STORM_LOG_ASSERT(!obj.upperTimeBound || *obj.lowerTimeBound < *obj.upperTimeBound, "Got point intervall or empty intervall on time bounded property which is not supported"); // should also have been handled in preprocessing + eToPowerOfMinusMaxRateTimesBound.back().first = std::exp(-maxRate * (*obj.lowerTimeBound)); + smallestNonZeroBound = storm::utility::isZero(smallestNonZeroBound) ? *obj.lowerTimeBound : std::min(smallestNonZeroBound, *obj.lowerTimeBound); + } + if(obj.upperTimeBound){ + STORM_LOG_ASSERT(!storm::utility::isZero(*obj.upperTimeBound), "Got zero-valued upper bound."); // should have been handled in preprocessing + eToPowerOfMinusMaxRateTimesBound.back().second = std::exp(-maxRate * (*obj.upperTimeBound)); + smallestNonZeroBound = storm::utility::isZero(smallestNonZeroBound) ? *obj.upperTimeBound : std::min(smallestNonZeroBound, *obj.upperTimeBound); + } + } + if(storm::utility::isZero(smallestNonZeroBound)) { + // There are no time bounds. In this case, one is a valid digitization constant. + return storm::utility::one(); + } + VT goalPrecisionTimesNorm = this->weightedPrecision * storm::utility::sqrt(storm::utility::vector::dotProduct(weightVector, weightVector)); + + // We brute-force a delta, since a direct computation is apparently not easy. + // Also note that the number of times this loop runs is a lower bound for the number of minMaxSolver invocations. + // Hence, this brute-force approach will most likely not be a bottleneck. + uint_fast64_t smallestStepBound = 1; + VT delta = smallestNonZeroBound / smallestStepBound; + while(true) { + bool deltaValid = true; + for(auto const& obj : this->objectives) { + if((obj.lowerTimeBound && *obj.lowerTimeBound/delta != std::floor(*obj.lowerTimeBound/delta)) || + (obj.upperTimeBound && *obj.upperTimeBound/delta != std::floor(*obj.upperTimeBound/delta))) { + deltaValid = false; + break; + } + } + if(deltaValid) { + VT weightedPrecisionForCurrentDelta = storm::utility::zero(); + for(uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) { + auto const& obj = this->objectives[objIndex]; + VT precisionOfObj = storm::utility::zero(); + if(obj.lowerTimeBound) { + precisionOfObj += storm::utility::one() - (eToPowerOfMinusMaxRateTimesBound[objIndex].first * storm::utility::pow(storm::utility::one() + maxRate * delta, *obj.lowerTimeBound / delta) ) + + storm::utility::one() - std::exp(-maxRate * delta); + } + if(obj.upperTimeBound) { + precisionOfObj += storm::utility::one() - (eToPowerOfMinusMaxRateTimesBound[objIndex].second * storm::utility::pow(storm::utility::one() + maxRate * delta, *obj.upperTimeBound / delta) ); + } + weightedPrecisionForCurrentDelta += weightVector[objIndex] * precisionOfObj; + } + deltaValid &= weightedPrecisionForCurrentDelta <= goalPrecisionTimesNorm; + } + if(deltaValid) { + break; + } + ++smallestStepBound; + STORM_LOG_ASSERT(delta>smallestNonZeroBound / smallestStepBound, "Digitization constant is expected to become smaller in every iteration"); + delta = smallestNonZeroBound / smallestStepBound; + } + STORM_LOG_DEBUG("Found digitization constant: " << delta << ". At least " << smallestStepBound << " digitization steps will be necessarry"); + return delta; + } + + template + template ::SupportsExponential, int>::type> + VT SparseMaPcaaWeightVectorChecker::getDigitizationConstant(std::vector const& weightVector) const { + STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Computing bounded probabilities of MAs is unsupported for this value type."); + } + + template + template ::SupportsExponential, int>::type> + void SparseMaPcaaWeightVectorChecker::digitize(SubModel& MS, VT const& digitizationConstant) const { + std::vector rateVector(MS.getNumberOfChoices()); + storm::utility::vector::selectVectorValues(rateVector, MS.states, this->model.getExitRates()); + for(uint_fast64_t row = 0; row < rateVector.size(); ++row) { + VT const eToMinusRateTimesDelta = std::exp(-rateVector[row] * digitizationConstant); + for(auto& entry : MS.toMS.getRow(row)) { + entry.setValue((storm::utility::one() - eToMinusRateTimesDelta) * entry.getValue()); + if(entry.getColumn() == row) { + entry.setValue(entry.getValue() + eToMinusRateTimesDelta); + } + } + for(auto& entry : MS.toPS.getRow(row)) { + entry.setValue((storm::utility::one() - eToMinusRateTimesDelta) * entry.getValue()); + } + MS.weightedRewardVector[row] *= storm::utility::one() - eToMinusRateTimesDelta; + for(auto& objVector : MS.objectiveRewardVectors) { + objVector[row] *= storm::utility::one() - eToMinusRateTimesDelta; + } + } + } + + template + template ::SupportsExponential, int>::type> + void SparseMaPcaaWeightVectorChecker::digitize(SubModel& subModel, VT const& digitizationConstant) const { + STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Computing bounded probabilities of MAs is unsupported for this value type."); + } + + template + template ::SupportsExponential, int>::type> + void SparseMaPcaaWeightVectorChecker::digitizeTimeBounds(TimeBoundMap& lowerTimeBounds, TimeBoundMap& upperTimeBounds, VT const& digitizationConstant) { + + VT const maxRate = this->model.getMaximalExitRate(); + for(uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) { + auto const& obj = this->objectives[objIndex]; + VT errorTowardsZero; + VT errorAwayFromZero; + if(obj.lowerTimeBound) { + uint_fast64_t digitizedBound = storm::utility::convertNumber((*obj.lowerTimeBound)/digitizationConstant); + auto timeBoundIt = lowerTimeBounds.insert(std::make_pair(digitizedBound, storm::storage::BitVector(this->objectives.size(), false))).first; + timeBoundIt->second.set(objIndex); + VT digitizationError = storm::utility::one(); + digitizationError -= std::exp(-maxRate * (*obj.lowerTimeBound)) * storm::utility::pow(storm::utility::one() + maxRate * digitizationConstant, digitizedBound); + errorTowardsZero = -digitizationError; + errorAwayFromZero = storm::utility::one() - std::exp(-maxRate * digitizationConstant);; + } else { + errorTowardsZero = storm::utility::zero(); + errorAwayFromZero = storm::utility::zero(); + } + if(obj.upperTimeBound) { + uint_fast64_t digitizedBound = storm::utility::convertNumber((*obj.upperTimeBound)/digitizationConstant); + auto timeBoundIt = upperTimeBounds.insert(std::make_pair(digitizedBound, storm::storage::BitVector(this->objectives.size(), false))).first; + timeBoundIt->second.set(objIndex); + VT digitizationError = storm::utility::one(); + digitizationError -= std::exp(-maxRate * (*obj.upperTimeBound)) * storm::utility::pow(storm::utility::one() + maxRate * digitizationConstant, digitizedBound); + errorAwayFromZero += digitizationError; + } + if (obj.rewardsArePositive) { + this->offsetsToLowerBound[objIndex] = -errorTowardsZero; + this->offsetsToUpperBound[objIndex] = errorAwayFromZero; + } else { + this->offsetsToLowerBound[objIndex] = -errorAwayFromZero; + this->offsetsToUpperBound[objIndex] = errorTowardsZero; + } + } + } + + template + template ::SupportsExponential, int>::type> + void SparseMaPcaaWeightVectorChecker::digitizeTimeBounds(TimeBoundMap& lowerTimeBounds, TimeBoundMap& upperTimeBounds, VT const& digitizationConstant) { + STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Computing bounded probabilities of MAs is unsupported for this value type."); + } + + template + std::unique_ptr::MinMaxSolverData> SparseMaPcaaWeightVectorChecker::initMinMaxSolver(SubModel const& PS) const { + std::unique_ptr result(new MinMaxSolverData()); + storm::solver::GeneralMinMaxLinearEquationSolverFactory minMaxSolverFactory; + result->solver = minMaxSolverFactory.create(PS.toPS); + result->solver->setOptimizationDirection(storm::solver::OptimizationDirection::Maximize); + result->solver->setTrackScheduler(true); + + result->b.resize(PS.getNumberOfChoices()); + + return result; + } + + template + std::unique_ptr::LinEqSolverData> SparseMaPcaaWeightVectorChecker::initLinEqSolver(SubModel const& PS) const { + std::unique_ptr result(new LinEqSolverData()); + // We choose Jacobi since we call the solver very frequently on 'easy' inputs (note that jacobi without preconditioning has very little overhead). + result->factory.getSettings().setSolutionMethod(storm::solver::GmmxxLinearEquationSolverSettings::SolutionMethod::Jacobi); + result->factory.getSettings().setPreconditioner(storm::solver::GmmxxLinearEquationSolverSettings::Preconditioner::None); + result->b.resize(PS.getNumberOfStates()); + return result; + } + + template + void SparseMaPcaaWeightVectorChecker::updateDataToCurrentEpoch(SubModel& MS, SubModel& PS, MinMaxSolverData& minMax, storm::storage::BitVector& consideredObjectives, uint_fast64_t const& currentEpoch, std::vector const& weightVector, TimeBoundMap::iterator& lowerTimeBoundIt, TimeBoundMap const& lowerTimeBounds, TimeBoundMap::iterator& upperTimeBoundIt, TimeBoundMap const& upperTimeBounds) { + + //Note that lower time bounds are always strict. Hence, we need to react when the current epoch equals the stored bound. + if(lowerTimeBoundIt != lowerTimeBounds.end() && currentEpoch == lowerTimeBoundIt->first) { + for(auto objIndex : lowerTimeBoundIt->second) { + // No more reward is earned for this objective. + storm::utility::vector::addScaledVector(MS.weightedRewardVector, MS.objectiveRewardVectors[objIndex], -weightVector[objIndex]); + storm::utility::vector::addScaledVector(PS.weightedRewardVector, PS.objectiveRewardVectors[objIndex], -weightVector[objIndex]); + MS.objectiveRewardVectors[objIndex] = std::vector(MS.objectiveRewardVectors[objIndex].size(), storm::utility::zero()); + PS.objectiveRewardVectors[objIndex] = std::vector(PS.objectiveRewardVectors[objIndex].size(), storm::utility::zero()); + } + ++lowerTimeBoundIt; + } + + if(upperTimeBoundIt != upperTimeBounds.end() && currentEpoch == upperTimeBoundIt->first) { + consideredObjectives |= upperTimeBoundIt->second; + for(auto objIndex : upperTimeBoundIt->second) { + // This objective now plays a role in the weighted sum + storm::utility::vector::addScaledVector(MS.weightedRewardVector, MS.objectiveRewardVectors[objIndex], weightVector[objIndex]); + storm::utility::vector::addScaledVector(PS.weightedRewardVector, PS.objectiveRewardVectors[objIndex], weightVector[objIndex]); + } + ++upperTimeBoundIt; + } + + // Update the solver data + PS.toMS.multiplyWithVector(MS.weightedSolutionVector, minMax.b); + storm::utility::vector::addVectors(minMax.b, PS.weightedRewardVector, minMax.b); + } + + template + void SparseMaPcaaWeightVectorChecker::performPSStep(SubModel& PS, SubModel const& MS, MinMaxSolverData& minMax, LinEqSolverData& linEq, std::vector& optimalChoicesAtCurrentEpoch, storm::storage::BitVector const& consideredObjectives) const { + // compute a choice vector for the probabilistic states that is optimal w.r.t. the weighted reward vector + minMax.solver->solveEquations(PS.weightedSolutionVector, minMax.b); + auto newScheduler = minMax.solver->getScheduler(); + // check whether the linEqSolver needs to be updated, i.e., whether the scheduler has changed + if(linEq.solver == nullptr || newScheduler->getChoices() != optimalChoicesAtCurrentEpoch) { + optimalChoicesAtCurrentEpoch = newScheduler->getChoices(); + linEq.solver = nullptr; + storm::storage::SparseMatrix linEqMatrix = PS.toPS.selectRowsFromRowGroups(optimalChoicesAtCurrentEpoch, true); + linEqMatrix.convertToEquationSystem(); + linEq.solver = linEq.factory.create(std::move(linEqMatrix)); + } + + // Get the results for the individual objectives. + // Note that we do not consider an estimate for each objective (as done in the unbounded phase) since the results from the previous epoch are already pretty close + for(auto objIndex : consideredObjectives) { + auto const& objectiveRewardVectorPS = PS.objectiveRewardVectors[objIndex]; + auto const& objectiveSolutionVectorMS = MS.objectiveSolutionVectors[objIndex]; + // compute rhs of equation system, i.e., PS.toMS * x + Rewards + // To safe some time, only do this for the obtained optimal choices + auto itGroupIndex = PS.toPS.getRowGroupIndices().begin(); + auto itChoiceOffset = optimalChoicesAtCurrentEpoch.begin(); + for(auto& bValue : linEq.b) { + uint_fast64_t row = (*itGroupIndex) + (*itChoiceOffset); + bValue = objectiveRewardVectorPS[row]; + for(auto const& entry : PS.toMS.getRow(row)){ + bValue += entry.getValue() * objectiveSolutionVectorMS[entry.getColumn()]; + } + ++itGroupIndex; + ++itChoiceOffset; + } + linEq.solver->solveEquations(PS.objectiveSolutionVectors[objIndex], linEq.b); + } + } + + template + void SparseMaPcaaWeightVectorChecker::performMSStep(SubModel& MS, SubModel const& PS, storm::storage::BitVector const& consideredObjectives) const { + + MS.toMS.multiplyWithVector(MS.weightedSolutionVector, MS.auxChoiceValues); + storm::utility::vector::addVectors(MS.weightedRewardVector, MS.auxChoiceValues, MS.weightedSolutionVector); + MS.toPS.multiplyWithVector(PS.weightedSolutionVector, MS.auxChoiceValues); + storm::utility::vector::addVectors(MS.weightedSolutionVector, MS.auxChoiceValues, MS.weightedSolutionVector); + + for(auto objIndex : consideredObjectives) { + MS.toMS.multiplyWithVector(MS.objectiveSolutionVectors[objIndex], MS.auxChoiceValues); + storm::utility::vector::addVectors(MS.objectiveRewardVectors[objIndex], MS.auxChoiceValues, MS.objectiveSolutionVectors[objIndex]); + MS.toPS.multiplyWithVector(PS.objectiveSolutionVectors[objIndex], MS.auxChoiceValues); + storm::utility::vector::addVectors(MS.objectiveSolutionVectors[objIndex], MS.auxChoiceValues, MS.objectiveSolutionVectors[objIndex]); + } + } + + + template class SparseMaPcaaWeightVectorChecker>; + template double SparseMaPcaaWeightVectorChecker>::getDigitizationConstant(std::vector const& direction) const; + template void SparseMaPcaaWeightVectorChecker>::digitize(SparseMaPcaaWeightVectorChecker>::SubModel& subModel, double const& digitizationConstant) const; + template void SparseMaPcaaWeightVectorChecker>::digitizeTimeBounds(SparseMaPcaaWeightVectorChecker>::TimeBoundMap& lowerTimeBounds, SparseMaPcaaWeightVectorChecker>::TimeBoundMap& upperTimeBounds, double const& digitizationConstant); +#ifdef STORM_HAVE_CARL +// template class SparseMaPcaaWeightVectorChecker>; + // template storm::RationalNumber SparseMaPcaaWeightVectorChecker>::getDigitizationConstant(std::vector const& direction) const; + // template void SparseMaPcaaWeightVectorChecker>::digitize(SparseMaPcaaWeightVectorChecker>::SubModel& subModel, storm::RationalNumber const& digitizationConstant) const; +// template void SparseMaPcaaWeightVectorChecker>::digitizeTimeBounds(SparseMaPcaaWeightVectorChecker>::TimeBoundMap& lowerTimeBounds, SparseMaPcaaWeightVectorChecker>::TimeBoundMap& upperTimeBounds, storm::RationalNumber const& digitizationConstant); +#endif + + } + } +} diff --git a/src/modelchecker/multiobjective/pcaa/SparseMaPcaaWeightVectorChecker.h b/src/modelchecker/multiobjective/pcaa/SparseMaPcaaWeightVectorChecker.h new file mode 100644 index 000000000..0540b39cc --- /dev/null +++ b/src/modelchecker/multiobjective/pcaa/SparseMaPcaaWeightVectorChecker.h @@ -0,0 +1,157 @@ +#ifndef STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEMAPCAAWEIGHTVECTORCHECKER_H_ +#define STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEMAPCAAWEIGHTVECTORCHECKER_H_ + +#include +#include + +#include "src/modelchecker/multiobjective/pcaa/SparsePcaaWeightVectorChecker.h" +#include "src/solver/LinearEquationSolver.h" +#include "src/solver/GmmxxLinearEquationSolver.h" +#include "src/solver/MinMaxLinearEquationSolver.h" +#include "src/utility/NumberTraits.h" + +namespace storm { + namespace modelchecker { + namespace multiobjective { + + /*! + * Helper Class that takes preprocessed Pcaa data and a weight vector and ... + * - computes the maximal expected reward w.r.t. the weighted sum of the rewards of the individual objectives + * - extracts the scheduler that induces this maximum + * - computes for each objective the value induced by this scheduler + */ + template + class SparseMaPcaaWeightVectorChecker : public SparsePcaaWeightVectorChecker { + public: + typedef typename SparseMaModelType::ValueType ValueType; + + SparseMaPcaaWeightVectorChecker(SparseMaModelType const& model, + std::vector> const& objectives, + storm::storage::BitVector const& actionsWithNegativeReward, + storm::storage::BitVector const& ecActions, + storm::storage::BitVector const& possiblyRecurrentStates); + + private: + + /* + * Stores (digitized) time bounds in descending order + */ + typedef std::map> TimeBoundMap; + + /* + * Stores the ingredients of a sub model + */ + struct SubModel { + storm::storage::BitVector states; // The states that are part of this sub model + storm::storage::BitVector choices; // The choices that are part of this sub model + + storm::storage::SparseMatrix toMS; // Transitions to Markovian states + storm::storage::SparseMatrix toPS; // Transitions to probabilistic states + + std::vector weightedRewardVector; + std::vector> objectiveRewardVectors; + + std::vector weightedSolutionVector; + std::vector> objectiveSolutionVectors; + + std::vector auxChoiceValues; //stores auxiliary values for every choice + + uint_fast64_t getNumberOfStates() const { return toMS.getRowGroupCount(); }; + uint_fast64_t getNumberOfChoices() const { return toMS.getRowCount(); }; + }; + + /* + * Stores the data that is relevant to invoke the minMaxSolver and retrieve the result. + */ + struct MinMaxSolverData { + std::unique_ptr> solver; + std::vector b; + }; + + struct LinEqSolverData { + storm::solver::GmmxxLinearEquationSolverFactory factory; + std::unique_ptr> solver; + std::vector b; + }; + + /*! + * + * @param weightVector the weight vector of the current check + * @param weightedRewardVector the weighted rewards considering the unbounded objectives. Will be invalidated after calling this. + */ + virtual void boundedPhase(std::vector const& weightVector, std::vector& weightedRewardVector) override; + + /*! + * Retrieves the data for a submodel of the data->preprocessedModel + * @param createMS if true, the submodel containing the Markovian states is created. + * if false, the submodel containing the probabilistic states is created. + */ + SubModel createSubModel(bool createMS, std::vector const& weightedRewardVector) const; + + /*! + * Retrieves the delta used for digitization + */ + template ::SupportsExponential, int>::type = 0> + VT getDigitizationConstant(std::vector const& weightVector) const; + template ::SupportsExponential, int>::type = 0> + VT getDigitizationConstant(std::vector const& weightVector) const; + + /*! + * Digitizes the given matrix and vectors w.r.t. the given digitization constant and the given rate vector. + */ + template ::SupportsExponential, int>::type = 0> + void digitize(SubModel& subModel, VT const& digitizationConstant) const; + template ::SupportsExponential, int>::type = 0> + void digitize(SubModel& subModel, VT const& digitizationConstant) const; + + /* + * Fills the given maps with the digitized time bounds. Also sets the offsetsToLowerBound / offsetsToUpperBound values + * according to the digitization error + */ + template ::SupportsExponential, int>::type = 0> + void digitizeTimeBounds(TimeBoundMap& lowerTimeBounds, TimeBoundMap& upperTimeBounds, VT const& digitizationConstant); + template ::SupportsExponential, int>::type = 0> + void digitizeTimeBounds(TimeBoundMap& lowerTimeBounds, TimeBoundMap& upperTimeBounds, VT const& digitizationConstant); + + + /*! + * Initializes the data for the MinMax solver + */ + std::unique_ptr initMinMaxSolver(SubModel const& PS) const; + + /*! + * Initializes the data for the LinEq solver + */ + std::unique_ptr initLinEqSolver(SubModel const& PS) const; + + /* + * Updates the reward vectors within the split model, + * the reward vector of the reduced PStoPS model, and + * objectives that are considered at the current time epoch. + */ + void updateDataToCurrentEpoch(SubModel& MS, SubModel& PS, MinMaxSolverData& minMax, storm::storage::BitVector& consideredObjectives, uint_fast64_t const& currentEpoch, std::vector const& weightVector, TimeBoundMap::iterator& lowerTimeBoundIt, TimeBoundMap const& lowerTimeBounds, TimeBoundMap::iterator& upperTimeBoundIt, TimeBoundMap const& upperTimeBounds); + + /* + * Performs a step for the probabilistic states, that is + * * Compute an optimal scheduler for the weighted reward sum + * * Compute the values for the individual objectives w.r.t. that scheduler + * + * The resulting values represent the rewards at probabilistic states that are obtained at the current time epoch. + */ + void performPSStep(SubModel& PS, SubModel const& MS, MinMaxSolverData& minMax, LinEqSolverData& linEq, std::vector& optimalChoicesAtCurrentEpoch, storm::storage::BitVector const& consideredObjectives) const; + + /* + * Performs a step for the Markovian states, that is + * * Compute values for the weighted reward sum as well as for the individual objectives + * + * The resulting values represent the rewards at Markovian states that are obtained after one (digitized) time unit has passed. + */ + void performMSStep(SubModel& MS, SubModel const& PS, storm::storage::BitVector const& consideredObjectives) const; + + }; + + } + } +} + +#endif /* STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEMAPCAAWEIGHTVECTORCHECKER_H_ */ diff --git a/src/modelchecker/multiobjective/pcaa/SparseMdpPcaaWeightVectorChecker.cpp b/src/modelchecker/multiobjective/pcaa/SparseMdpPcaaWeightVectorChecker.cpp new file mode 100644 index 000000000..9529bf890 --- /dev/null +++ b/src/modelchecker/multiobjective/pcaa/SparseMdpPcaaWeightVectorChecker.cpp @@ -0,0 +1,122 @@ +#include "src/modelchecker/multiobjective/pcaa/SparseMdpPcaaWeightVectorChecker.h" + +#include "src/adapters/CarlAdapter.h" +#include "src/models/sparse/Mdp.h" +#include "src/models/sparse/StandardRewardModel.h" +#include "src/utility/macros.h" +#include "src/utility/vector.h" + + +namespace storm { + namespace modelchecker { + namespace multiobjective { + + template + SparseMdpPcaaWeightVectorChecker::SparseMdpPcaaWeightVectorChecker(SparseMdpModelType const& model, + std::vector> const& objectives, + storm::storage::BitVector const& actionsWithNegativeReward, + storm::storage::BitVector const& ecActions, + storm::storage::BitVector const& possiblyRecurrentStates) : + SparsePcaaWeightVectorChecker(model, objectives, actionsWithNegativeReward, ecActions, possiblyRecurrentStates) { + // set the state action rewards + for(uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) { + typename SparseMdpModelType::RewardModelType const& rewModel = this->model.getRewardModel(this->objectives[objIndex].rewardModelName); + STORM_LOG_ASSERT(!rewModel.hasTransitionRewards(), "Reward model has transition rewards which is not expected."); + this->discreteActionRewards[objIndex] = rewModel.getTotalRewardVector(this->model.getTransitionMatrix()); + } + } + + template + void SparseMdpPcaaWeightVectorChecker::boundedPhase(std::vector const& weightVector, std::vector& weightedRewardVector) { + // Allocate some memory so this does not need to happen for each time epoch + std::vector optimalChoicesInCurrentEpoch(this->model.getNumberOfStates()); + std::vector choiceValues(weightedRewardVector.size()); + std::vector temporaryResult(this->model.getNumberOfStates()); + std::vector zeroReward(weightedRewardVector.size(), storm::utility::zero()); + // Get for each occurring timeBound the indices of the objectives with that bound. + std::map> lowerTimeBounds; + std::map> upperTimeBounds; + for(uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) { + auto const& obj = this->objectives[objIndex]; + if(obj.lowerTimeBound) { + auto timeBoundIt = lowerTimeBounds.insert(std::make_pair(storm::utility::convertNumber(*obj.lowerTimeBound), storm::storage::BitVector(this->objectives.size(), false))).first; + STORM_LOG_WARN_COND(storm::utility::convertNumber(timeBoundIt->first) == (*obj.lowerTimeBound), "Rounded non-integral bound " << *obj.lowerTimeBound << " to " << timeBoundIt->first << "."); + timeBoundIt->second.set(objIndex); + } + if(obj.upperTimeBound) { + auto timeBoundIt = upperTimeBounds.insert(std::make_pair(storm::utility::convertNumber(*obj.upperTimeBound), storm::storage::BitVector(this->objectives.size(), false))).first; + STORM_LOG_WARN_COND(storm::utility::convertNumber(timeBoundIt->first) == (*obj.upperTimeBound), "Rounded non-integral bound " << *obj.upperTimeBound << " to " << timeBoundIt->first << "."); + timeBoundIt->second.set(objIndex); + + // There is no error for the values of these objectives. + this->offsetsToLowerBound[objIndex] = storm::utility::zero(); + this->offsetsToUpperBound[objIndex] = storm::utility::zero(); + } + } + + // Stores the objectives for which we need to compute values in the current time epoch. + storm::storage::BitVector consideredObjectives = this->objectivesWithNoUpperTimeBound; + + // Stores objectives for which the current epoch passed their lower bound + storm::storage::BitVector lowerBoundViolatedObjectives(consideredObjectives.size(), false); + + auto lowerTimeBoundIt = lowerTimeBounds.begin(); + auto upperTimeBoundIt = upperTimeBounds.begin(); + uint_fast64_t currentEpoch = std::max(lowerTimeBounds.empty() ? 0 : lowerTimeBoundIt->first - 1, upperTimeBounds.empty() ? 0 : upperTimeBoundIt->first); // consider lowerBound - 1 since we are interested in the first epoch that passes the bound + + while(currentEpoch > 0) { + //For lower time bounds we need to react when the currentEpoch passed the bound + // Hence, we substract 1 from the lower time bounds. + if(lowerTimeBoundIt != lowerTimeBounds.end() && currentEpoch == lowerTimeBoundIt->first - 1) { + lowerBoundViolatedObjectives |= lowerTimeBoundIt->second; + for(auto objIndex : lowerTimeBoundIt->second) { + // No more reward is earned for this objective. + storm::utility::vector::addScaledVector(weightedRewardVector, this->discreteActionRewards[objIndex], -weightVector[objIndex]); + } + ++lowerTimeBoundIt; + } + + if(upperTimeBoundIt != upperTimeBounds.end() && currentEpoch == upperTimeBoundIt->first) { + consideredObjectives |= upperTimeBoundIt->second; + for(auto objIndex : upperTimeBoundIt->second) { + // This objective now plays a role in the weighted sum + storm::utility::vector::addScaledVector(weightedRewardVector, this->discreteActionRewards[objIndex], weightVector[objIndex]); + } + ++upperTimeBoundIt; + } + + // Get values and scheduler for weighted sum of objectives + this->model.getTransitionMatrix().multiplyWithVector(this->weightedResult, choiceValues); + storm::utility::vector::addVectors(choiceValues, weightedRewardVector, choiceValues); + storm::utility::vector::reduceVectorMax(choiceValues, this->weightedResult, this->model.getTransitionMatrix().getRowGroupIndices(), &optimalChoicesInCurrentEpoch); + + // get values for individual objectives + // TODO we could compute the result for one of the objectives from the weighted result, the given weight vector, and the remaining objective results. + for(auto objIndex : consideredObjectives) { + std::vector& objectiveResult = this->objectiveResults[objIndex]; + std::vector const& objectiveRewards = lowerBoundViolatedObjectives.get(objIndex) ? zeroReward : this->discreteActionRewards[objIndex]; + auto rowGroupIndexIt = this->model.getTransitionMatrix().getRowGroupIndices().begin(); + auto optimalChoiceIt = optimalChoicesInCurrentEpoch.begin(); + for(ValueType& stateValue : temporaryResult){ + uint_fast64_t row = (*rowGroupIndexIt) + (*optimalChoiceIt); + ++rowGroupIndexIt; + ++optimalChoiceIt; + stateValue = objectiveRewards[row]; + for(auto const& entry : this->model.getTransitionMatrix().getRow(row)) { + stateValue += entry.getValue() * objectiveResult[entry.getColumn()]; + } + } + objectiveResult.swap(temporaryResult); + } + --currentEpoch; + } + } + + template class SparseMdpPcaaWeightVectorChecker>; +#ifdef STORM_HAVE_CARL + template class SparseMdpPcaaWeightVectorChecker>; +#endif + + } + } +} diff --git a/src/modelchecker/multiobjective/pcaa/SparseMdpPcaaWeightVectorChecker.h b/src/modelchecker/multiobjective/pcaa/SparseMdpPcaaWeightVectorChecker.h new file mode 100644 index 000000000..e6565f413 --- /dev/null +++ b/src/modelchecker/multiobjective/pcaa/SparseMdpPcaaWeightVectorChecker.h @@ -0,0 +1,48 @@ +#ifndef STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEMDPPCAAWEIGHTVECTORCHECKER_H_ +#define STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEMDPPCAAWEIGHTVECTORCHECKER_H_ + +#include + +#include "src/modelchecker/multiobjective/pcaa/SparsePcaaWeightVectorChecker.h" + +namespace storm { + namespace modelchecker { + namespace multiobjective { + + /*! + * Helper Class that takes preprocessed Pcaa data and a weight vector and ... + * - computes the maximal expected reward w.r.t. the weighted sum of the rewards of the individual objectives + * - extracts the scheduler that induces this maximum + * - computes for each objective the value induced by this scheduler + */ + template + class SparseMdpPcaaWeightVectorChecker : public SparsePcaaWeightVectorChecker { + public: + typedef typename SparseMdpModelType::ValueType ValueType; + + SparseMdpPcaaWeightVectorChecker(SparseMdpModelType const& model, + std::vector> const& objectives, + storm::storage::BitVector const& actionsWithNegativeReward, + storm::storage::BitVector const& ecActions, + storm::storage::BitVector const& possiblyRecurrentStates); + + private: + + /*! + * For each time epoch (starting with the maximal stepBound occurring in the objectives), this method + * - determines the objectives that are relevant in the current time epoch + * - determines the maximizing scheduler for the weighted reward vector of these objectives + * - computes the values of these objectives w.r.t. this scheduler + * + * @param weightVector the weight vector of the current check + * @param weightedRewardVector the weighted rewards considering the unbounded objectives. Will be invalidated after calling this. + */ + virtual void boundedPhase(std::vector const& weightVector, std::vector& weightedRewardVector) override; + + }; + + } + } +} + +#endif /* STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEMDPPCAAWEIGHTVECTORCHECKER_H_ */ diff --git a/src/modelchecker/multiobjective/pcaa/SparsePCAAPreprocessor.cpp b/src/modelchecker/multiobjective/pcaa/SparsePCAAPreprocessor.cpp new file mode 100644 index 000000000..977a7b19c --- /dev/null +++ b/src/modelchecker/multiobjective/pcaa/SparsePCAAPreprocessor.cpp @@ -0,0 +1,418 @@ + #include "src/modelchecker/multiobjective/pcaa/SparsePcaaPreprocessor.h" + +#include "src/models/sparse/Mdp.h" +#include "src/models/sparse/MarkovAutomaton.h" +#include "src/models/sparse/StandardRewardModel.h" +#include "src/modelchecker/propositional/SparsePropositionalModelChecker.h" +#include "src/modelchecker/results/ExplicitQualitativeCheckResult.h" +#include "src/storage/MaximalEndComponentDecomposition.h" +#include "src/transformer/StateDuplicator.h" +#include "src/transformer/SubsystemBuilder.h" +#include "src/utility/macros.h" +#include "src/utility/vector.h" +#include "src/utility/graph.h" + +#include "src/exceptions/InvalidPropertyException.h" +#include "src/exceptions/UnexpectedException.h" + +namespace storm { + namespace modelchecker { + namespace multiobjective { + + template + typename SparsePcaaPreprocessor::ReturnType SparsePcaaPreprocessor::preprocess(SparseModelType const& originalModel, storm::logic::MultiObjectiveFormula const& originalFormula) { + + ReturnType result(originalFormula, originalModel, SparseModelType(originalModel)); + result.newToOldStateIndexMapping = storm::utility::vector::buildVectorForRange(0, originalModel.getNumberOfStates()); + + //Invoke preprocessing on the individual objectives + for(auto const& subFormula : originalFormula.getSubformulas()){ + STORM_LOG_DEBUG("Preprocessing objective " << *subFormula<< "."); + result.objectives.emplace_back(); + PcaaObjective& currentObjective = result.objectives.back(); + currentObjective.originalFormula = subFormula; + if(currentObjective.originalFormula->isOperatorFormula()) { + preprocessFormula(currentObjective.originalFormula->asOperatorFormula(), result, currentObjective); + } else { + STORM_LOG_THROW(false, storm::exceptions::InvalidPropertyException, "Could not preprocess the subformula " << *subFormula << " of " << originalFormula << " because it is not supported"); + } + } + + // Set the query type. In case of a quantitative query, also set the index of the objective to be optimized. + // Note: If there are only zero (or one) objectives left, we should not consider a pareto query! + storm::storage::BitVector objectivesWithoutThreshold(result.objectives.size()); + for(uint_fast64_t objIndex = 0; objIndex < result.objectives.size(); ++objIndex) { + objectivesWithoutThreshold.set(objIndex, !result.objectives[objIndex].threshold); + } + uint_fast64_t numOfObjectivesWithoutThreshold = objectivesWithoutThreshold.getNumberOfSetBits(); + if(numOfObjectivesWithoutThreshold == 0) { + result.queryType = ReturnType::QueryType::Achievability; + } else if (numOfObjectivesWithoutThreshold == 1) { + result.queryType = ReturnType::QueryType::Quantitative; + result.indexOfOptimizingObjective = objectivesWithoutThreshold.getNextSetIndex(0); + } else if (numOfObjectivesWithoutThreshold == result.objectives.size()) { + result.queryType = ReturnType::QueryType::Pareto; + } else { + STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "The number of objectives without threshold is not valid. It should be either 0 (achievability query), 1 (quantitative query), or " << result.objectives.size() << " (Pareto Query). Got " << numOfObjectivesWithoutThreshold << " instead."); + } + + //We can remove the original reward models to save some memory + std::set origRewardModels = originalFormula.getReferencedRewardModels(); + for (auto const& rewModel : origRewardModels){ + result.preprocessedModel.removeRewardModel(rewModel); + } + + //Get actions to which a positive or negative reward is assigned for at least one objective + result.actionsWithPositiveReward = storm::storage::BitVector(result.preprocessedModel.getNumberOfChoices(), false); + result.actionsWithNegativeReward = storm::storage::BitVector(result.preprocessedModel.getNumberOfChoices(), false); + for(uint_fast64_t objIndex = 0; objIndex < result.objectives.size(); ++objIndex) { + if(!result.objectives[objIndex].upperTimeBound) { + if(result.objectives[objIndex].rewardsArePositive) { + result.actionsWithPositiveReward |= ~storm::utility::vector::filterZero(result.preprocessedModel.getRewardModel(result.objectives[objIndex].rewardModelName).getTotalRewardVector(result.preprocessedModel.getTransitionMatrix())); + } else { + result.actionsWithNegativeReward |= ~storm::utility::vector::filterZero(result.preprocessedModel.getRewardModel(result.objectives[objIndex].rewardModelName).getTotalRewardVector(result.preprocessedModel.getTransitionMatrix())); + } + } + } + + auto backwardTransitions = result.preprocessedModel.getBackwardTransitions(); + analyzeEndComponents(result, backwardTransitions); + ensureRewardFiniteness(result, backwardTransitions); + + return result; + } + + template + void SparsePcaaPreprocessor::updatePreprocessedModel(ReturnType& result, SparseModelType& newPreprocessedModel, std::vector& newToOldStateIndexMapping) { + result.preprocessedModel = std::move(newPreprocessedModel); + // the given newToOldStateIndexMapping reffers to the indices of the former preprocessedModel as 'old indices' + for(auto & preprocessedModelStateIndex : newToOldStateIndexMapping){ + preprocessedModelStateIndex = result.newToOldStateIndexMapping[preprocessedModelStateIndex]; + } + result.newToOldStateIndexMapping = std::move(newToOldStateIndexMapping); + } + + template + void SparsePcaaPreprocessor::preprocessFormula(storm::logic::OperatorFormula const& formula, ReturnType& result, PcaaObjective& currentObjective) { + + // Get a unique name for the new reward model. + currentObjective.rewardModelName = "objective" + std::to_string(result.objectives.size()); + while(result.preprocessedModel.hasRewardModel(currentObjective.rewardModelName)){ + currentObjective.rewardModelName = "_" + currentObjective.rewardModelName; + } + + currentObjective.toOriginalValueTransformationFactor = storm::utility::one(); + currentObjective.toOriginalValueTransformationOffset = storm::utility::zero(); + currentObjective.rewardsArePositive = true; + + bool formulaMinimizes = false; + if(formula.hasBound()) { + currentObjective.threshold = storm::utility::convertNumber(formula.getBound().threshold); + currentObjective.thresholdIsStrict = storm::logic::isStrict(formula.getBound().comparisonType); + //Note that we minimize for upper bounds since we are looking for the EXISTENCE of a satisfying scheduler + formulaMinimizes = !storm::logic::isLowerBound(formula.getBound().comparisonType); + } else if (formula.hasOptimalityType()){ + formulaMinimizes = storm::solver::minimize(formula.getOptimalityType()); + } else { + STORM_LOG_THROW(false, storm::exceptions::InvalidPropertyException, "Current objective " << formula << " does not specify whether to minimize or maximize"); + } + if(formulaMinimizes) { + // We negate all the values so we can consider the maximum for this objective + // Thus, all objectives will be maximized. + currentObjective.rewardsArePositive = false; + currentObjective.toOriginalValueTransformationFactor = -storm::utility::one(); + } + + if(formula.isProbabilityOperatorFormula()){ + preprocessFormula(formula.asProbabilityOperatorFormula(), result, currentObjective); + } else if(formula.isRewardOperatorFormula()){ + preprocessFormula(formula.asRewardOperatorFormula(), result, currentObjective); + } else if(formula.isTimeOperatorFormula()){ + preprocessFormula(formula.asTimeOperatorFormula(), result, currentObjective); + } else { + STORM_LOG_THROW(false, storm::exceptions::InvalidPropertyException, "Could not preprocess the objective " << formula << " because it is not supported"); + } + + // Transform the threshold for the preprocessed Model + if(currentObjective.threshold) { + currentObjective.threshold = (currentObjective.threshold.get() - currentObjective.toOriginalValueTransformationOffset) / currentObjective.toOriginalValueTransformationFactor; + } + } + + template + void SparsePcaaPreprocessor::preprocessFormula(storm::logic::ProbabilityOperatorFormula const& formula, ReturnType& result, PcaaObjective& currentObjective) { + + if(formula.getSubformula().isUntilFormula()){ + preprocessFormula(formula.getSubformula().asUntilFormula(), result, currentObjective); + } else if(formula.getSubformula().isBoundedUntilFormula()){ + preprocessFormula(formula.getSubformula().asBoundedUntilFormula(), result, currentObjective); + } else if(formula.getSubformula().isGloballyFormula()){ + preprocessFormula(formula.getSubformula().asGloballyFormula(), result, currentObjective); + } else if(formula.getSubformula().isEventuallyFormula()){ + preprocessFormula(formula.getSubformula().asEventuallyFormula(), result, currentObjective); + } else { + STORM_LOG_THROW(false, storm::exceptions::InvalidPropertyException, "The subformula of " << formula << " is not supported."); + } + } + + template + void SparsePcaaPreprocessor::preprocessFormula(storm::logic::RewardOperatorFormula const& formula, ReturnType& result, PcaaObjective& currentObjective) { + // Check if the reward model is uniquely specified + STORM_LOG_THROW((formula.hasRewardModelName() && result.preprocessedModel.hasRewardModel(formula.getRewardModelName())) + || result.preprocessedModel.hasUniqueRewardModel(), storm::exceptions::InvalidPropertyException, "The reward model is not unique and the formula " << formula << " does not specify a reward model."); + + if(formula.getSubformula().isEventuallyFormula()){ + preprocessFormula(formula.getSubformula().asEventuallyFormula(), result, currentObjective, formula.getOptionalRewardModelName()); + } else if(formula.getSubformula().isCumulativeRewardFormula()) { + preprocessFormula(formula.getSubformula().asCumulativeRewardFormula(), result, currentObjective, formula.getOptionalRewardModelName()); + } else if(formula.getSubformula().isTotalRewardFormula()) { + preprocessFormula(formula.getSubformula().asTotalRewardFormula(), result, currentObjective, formula.getOptionalRewardModelName()); + } else { + STORM_LOG_THROW(false, storm::exceptions::InvalidPropertyException, "The subformula of " << formula << " is not supported."); + } + } + + template + void SparsePcaaPreprocessor::preprocessFormula(storm::logic::TimeOperatorFormula const& formula, ReturnType& result, PcaaObjective& currentObjective) { + // Time formulas are only supported for Markov automata + STORM_LOG_THROW(result.originalModel.isOfType(storm::models::ModelType::MarkovAutomaton), storm::exceptions::InvalidPropertyException, "Time operator formulas are only supported for Markov automata."); + + if(formula.getSubformula().isEventuallyFormula()){ + preprocessFormula(formula.getSubformula().asEventuallyFormula(), result, currentObjective); + } else { + STORM_LOG_THROW(false, storm::exceptions::InvalidPropertyException, "The subformula of " << formula << " is not supported."); + } + } + + template + void SparsePcaaPreprocessor::preprocessFormula(storm::logic::UntilFormula const& formula, ReturnType& result, PcaaObjective& currentObjective) { + CheckTask phiTask(formula.getLeftSubformula()); + CheckTask psiTask(formula.getRightSubformula()); + storm::modelchecker::SparsePropositionalModelChecker mc(result.preprocessedModel); + STORM_LOG_THROW(mc.canHandle(phiTask) && mc.canHandle(psiTask), storm::exceptions::InvalidPropertyException, "The subformulas of " << formula << " should be propositional."); + storm::storage::BitVector phiStates = mc.check(phiTask)->asExplicitQualitativeCheckResult().getTruthValuesVector(); + storm::storage::BitVector psiStates = mc.check(psiTask)->asExplicitQualitativeCheckResult().getTruthValuesVector(); + + if(!(psiStates & result.preprocessedModel.getInitialStates()).empty() && !currentObjective.lowerTimeBound) { + // The probability is always one as the initial state is a target state. + // For this special case, the transformation to an expected reward objective fails. + // We could handle this with further preprocessing steps but as this case is boring anyway, we simply reject the input. + STORM_LOG_THROW(false, storm::exceptions::InvalidPropertyException, "The Probability for the objective " << currentObjective.originalFormula << " is always one as the rhs of the until formula is true in the initial state. Please omit this objective."); + } + + auto duplicatorResult = storm::transformer::StateDuplicator::transform(result.preprocessedModel, ~phiStates | psiStates); + updatePreprocessedModel(result, *duplicatorResult.model, duplicatorResult.newToOldStateIndexMapping); + + storm::storage::BitVector newPsiStates(result.preprocessedModel.getNumberOfStates(), false); + for(auto const& oldPsiState : psiStates){ + //note that psiStates are always located in the second copy + newPsiStates.set(duplicatorResult.secondCopyOldToNewStateIndexMapping[oldPsiState], true); + } + + // build stateAction reward vector that gives (one*transitionProbability) reward whenever a transition leads from the firstCopy to a psiState + std::vector objectiveRewards(result.preprocessedModel.getTransitionMatrix().getRowCount(), storm::utility::zero()); + for (auto const& firstCopyState : duplicatorResult.firstCopy) { + for (uint_fast64_t row = result.preprocessedModel.getTransitionMatrix().getRowGroupIndices()[firstCopyState]; row < result.preprocessedModel.getTransitionMatrix().getRowGroupIndices()[firstCopyState + 1]; ++row) { + objectiveRewards[row] = result.preprocessedModel.getTransitionMatrix().getConstrainedRowSum(row, newPsiStates); + } + } + if(!currentObjective.rewardsArePositive) { + storm::utility::vector::scaleVectorInPlace(objectiveRewards, -storm::utility::one()); + } + result.preprocessedModel.addRewardModel(currentObjective.rewardModelName, RewardModelType(boost::none, objectiveRewards)); + } + + template + void SparsePcaaPreprocessor::preprocessFormula(storm::logic::BoundedUntilFormula const& formula, ReturnType& result, PcaaObjective& currentObjective) { + + if(formula.hasDiscreteTimeBound()) { + currentObjective.upperTimeBound = storm::utility::convertNumber(formula.getDiscreteTimeBound()); + } else { + if(result.originalModel.isOfType(storm::models::ModelType::Mdp)) { + STORM_LOG_THROW(formula.getIntervalBounds().first == std::round(formula.getIntervalBounds().first), storm::exceptions::InvalidPropertyException, "Expected a boundedUntilFormula with discrete lower time bound but got " << formula << "."); + STORM_LOG_THROW(formula.getIntervalBounds().second == std::round(formula.getIntervalBounds().second), storm::exceptions::InvalidPropertyException, "Expected a boundedUntilFormula with discrete upper time bound but got " << formula << "."); + } else { + STORM_LOG_THROW(result.originalModel.isOfType(storm::models::ModelType::MarkovAutomaton), storm::exceptions::InvalidPropertyException, "Got a boundedUntilFormula which can not be checked for the current model type."); + STORM_LOG_THROW(formula.getIntervalBounds().second > formula.getIntervalBounds().first, storm::exceptions::InvalidPropertyException, "Neither empty nor point intervalls are allowed but got " << formula << "."); + } + if(!storm::utility::isZero(formula.getIntervalBounds().first)) { + currentObjective.lowerTimeBound = storm::utility::convertNumber(formula.getIntervalBounds().first); + } + currentObjective.upperTimeBound = storm::utility::convertNumber(formula.getIntervalBounds().second); + } + preprocessFormula(storm::logic::UntilFormula(formula.getLeftSubformula().asSharedPointer(), formula.getRightSubformula().asSharedPointer()), result, currentObjective); + } + + template + void SparsePcaaPreprocessor::preprocessFormula(storm::logic::GloballyFormula const& formula, ReturnType& result, PcaaObjective& currentObjective) { + // The formula will be transformed to an until formula for the complementary event. + // If the original formula minimizes, the complementary one will maximize and vice versa. + // Hence, the decision whether to consider positive or negative rewards flips. + currentObjective.rewardsArePositive = !currentObjective.rewardsArePositive; + // To transform from the value of the preprocessed model back to the value of the original model, we have to add 1 to the result. + // The transformation factor has already been set correctly. + currentObjective.toOriginalValueTransformationOffset = storm::utility::one(); + + auto negatedSubformula = std::make_shared(storm::logic::UnaryBooleanStateFormula::OperatorType::Not, formula.getSubformula().asSharedPointer()); + + preprocessFormula(storm::logic::UntilFormula(storm::logic::Formula::getTrueFormula(), negatedSubformula), result, currentObjective); + } + + template + void SparsePcaaPreprocessor::preprocessFormula(storm::logic::EventuallyFormula const& formula, ReturnType& result, PcaaObjective& currentObjective, boost::optional const& optionalRewardModelName) { + if(formula.isReachabilityProbabilityFormula()){ + preprocessFormula(storm::logic::UntilFormula(storm::logic::Formula::getTrueFormula(), formula.getSubformula().asSharedPointer()), result, currentObjective); + return; + } + + CheckTask targetTask(formula.getSubformula()); + storm::modelchecker::SparsePropositionalModelChecker mc(result.preprocessedModel); + STORM_LOG_THROW(mc.canHandle(targetTask), storm::exceptions::InvalidPropertyException, "The subformula of " << formula << " should be propositional."); + storm::storage::BitVector targetStates = mc.check(targetTask)->asExplicitQualitativeCheckResult().getTruthValuesVector(); + + auto duplicatorResult = storm::transformer::StateDuplicator::transform(result.preprocessedModel, targetStates); + updatePreprocessedModel(result, *duplicatorResult.model, duplicatorResult.newToOldStateIndexMapping); + + // Add a reward model that gives zero reward to the actions of states of the second copy. + RewardModelType objectiveRewards(boost::none); + if(formula.isReachabilityRewardFormula()) { + objectiveRewards = result.preprocessedModel.getRewardModel(optionalRewardModelName ? optionalRewardModelName.get() : ""); + objectiveRewards.reduceToStateBasedRewards(result.preprocessedModel.getTransitionMatrix(), false); + if(objectiveRewards.hasStateRewards()) { + storm::utility::vector::setVectorValues(objectiveRewards.getStateRewardVector(), duplicatorResult.secondCopy, storm::utility::zero()); + } + if(objectiveRewards.hasStateActionRewards()) { + for(auto secondCopyState : duplicatorResult.secondCopy) { + std::fill_n(objectiveRewards.getStateActionRewardVector().begin() + result.preprocessedModel.getTransitionMatrix().getRowGroupIndices()[secondCopyState], result.preprocessedModel.getTransitionMatrix().getRowGroupSize(secondCopyState), storm::utility::zero()); + } + } + } else if(formula.isReachabilityTimeFormula() && result.preprocessedModel.isOfType(storm::models::ModelType::MarkovAutomaton)) { + objectiveRewards = RewardModelType(std::vector(result.preprocessedModel.getNumberOfStates(), storm::utility::zero())); + storm::utility::vector::setVectorValues(objectiveRewards.getStateRewardVector(), dynamic_cast*>(&result.preprocessedModel)->getMarkovianStates() & duplicatorResult.firstCopy, storm::utility::one()); + } else { + STORM_LOG_THROW(false, storm::exceptions::InvalidPropertyException, "The formula " << formula << " neither considers reachability probabilities nor reachability rewards " << (result.preprocessedModel.isOfType(storm::models::ModelType::MarkovAutomaton) ? "nor reachability time" : "") << ". This is not supported."); + } + if(!currentObjective.rewardsArePositive){ + if(objectiveRewards.hasStateRewards()) { + storm::utility::vector::scaleVectorInPlace(objectiveRewards.getStateRewardVector(), -storm::utility::one()); + } + if(objectiveRewards.hasStateActionRewards()) { + storm::utility::vector::scaleVectorInPlace(objectiveRewards.getStateActionRewardVector(), -storm::utility::one()); + } + } + result.preprocessedModel.addRewardModel(currentObjective.rewardModelName, std::move(objectiveRewards)); + } + + template + void SparsePcaaPreprocessor::preprocessFormula(storm::logic::CumulativeRewardFormula const& formula, ReturnType& result, PcaaObjective& currentObjective, boost::optional const& optionalRewardModelName) { + STORM_LOG_THROW(result.originalModel.isOfType(storm::models::ModelType::Mdp), storm::exceptions::InvalidPropertyException, "Cumulative reward formulas are not supported for the given model type."); + STORM_LOG_THROW(formula.hasDiscreteTimeBound(), storm::exceptions::InvalidPropertyException, "Expected a cumulativeRewardFormula with a discrete time bound but got " << formula << "."); + STORM_LOG_THROW(formula.getDiscreteTimeBound()>0, storm::exceptions::InvalidPropertyException, "Expected a cumulativeRewardFormula with a positive discrete time bound but got " << formula << "."); + currentObjective.upperTimeBound = storm::utility::convertNumber(formula.getDiscreteTimeBound()); + + RewardModelType objectiveRewards = result.preprocessedModel.getRewardModel(optionalRewardModelName ? optionalRewardModelName.get() : ""); + objectiveRewards.reduceToStateBasedRewards(result.preprocessedModel.getTransitionMatrix(), false); + if(!currentObjective.rewardsArePositive){ + if(objectiveRewards.hasStateRewards()) { + storm::utility::vector::scaleVectorInPlace(objectiveRewards.getStateRewardVector(), -storm::utility::one()); + } + if(objectiveRewards.hasStateActionRewards()) { + storm::utility::vector::scaleVectorInPlace(objectiveRewards.getStateActionRewardVector(), -storm::utility::one()); + } + } + result.preprocessedModel.addRewardModel(currentObjective.rewardModelName, std::move(objectiveRewards)); + } + + template + void SparsePcaaPreprocessor::preprocessFormula(storm::logic::TotalRewardFormula const& formula, ReturnType& result, PcaaObjective& currentObjective, boost::optional const& optionalRewardModelName) { + RewardModelType objectiveRewards = result.preprocessedModel.getRewardModel(optionalRewardModelName ? optionalRewardModelName.get() : ""); + objectiveRewards.reduceToStateBasedRewards(result.preprocessedModel.getTransitionMatrix(), false); + if(!currentObjective.rewardsArePositive){ + if(objectiveRewards.hasStateRewards()) { + storm::utility::vector::scaleVectorInPlace(objectiveRewards.getStateRewardVector(), -storm::utility::one()); + } + if(objectiveRewards.hasStateActionRewards()) { + storm::utility::vector::scaleVectorInPlace(objectiveRewards.getStateActionRewardVector(), -storm::utility::one()); + } + } + result.preprocessedModel.addRewardModel(currentObjective.rewardModelName, std::move(objectiveRewards)); + } + + + template + void SparsePcaaPreprocessor::analyzeEndComponents(ReturnType& result, storm::storage::SparseMatrix const& backwardTransitions) { + + result.ecActions = storm::storage::BitVector(result.preprocessedModel.getNumberOfChoices(), false); + std::vector ecs; + auto mecDecomposition = storm::storage::MaximalEndComponentDecomposition(result.preprocessedModel.getTransitionMatrix(), backwardTransitions); + STORM_LOG_ASSERT(!mecDecomposition.empty(), "Empty maximal end component decomposition."); + ecs.reserve(mecDecomposition.size()); + for(auto& mec : mecDecomposition) { + for(auto const& stateActionsPair : mec) { + for(auto const& action : stateActionsPair.second) { + result.ecActions.set(action); + } + } + ecs.push_back(std::move(mec)); + } + + result.possiblyRecurrentStates = storm::storage::BitVector(result.preprocessedModel.getNumberOfStates(), false); + storm::storage::BitVector neutralActions = ~(result.actionsWithNegativeReward | result.actionsWithPositiveReward); + storm::storage::BitVector statesInCurrentECWithNeutralAction(result.preprocessedModel.getNumberOfStates(), false); + for(uint_fast64_t ecIndex = 0; ecIndex < ecs.size(); ++ecIndex) { //we will insert new ecs in the vector (thus no iterators for the loop) + bool currentECIsNeutral = true; + for(auto const& stateActionsPair : ecs[ecIndex]) { + bool stateHasNeutralActionWithinEC = false; + for(auto const& action : stateActionsPair.second) { + stateHasNeutralActionWithinEC |= neutralActions.get(action); + } + statesInCurrentECWithNeutralAction.set(stateActionsPair.first, stateHasNeutralActionWithinEC); + currentECIsNeutral &= stateHasNeutralActionWithinEC; + } + if(currentECIsNeutral) { + result.possiblyRecurrentStates |= statesInCurrentECWithNeutralAction; + }else{ + // Check if the ec contains neutral sub ecs. This is done by adding the subECs to our list of ECs + // A neutral subEC only consist of states that can stay in statesInCurrentECWithNeutralAction + statesInCurrentECWithNeutralAction = storm::utility::graph::performProb0E(result.preprocessedModel.getTransitionMatrix(), result.preprocessedModel.getTransitionMatrix().getRowGroupIndices(), backwardTransitions,statesInCurrentECWithNeutralAction, ~statesInCurrentECWithNeutralAction); + auto subECs = storm::storage::MaximalEndComponentDecomposition(result.preprocessedModel.getTransitionMatrix(), backwardTransitions, statesInCurrentECWithNeutralAction); + ecs.reserve(ecs.size() + subECs.size()); + for(auto& ec : subECs){ + ecs.push_back(std::move(ec)); + } + } + statesInCurrentECWithNeutralAction.clear(); + } + } + + template + void SparsePcaaPreprocessor::ensureRewardFiniteness(ReturnType& result, storm::storage::SparseMatrix const& backwardTransitions) { + STORM_LOG_THROW((result.actionsWithPositiveReward & result.ecActions).empty(), storm::exceptions::InvalidPropertyException, "Infinite reward: There is one maximizing objective for which infinite reward is possible. This is not supported."); + + //Check whether the states that can be visited inf. often are reachable with prob. 1 under some scheduler + storm::storage::BitVector statesReachingNegativeRewardsFinitelyOftenForSomeScheduler = storm::utility::graph::performProb1E(result.preprocessedModel.getTransitionMatrix(), result.preprocessedModel.getTransitionMatrix().getRowGroupIndices(), backwardTransitions, storm::storage::BitVector(result.preprocessedModel.getNumberOfStates(), true), result.possiblyRecurrentStates); + STORM_LOG_THROW(!(statesReachingNegativeRewardsFinitelyOftenForSomeScheduler & result.preprocessedModel.getInitialStates()).empty(), storm::exceptions::InvalidPropertyException, "Infinite Rewards: For every scheduler, the induced reward for one or more of the objectives that minimize rewards is infinity."); + + if(!statesReachingNegativeRewardsFinitelyOftenForSomeScheduler.full()) { + // Remove the states that for any scheduler have one objective with infinite expected reward. + auto subsystemBuilderResult = storm::transformer::SubsystemBuilder::transform(result.preprocessedModel, statesReachingNegativeRewardsFinitelyOftenForSomeScheduler, storm::storage::BitVector(result.preprocessedModel.getNumberOfChoices(), true)); + updatePreprocessedModel(result, *subsystemBuilderResult.model, subsystemBuilderResult.newToOldStateIndexMapping); + result.ecActions = result.ecActions % subsystemBuilderResult.keptActions; + result.actionsWithPositiveReward = result.actionsWithPositiveReward % subsystemBuilderResult.keptActions; + result.actionsWithNegativeReward = result.actionsWithNegativeReward % subsystemBuilderResult.keptActions; + result.possiblyRecurrentStates = result.possiblyRecurrentStates % statesReachingNegativeRewardsFinitelyOftenForSomeScheduler; + } + } + + + + template class SparsePcaaPreprocessor>; + template class SparsePcaaPreprocessor>; + +#ifdef STORM_HAVE_CARL + template class SparsePcaaPreprocessor>; + template class SparsePcaaPreprocessor>; +#endif + } + } +} diff --git a/src/modelchecker/multiobjective/pcaa/SparsePCAAPreprocessor.h b/src/modelchecker/multiobjective/pcaa/SparsePCAAPreprocessor.h new file mode 100644 index 000000000..aa8832cc5 --- /dev/null +++ b/src/modelchecker/multiobjective/pcaa/SparsePCAAPreprocessor.h @@ -0,0 +1,82 @@ +#ifndef STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEPCAAPREPROCESSOR_H_ +#define STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEPCAAPREPROCESSOR_H_ + +#include + +#include "src/logic/Formulas.h" +#include "src/storage/BitVector.h" +#include "src/modelchecker/multiobjective/pcaa/SparsePcaaPreprocessorReturnType.h" + +namespace storm { + namespace modelchecker { + namespace multiobjective { + + /* + * This class invokes the necessary preprocessing for the Pareto Curve Approximation Algorithm (PCAA) + */ + template + class SparsePcaaPreprocessor { + public: + typedef typename SparseModelType::ValueType ValueType; + typedef typename SparseModelType::RewardModelType RewardModelType; + typedef SparsePcaaPreprocessorReturnType ReturnType; + + /*! + * 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(SparseModelType const& originalModel, storm::logic::MultiObjectiveFormula const& originalFormula); + + private: + /*! + * Initializes the returned Information + * @param originalModel The considered model + * @param originalFormula the considered formula + */ + static ReturnType initializeResult(storm::logic::MultiObjectiveFormula const& originalFormula, SparseModelType const& originalModel); + + /*! + * Updates the preprocessed model stored in the given result to the given model. + * The given newToOldStateIndexMapping should give for each state in the newPreprocessedModel + * the index of the state in the current result.preprocessedModel. + */ + static void updatePreprocessedModel(ReturnType& result, SparseModelType& newPreprocessedModel, std::vector& newToOldStateIndexMapping); + + /*! + * Apply the neccessary preprocessing for the given formula. + * @param formula the current (sub)formula + * @param result 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::OperatorFormula const& formula, ReturnType& result, PcaaObjective& currentObjective); + static void preprocessFormula(storm::logic::ProbabilityOperatorFormula const& formula, ReturnType& result, PcaaObjective& currentObjective); + static void preprocessFormula(storm::logic::RewardOperatorFormula const& formula, ReturnType& result, PcaaObjective& currentObjective); + static void preprocessFormula(storm::logic::TimeOperatorFormula const& formula, ReturnType& result, PcaaObjective& currentObjective); + static void preprocessFormula(storm::logic::UntilFormula const& formula, ReturnType& result, PcaaObjective& currentObjective); + static void preprocessFormula(storm::logic::BoundedUntilFormula const& formula, ReturnType& result, PcaaObjective& currentObjective); + static void preprocessFormula(storm::logic::GloballyFormula const& formula, ReturnType& result, PcaaObjective& currentObjective); + static void preprocessFormula(storm::logic::EventuallyFormula const& formula, ReturnType& result, PcaaObjective& currentObjective, boost::optional const& optionalRewardModelName = boost::none); + static void preprocessFormula(storm::logic::CumulativeRewardFormula const& formula, ReturnType& result, PcaaObjective& currentObjective, boost::optional const& optionalRewardModelName = boost::none); + static void preprocessFormula(storm::logic::TotalRewardFormula const& formula, ReturnType& result, PcaaObjective& currentObjective, boost::optional const& optionalRewardModelName = boost::none); + + /*! + * Analyzes the end components of the preprocessed model. That is: + * -get the set of actions that are part of an end component + * -Find the states that can be visited infinitely often without inducing infinite reward + */ + static void analyzeEndComponents(ReturnType& result, storm::storage::SparseMatrix const& backwardTransitions); + + /*! + * Checks whether the occurring expected rewards are finite. If not, the input is rejected. + * Also removes all states for which no finite reward wrt. all objectives is possible + */ + static void ensureRewardFiniteness(ReturnType& result, storm::storage::SparseMatrix const& backwardTransitions); + + }; + } + } +} + +#endif /* STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEPCAAPREPROCESSOR_H_ */ diff --git a/src/modelchecker/multiobjective/pcaa/SparsePCAAPreprocessorReturnType.h b/src/modelchecker/multiobjective/pcaa/SparsePCAAPreprocessorReturnType.h new file mode 100644 index 000000000..add8d7830 --- /dev/null +++ b/src/modelchecker/multiobjective/pcaa/SparsePCAAPreprocessorReturnType.h @@ -0,0 +1,93 @@ +#ifndef STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEPCAAPREPROCESSORRETURNTYPE_H_ +#define STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEPCAAPREPROCESSORRETURNTYPE_H_ + +#include +#include +#include +#include +#include + +#include "src/logic/Formulas.h" +#include "src/modelchecker/multiobjective/pcaa/PcaaObjective.h" +#include "src/models/sparse/MarkovAutomaton.h" +#include "src/storage/BitVector.h" +#include "src/utility/macros.h" +#include "src/utility/constants.h" + +#include "src/exceptions/UnexpectedException.h" + +namespace storm { + namespace modelchecker { + namespace multiobjective { + + template + struct SparsePcaaPreprocessorReturnType { + + enum class QueryType { Achievability, Quantitative, Pareto }; + + storm::logic::MultiObjectiveFormula const& originalFormula; + SparseModelType const& originalModel; + SparseModelType preprocessedModel; + QueryType queryType; + + // The (preprocessed) objectives + std::vector> objectives; + + // The index of the objective that is to be maximized (or minimized) in case of a quantitative Query + boost::optional indexOfOptimizingObjective; + + // Maps any state of the preprocessed model to the corresponding state of the original Model + std::vector newToOldStateIndexMapping; + + // The actions of the preprocessed model that have positive reward assigned for at least one objective (objectives with an upper time-bound are ignored!) + storm::storage::BitVector actionsWithPositiveReward; + // The actions of the preprocessed model that have negative reward assigned for at least one objective (objectives with an upper time-bound are ignored!) + storm::storage::BitVector actionsWithNegativeReward; + // The actions of the preprocessed model that are part of an EC + storm::storage::BitVector ecActions; + + // The set of states of the preprocessed model for which there is a scheduler such that + // the state is visited infinitely often and the induced reward is finite for all objectives + storm::storage::BitVector possiblyRecurrentStates; + + SparsePcaaPreprocessorReturnType(storm::logic::MultiObjectiveFormula const& originalFormula, SparseModelType const& originalModel, SparseModelType&& preprocessedModel) : originalFormula(originalFormula), originalModel(originalModel), preprocessedModel(preprocessedModel) { + // Intentionally left empty + } + + void printToStream(std::ostream& out) const { + out << std::endl; + out << "---------------------------------------------------------------------------------------------------------------------------------------" << std::endl; + out << " Multi-objective Query " << std::endl; + out << "---------------------------------------------------------------------------------------------------------------------------------------" << std::endl; + out << std::endl; + out << "Original Formula: " << std::endl; + out << "--------------------------------------------------------------" << std::endl; + out << "\t" << originalFormula << std::endl; + out << std::endl; + out << "Objectives:" << std::endl; + out << "--------------------------------------------------------------" << std::endl; + for (auto const& obj : objectives) { + obj.printToStream(out); + } + out << "--------------------------------------------------------------" << std::endl; + out << std::endl; + out << "Original Model Information:" << std::endl; + originalModel.printModelInformationToStream(out); + out << std::endl; + out << "Preprocessed Model Information:" << std::endl; + preprocessedModel.printModelInformationToStream(out); + out << std::endl; + out << "---------------------------------------------------------------------------------------------------------------------------------------" << std::endl; + } + + friend std::ostream& operator<<(std::ostream& out, SparsePcaaPreprocessorReturnType const& ret) { + ret.printToStream(out); + return out; + } + + }; + } + } +} + +#endif /* STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEPCAAPREPROCESSORRETURNTYPE_H_ */ diff --git a/src/modelchecker/multiobjective/pcaa/SparsePcaaAchievabilityQuery.cpp b/src/modelchecker/multiobjective/pcaa/SparsePcaaAchievabilityQuery.cpp new file mode 100644 index 000000000..fa0791afb --- /dev/null +++ b/src/modelchecker/multiobjective/pcaa/SparsePcaaAchievabilityQuery.cpp @@ -0,0 +1,112 @@ +#include "src/modelchecker/multiobjective/pcaa/SparsePcaaAchievabilityQuery.h" + +#include "src/adapters/CarlAdapter.h" +#include "src/models/sparse/Mdp.h" +#include "src/models/sparse/MarkovAutomaton.h" +#include "src/models/sparse/StandardRewardModel.h" +#include "src/modelchecker/results/ExplicitQualitativeCheckResult.h" +#include "src/utility/constants.h" +#include "src/utility/vector.h" +#include "src/settings//SettingsManager.h" +#include "src/settings/modules/GeneralSettings.h" +#include "src/settings/modules/MultiObjectiveSettings.h" + +namespace storm { + namespace modelchecker { + namespace multiobjective { + + + template + SparsePcaaAchievabilityQuery::SparsePcaaAchievabilityQuery(SparsePcaaPreprocessorReturnType& preprocessorResult) : SparsePcaaQuery(preprocessorResult) { + STORM_LOG_ASSERT(preprocessorResult.queryType==SparsePcaaPreprocessorReturnType::QueryType::Achievability, "Invalid query Type"); + initializeThresholdData(); + + // Set the precision of the weight vector checker. Will be refined during the computation + this->weightVectorChecker->setWeightedPrecision(storm::utility::convertNumber(0.1)); + } + + template + void SparsePcaaAchievabilityQuery::initializeThresholdData() { + thresholds.reserve(this->objectives.size()); + strictThresholds = storm::storage::BitVector(this->objectives.size(), false); + for(uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) { + thresholds.push_back(storm::utility::convertNumber(*this->objectives[objIndex].threshold)); + strictThresholds.set(objIndex, this->objectives[objIndex].thresholdIsStrict); + } + } + + template + std::unique_ptr SparsePcaaAchievabilityQuery::check() { + + bool result = this->checkAchievability(); + + return std::unique_ptr(new ExplicitQualitativeCheckResult(this->originalModel.getInitialStates().getNextSetIndex(0), result)); + + } + + template + bool SparsePcaaAchievabilityQuery::checkAchievability() { + // repeatedly refine the over/ under approximation until the threshold point is either in the under approx. or not in the over approx. + while(!this->maxStepsPerformed()){ + WeightVector separatingVector = this->findSeparatingVector(thresholds); + this->updateWeightedPrecision(separatingVector); + this->performRefinementStep(std::move(separatingVector)); + if(!checkIfThresholdsAreSatisfied(this->overApproximation)){ + return false; + } + if(checkIfThresholdsAreSatisfied(this->underApproximation)){ + return true; + } + } + STORM_LOG_ERROR("Could not check whether thresholds are achievable: Exceeded maximum number of refinement steps"); + return false; + } + + template + void SparsePcaaAchievabilityQuery::updateWeightedPrecision(WeightVector const& weights) { + // Our heuristic considers the distance between the under- and the over approximation w.r.t. the given direction + std::pair optimizationResOverApprox = this->overApproximation->optimize(weights); + if(optimizationResOverApprox.second) { + std::pair optimizationResUnderApprox = this->underApproximation->optimize(weights); + if(optimizationResUnderApprox.second) { + GeometryValueType distance = storm::utility::vector::dotProduct(optimizationResOverApprox.first, weights) - storm::utility::vector::dotProduct(optimizationResUnderApprox.first, weights); + STORM_LOG_ASSERT(distance >= storm::utility::zero(), "Negative distance between under- and over approximation was not expected"); + // Normalize the distance by dividing it with the Euclidean Norm of the weight-vector + distance /= storm::utility::sqrt(storm::utility::vector::dotProduct(weights, weights)); + distance /= GeometryValueType(2); + this->weightVectorChecker->setWeightedPrecision(storm::utility::convertNumber(distance)); + } + } + // do not update the precision if one of the approximations is unbounded in the provided direction + } + + template + bool SparsePcaaAchievabilityQuery::checkIfThresholdsAreSatisfied(std::shared_ptr> const& polytope) { + std::vector> halfspaces = polytope->getHalfspaces(); + for(auto const& h : halfspaces) { + if(storm::utility::isZero(h.distance(thresholds))) { + // Check if the threshold point is on the boundary of the halfspace and whether this is violates strict thresholds + if(h.isPointOnBoundary(thresholds)) { + for(auto strictThreshold : strictThresholds) { + if(h.normalVector()[strictThreshold] > storm::utility::zero()) { + return false; + } + } + } + } else { + return false; + } + } + return true; + } + +#ifdef STORM_HAVE_CARL + template class SparsePcaaAchievabilityQuery, storm::RationalNumber>; + template class SparsePcaaAchievabilityQuery, storm::RationalNumber>; + + template class SparsePcaaAchievabilityQuery, storm::RationalNumber>; + // template class SparsePcaaAchievabilityQuery, storm::RationalNumber>; +#endif + } + } +} diff --git a/src/modelchecker/multiobjective/pcaa/SparsePcaaAchievabilityQuery.h b/src/modelchecker/multiobjective/pcaa/SparsePcaaAchievabilityQuery.h new file mode 100644 index 000000000..638e2174c --- /dev/null +++ b/src/modelchecker/multiobjective/pcaa/SparsePcaaAchievabilityQuery.h @@ -0,0 +1,63 @@ +#ifndef STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEPCAAACHIEVABILITYQUERY_H_ +#define STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEPCAAACHIEVABILITYQUERY_H_ + +#include "src/modelchecker/multiobjective/pcaa/SparsePcaaQuery.h" + +namespace storm { + namespace modelchecker { + namespace multiobjective { + + /* + * This class represents a query for the Pareto curve approximation algorithm (Pcaa). + * It implements the necessary computations for the different query types. + */ + template + class SparsePcaaAchievabilityQuery : public SparsePcaaQuery { + public: + + // Typedefs for simple geometric objects + typedef std::vector Point; + typedef std::vector WeightVector; + + /* + * Creates a new query for the Pareto curve approximation algorithm (Pcaa) + * @param preprocessorResult the result from preprocessing + */ + SparsePcaaAchievabilityQuery(SparsePcaaPreprocessorReturnType& preprocessorResult); + + + /* + * Invokes the computation and retrieves the result + */ + virtual std::unique_ptr check() override; + + private: + + void initializeThresholdData(); + + /* + * Returns whether the given thresholds are achievable. + */ + bool checkAchievability(); + + /* + * Updates the precision of the weightVectorChecker w.r.t. the provided weights + */ + void updateWeightedPrecision(WeightVector const& weights); + + /* + * Returns true iff there is one 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> const& polytope); + + + Point thresholds; + storm::storage::BitVector strictThresholds; + }; + + } + } +} + +#endif /* STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEPCAAACHIEVABILITYQUERY_H_ */ diff --git a/src/modelchecker/multiobjective/pcaa/SparsePcaaParetoQuery.cpp b/src/modelchecker/multiobjective/pcaa/SparsePcaaParetoQuery.cpp new file mode 100644 index 000000000..e8746441f --- /dev/null +++ b/src/modelchecker/multiobjective/pcaa/SparsePcaaParetoQuery.cpp @@ -0,0 +1,98 @@ +#include "src/modelchecker/multiobjective/pcaa/SparsePcaaParetoQuery.h" + +#include "src/adapters/CarlAdapter.h" +#include "src/models/sparse/Mdp.h" +#include "src/models/sparse/MarkovAutomaton.h" +#include "src/models/sparse/StandardRewardModel.h" +#include "src/modelchecker/results/ParetoCurveCheckResult.h" +#include "src/utility/constants.h" +#include "src/utility/vector.h" +#include "src/settings//SettingsManager.h" +#include "src/settings/modules/MultiObjectiveSettings.h" +#include "src/settings/modules/GeneralSettings.h" + + +namespace storm { + namespace modelchecker { + namespace multiobjective { + + template + SparsePcaaParetoQuery::SparsePcaaParetoQuery(SparsePcaaPreprocessorReturnType& preprocessorResult) : SparsePcaaQuery(preprocessorResult) { + STORM_LOG_ASSERT(preprocessorResult.queryType==SparsePcaaPreprocessorReturnType::QueryType::Pareto, "Invalid query Type"); + + // Set the precision of the weight vector checker + typename SparseModelType::ValueType weightedPrecision = storm::utility::convertNumber(storm::settings::getModule().getPrecision()); + weightedPrecision /= typename SparseModelType::ValueType(2); + this->weightVectorChecker->setWeightedPrecision(weightedPrecision); + + } + + template + std::unique_ptr SparsePcaaParetoQuery::check() { + + // refine the approximation + exploreSetOfAchievablePoints(); + + // obtain the data for the checkresult + std::vector> paretoOptimalPoints; + paretoOptimalPoints.reserve(this->refinementSteps.size()); + for(auto const& step : this->refinementSteps) { + paretoOptimalPoints.push_back(storm::utility::vector::convertNumericVector(this->transformPointToOriginalModel(step.lowerBoundPoint))); + } + return std::unique_ptr(new ParetoCurveCheckResult(this->originalModel.getInitialStates().getNextSetIndex(0), + std::move(paretoOptimalPoints), + this->transformPolytopeToOriginalModel(this->underApproximation)->template convertNumberRepresentation(), + this->transformPolytopeToOriginalModel(this->overApproximation)->template convertNumberRepresentation())); + + + } + + template + void SparsePcaaParetoQuery::exploreSetOfAchievablePoints() { + + //First consider the objectives individually + for(uint_fast64_t objIndex = 0; objIndexobjectives.size() && !this->maxStepsPerformed(); ++objIndex) { + WeightVector direction(this->objectives.size(), storm::utility::zero()); + direction[objIndex] = storm::utility::one(); + this->performRefinementStep(std::move(direction)); + } + + while(!this->maxStepsPerformed()) { + // Get the halfspace of the underApproximation with maximal distance to a vertex of the overApproximation + std::vector> underApproxHalfspaces = this->underApproximation->getHalfspaces(); + std::vector overApproxVertices = this->overApproximation->getVertices(); + uint_fast64_t farestHalfspaceIndex = underApproxHalfspaces.size(); + GeometryValueType farestDistance = storm::utility::zero(); + for(uint_fast64_t halfspaceIndex = 0; halfspaceIndex < underApproxHalfspaces.size(); ++halfspaceIndex) { + for(auto const& vertex : overApproxVertices) { + GeometryValueType distance = underApproxHalfspaces[halfspaceIndex].euclideanDistance(vertex); + if(distance > farestDistance) { + farestHalfspaceIndex = halfspaceIndex; + farestDistance = distance; + } + } + } + if(farestDistance < storm::utility::convertNumber(storm::settings::getModule().getPrecision())) { + // Goal precision reached! + return; + } + STORM_LOG_DEBUG("Current precision of the approximation of the pareto curve is ~" << storm::utility::convertNumber(farestDistance)); + WeightVector direction = underApproxHalfspaces[farestHalfspaceIndex].normalVector(); + this->performRefinementStep(std::move(direction)); + } + STORM_LOG_ERROR("Could not reach the desired precision: Exceeded maximum number of refinement steps"); + } + + + + +#ifdef STORM_HAVE_CARL + template class SparsePcaaParetoQuery, storm::RationalNumber>; + template class SparsePcaaParetoQuery, storm::RationalNumber>; + + template class SparsePcaaParetoQuery, storm::RationalNumber>; + // template class SparsePcaaParetoQuery, storm::RationalNumber>; +#endif + } + } +} diff --git a/src/modelchecker/multiobjective/pcaa/SparsePcaaParetoQuery.h b/src/modelchecker/multiobjective/pcaa/SparsePcaaParetoQuery.h new file mode 100644 index 000000000..bb47d5158 --- /dev/null +++ b/src/modelchecker/multiobjective/pcaa/SparsePcaaParetoQuery.h @@ -0,0 +1,47 @@ +#ifndef STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEPCAAPARETOQUERY_H_ +#define STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEPCAAPARETOQUERY_H_ + +#include "src/modelchecker/multiobjective/pcaa/SparsePcaaQuery.h" + +namespace storm { + namespace modelchecker { + namespace multiobjective { + + /* + * This class represents a query for the Pareto curve approximation algorithm (Pcaa). + * It implements the necessary computations for the different query types. + */ + template + class SparsePcaaParetoQuery : public SparsePcaaQuery { + public: + + // Typedefs for simple geometric objects + typedef std::vector Point; + typedef std::vector WeightVector; + + /* + * Creates a new query for the Pareto curve approximation algorithm (Pcaa) + * @param preprocessorResult the result from preprocessing + */ + SparsePcaaParetoQuery(SparsePcaaPreprocessorReturnType& preprocessorResult); + + + /* + * Invokes the computation and retrieves the result + */ + virtual std::unique_ptr check() override; + + private: + + + /* + * Performs refinement steps until the approximation is sufficiently precise + */ + void exploreSetOfAchievablePoints(); + }; + + } + } +} + +#endif /* STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEPCAAPARETOQUERY_H_ */ diff --git a/src/modelchecker/multiobjective/pcaa/SparsePcaaQuantitativeQuery.cpp b/src/modelchecker/multiobjective/pcaa/SparsePcaaQuantitativeQuery.cpp new file mode 100644 index 000000000..05b83b443 --- /dev/null +++ b/src/modelchecker/multiobjective/pcaa/SparsePcaaQuantitativeQuery.cpp @@ -0,0 +1,198 @@ +#include "src/modelchecker/multiobjective/pcaa/SparsePcaaQuantitativeQuery.h" + +#include "src/adapters/CarlAdapter.h" +#include "src/models/sparse/Mdp.h" +#include "src/models/sparse/MarkovAutomaton.h" +#include "src/models/sparse/StandardRewardModel.h" +#include "src/modelchecker/results/ExplicitQualitativeCheckResult.h" +#include "src/modelchecker/results/ExplicitQuantitativeCheckResult.h" +#include "src/utility/constants.h" +#include "src/utility/vector.h" +#include "src/settings//SettingsManager.h" +#include "src/settings/modules/MultiObjectiveSettings.h" +#include "src/settings/modules/GeneralSettings.h" + +namespace storm { + namespace modelchecker { + namespace multiobjective { + + + template + SparsePcaaQuantitativeQuery::SparsePcaaQuantitativeQuery(SparsePcaaPreprocessorReturnType& preprocessorResult) : SparsePcaaQuery(preprocessorResult) { + STORM_LOG_ASSERT(preprocessorResult.queryType==SparsePcaaPreprocessorReturnType::QueryType::Quantitative, "Invalid query Type"); + STORM_LOG_ASSERT(preprocessorResult.indexOfOptimizingObjective, "Detected quantitative query but index of optimizing objective is not set."); + indexOfOptimizingObjective = *preprocessorResult.indexOfOptimizingObjective; + initializeThresholdData(); + + // Set the maximum distance between lower and upper bound of the weightVectorChecker result. + this->weightVectorChecker->setWeightedPrecision(storm::utility::convertNumber(0.1)); + } + + template + void SparsePcaaQuantitativeQuery::initializeThresholdData() { + thresholds.reserve(this->objectives.size()); + strictThresholds = storm::storage::BitVector(this->objectives.size(), false); + std::vector> thresholdConstraints; + thresholdConstraints.reserve(this->objectives.size()-1); + for(uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) { + if(this->objectives[objIndex].threshold) { + thresholds.push_back(storm::utility::convertNumber(*this->objectives[objIndex].threshold)); + WeightVector normalVector(this->objectives.size(), storm::utility::zero()); + normalVector[objIndex] = -storm::utility::one(); + thresholdConstraints.emplace_back(std::move(normalVector), -thresholds.back()); + strictThresholds.set(objIndex, this->objectives[objIndex].thresholdIsStrict); + } else { + thresholds.push_back(storm::utility::zero()); + } + } + // Note: If we have a single objective (i.e., no objectives with thresholds), thresholdsAsPolytope gets no constraints + thresholdsAsPolytope = storm::storage::geometry::Polytope::create(thresholdConstraints); + } + + template + std::unique_ptr SparsePcaaQuantitativeQuery::check() { + + + // First find one solution that achieves the given thresholds ... + if(this->checkAchievability()) { + // ... then improve it + GeometryValueType result = this->improveSolution(); + + // transform the obtained result for the preprocessed model to a result w.r.t. the original model and return the checkresult + typename SparseModelType::ValueType resultForOriginalModel = + storm::utility::convertNumber(result) * + this->objectives[indexOfOptimizingObjective].toOriginalValueTransformationFactor + + this->objectives[indexOfOptimizingObjective].toOriginalValueTransformationOffset; + return std::unique_ptr(new ExplicitQuantitativeCheckResult(this->originalModel.getInitialStates().getNextSetIndex(0), resultForOriginalModel)); + } else { + return std::unique_ptr(new ExplicitQualitativeCheckResult(this->originalModel.getInitialStates().getNextSetIndex(0), false)); + } + + + } + + template + bool SparsePcaaQuantitativeQuery::checkAchievability() { + // We don't care for the optimizing objective at this point + this->diracWeightVectorsToBeChecked.set(indexOfOptimizingObjective, false); + + while(!this->maxStepsPerformed()){ + WeightVector separatingVector = this->findSeparatingVector(thresholds); + this->updateWeightedPrecisionInAchievabilityPhase(separatingVector); + this->performRefinementStep(std::move(separatingVector)); + //Pick the threshold for the optimizing objective low enough so valid solutions are not excluded + thresholds[indexOfOptimizingObjective] = std::min(thresholds[indexOfOptimizingObjective], this->refinementSteps.back().lowerBoundPoint[indexOfOptimizingObjective]); + if(!checkIfThresholdsAreSatisfied(this->overApproximation)){ + return false; + } + if(checkIfThresholdsAreSatisfied(this->underApproximation)){ + return true; + } + } + STORM_LOG_ERROR("Could not check whether thresholds are achievable: Exceeded maximum number of refinement steps"); + return false; + } + + template + void SparsePcaaQuantitativeQuery::updateWeightedPrecisionInAchievabilityPhase(WeightVector const& weights) { + // Our heuristic considers the distance between the under- and the over approximation w.r.t. the given direction + std::pair optimizationResOverApprox = this->overApproximation->optimize(weights); + if(optimizationResOverApprox.second) { + std::pair optimizationResUnderApprox = this->underApproximation->optimize(weights); + if(optimizationResUnderApprox.second) { + GeometryValueType distance = storm::utility::vector::dotProduct(optimizationResOverApprox.first, weights) - storm::utility::vector::dotProduct(optimizationResUnderApprox.first, weights); + STORM_LOG_ASSERT(distance >= storm::utility::zero(), "Negative distance between under- and over approximation was not expected"); + // Normalize the distance by dividing it with the Euclidean Norm of the weight-vector + distance /= storm::utility::sqrt(storm::utility::vector::dotProduct(weights, weights)); + distance /= GeometryValueType(2); + this->weightVectorChecker->setWeightedPrecision(storm::utility::convertNumber(distance)); + } + } + // do not update the precision if one of the approximations is unbounded in the provided direction + } + + template + GeometryValueType SparsePcaaQuantitativeQuery::improveSolution() { + this->diracWeightVectorsToBeChecked.clear(); // Only check weight vectors that can actually improve the solution + + WeightVector directionOfOptimizingObjective(this->objectives.size(), storm::utility::zero()); + directionOfOptimizingObjective[indexOfOptimizingObjective] = storm::utility::one(); + + // Improve the found solution. + // Note that we do not 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. + GeometryValueType result = storm::utility::zero(); + while(!this->maxStepsPerformed()) { + std::pair optimizationRes = this->underApproximation->intersection(thresholdsAsPolytope)->optimize(directionOfOptimizingObjective); + STORM_LOG_THROW(optimizationRes.second, storm::exceptions::UnexpectedException, "The underapproximation is either unbounded or empty."); + result = optimizationRes.first[indexOfOptimizingObjective]; + STORM_LOG_DEBUG("Best solution found so far is ~" << storm::utility::convertNumber(result) << "."); + //Compute an upper bound for the optimum and check for convergence + optimizationRes = this->overApproximation->intersection(thresholdsAsPolytope)->optimize(directionOfOptimizingObjective); + if(optimizationRes.second) { + GeometryValueType precisionOfResult = optimizationRes.first[indexOfOptimizingObjective] - result; + if(precisionOfResult < storm::utility::convertNumber(storm::settings::getModule().getPrecision())) { + // Goal precision reached! + return result; + } else { + STORM_LOG_DEBUG("Solution can be improved by at most " << storm::utility::convertNumber(precisionOfResult)); + thresholds[indexOfOptimizingObjective] = optimizationRes.first[indexOfOptimizingObjective]; + } + } else { + thresholds[indexOfOptimizingObjective] = result + storm::utility::one(); + } + WeightVector separatingVector = this->findSeparatingVector(thresholds); + this->updateWeightedPrecisionInImprovingPhase(separatingVector); + this->performRefinementStep(std::move(separatingVector)); + } + STORM_LOG_ERROR("Could not reach the desired precision: Exceeded maximum number of refinement steps"); + return result; + } + + + template + void SparsePcaaQuantitativeQuery::updateWeightedPrecisionInImprovingPhase(WeightVector const& weights) { + STORM_LOG_THROW(!storm::utility::isZero(weights[this->indexOfOptimizingObjective]), exceptions::UnexpectedException, "The chosen weight-vector gives zero weight for the objective that is to be optimized."); + // If weighs[indexOfOptimizingObjective] is low, the computation of the weightVectorChecker needs to be more precise. + // Our heuristic ensures that if p is the new vertex of the under-approximation, then max{ eps | p' = p + (0..0 eps 0..0) is in the over-approximation } <= multiobjective_precision/2 + GeometryValueType weightedPrecision = weights[this->indexOfOptimizingObjective] * storm::utility::convertNumber(storm::settings::getModule().getPrecision()); + // Normalize by division with the Euclidean Norm of the weight-vector + weightedPrecision /= storm::utility::sqrt(storm::utility::vector::dotProduct(weights, weights)); + weightedPrecision /= GeometryValueType(2); + this->weightVectorChecker->setWeightedPrecision(storm::utility::convertNumber(weightedPrecision)); + } + + + template + bool SparsePcaaQuantitativeQuery::checkIfThresholdsAreSatisfied(std::shared_ptr> const& polytope) { + std::vector> halfspaces = polytope->getHalfspaces(); + for(auto const& h : halfspaces) { + if(storm::utility::isZero(h.distance(thresholds))) { + // Check if the threshold point is on the boundary of the halfspace and whether this is violates strict thresholds + if(h.isPointOnBoundary(thresholds)) { + for(auto strictThreshold : strictThresholds) { + if(h.normalVector()[strictThreshold] > storm::utility::zero()) { + return false; + } + } + } + } else { + return false; + } + } + return true; + } + + + +#ifdef STORM_HAVE_CARL + template class SparsePcaaQuantitativeQuery, storm::RationalNumber>; + template class SparsePcaaQuantitativeQuery, storm::RationalNumber>; + + template class SparsePcaaQuantitativeQuery, storm::RationalNumber>; + // template class SparsePcaaQuantitativeQuery, storm::RationalNumber>; +#endif + } + } +} diff --git a/src/modelchecker/multiobjective/pcaa/SparsePcaaQuantitativeQuery.h b/src/modelchecker/multiobjective/pcaa/SparsePcaaQuantitativeQuery.h new file mode 100644 index 000000000..b25cf5c18 --- /dev/null +++ b/src/modelchecker/multiobjective/pcaa/SparsePcaaQuantitativeQuery.h @@ -0,0 +1,71 @@ +#ifndef STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEPCAAQUANTITATIVEQUERY_H_ +#define STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEPCAAQUANTITATIVEQUERY_H_ + +#include "src/modelchecker/multiobjective/pcaa/SparsePcaaQuery.h" + +namespace storm { + namespace modelchecker { + namespace multiobjective { + + /* + * This class represents a query for the Pareto curve approximation algorithm (Pcaa). + * It implements the necessary computations for the different query types. + */ + template + class SparsePcaaQuantitativeQuery : public SparsePcaaQuery { + public: + + // Typedefs for simple geometric objects + typedef std::vector Point; + typedef std::vector WeightVector; + + /* + * Creates a new query for the Pareto curve approximation algorithm (Pcaa) + * @param preprocessorResult the result from preprocessing + */ + SparsePcaaQuantitativeQuery(SparsePcaaPreprocessorReturnType& preprocessorResult); + + + /* + * Invokes the computation and retrieves the result + */ + virtual std::unique_ptr check() override; + + private: + + void initializeThresholdData(); + + /* + * Returns whether the given thresholds are achievable. + */ + bool checkAchievability(); + + /* + * Updates the precision of the weightVectorChecker w.r.t. the provided weights + */ + void updateWeightedPrecisionInAchievabilityPhase(WeightVector const& weights); + void updateWeightedPrecisionInImprovingPhase(WeightVector const& weights); + + /* + * Given that the thresholds are achievable, this function further refines the approximations and returns the optimized value + */ + GeometryValueType improveSolution(); + + /* + * Returns true iff there is one 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> const& polytope); + + uint_fast64_t indexOfOptimizingObjective; + + Point thresholds; + storm::storage::BitVector strictThresholds; + std::shared_ptr> thresholdsAsPolytope; + }; + + } + } +} + +#endif /* STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEPCAAQUANTITATIVEQUERY_H_ */ diff --git a/src/modelchecker/multiobjective/pcaa/SparsePcaaQuery.cpp b/src/modelchecker/multiobjective/pcaa/SparsePcaaQuery.cpp new file mode 100644 index 000000000..f38c01669 --- /dev/null +++ b/src/modelchecker/multiobjective/pcaa/SparsePcaaQuery.cpp @@ -0,0 +1,245 @@ +#include "src/modelchecker/multiobjective/pcaa/SparsePcaaQuery.h" + +#include "src/adapters/CarlAdapter.h" +#include "src/models/sparse/Mdp.h" +#include "src/models/sparse/MarkovAutomaton.h" +#include "src/models/sparse/StandardRewardModel.h" +#include "src/modelchecker/multiobjective/pcaa/PcaaObjective.h" +#include "src/modelchecker/multiobjective/pcaa/SparseMdpPcaaWeightVectorChecker.h" +#include "src/modelchecker/multiobjective/pcaa/SparseMaPcaaWeightVectorChecker.h" +#include "src/settings//SettingsManager.h" +#include "src/settings/modules/MultiObjectiveSettings.h" +#include "src/storage/geometry/Hyperrectangle.h" +#include "src/utility/constants.h" +#include "src/utility/vector.h" +#include "src/utility/export.h" + +#include "src/exceptions/UnexpectedException.h" + +namespace storm { + namespace modelchecker { + namespace multiobjective { + + + template + SparsePcaaQuery::SparsePcaaQuery(SparsePcaaPreprocessorReturnType& preprocessorResult) : + originalModel(preprocessorResult.originalModel), originalFormula(preprocessorResult.originalFormula), + preprocessedModel(std::move(preprocessorResult.preprocessedModel)), objectives(std::move(preprocessorResult.objectives)) { + initializeWeightVectorChecker(preprocessedModel, objectives, preprocessorResult.actionsWithNegativeReward, preprocessorResult.ecActions, preprocessorResult.possiblyRecurrentStates); + this->diracWeightVectorsToBeChecked = storm::storage::BitVector(this->objectives.size(), true); + this->overApproximation = storm::storage::geometry::Polytope::createUniversalPolytope(); + this->underApproximation = storm::storage::geometry::Polytope::createEmptyPolytope(); + } + + template<> + void SparsePcaaQuery, storm::RationalNumber>::initializeWeightVectorChecker(storm::models::sparse::Mdp const& model, std::vector> const& objectives, storm::storage::BitVector const& actionsWithNegativeReward, storm::storage::BitVector const& ecActions, storm::storage::BitVector const& possiblyRecurrentStates) { + this->weightVectorChecker = std::unique_ptr>>(new SparseMdpPcaaWeightVectorChecker>(model, objectives, actionsWithNegativeReward, ecActions, possiblyRecurrentStates)); + } + + template<> + void SparsePcaaQuery, storm::RationalNumber>::initializeWeightVectorChecker(storm::models::sparse::Mdp const& model, std::vector> const& objectives, storm::storage::BitVector const& actionsWithNegativeReward, storm::storage::BitVector const& ecActions, storm::storage::BitVector const& possiblyRecurrentStates) { + this->weightVectorChecker = std::unique_ptr>>(new SparseMdpPcaaWeightVectorChecker>(model, objectives, actionsWithNegativeReward, ecActions, possiblyRecurrentStates)); + } + + template<> + void SparsePcaaQuery, storm::RationalNumber>::initializeWeightVectorChecker(storm::models::sparse::MarkovAutomaton const& model, std::vector> const& objectives, storm::storage::BitVector const& actionsWithNegativeReward, storm::storage::BitVector const& ecActions, storm::storage::BitVector const& possiblyRecurrentStates) { + this->weightVectorChecker = std::unique_ptr>>(new SparseMaPcaaWeightVectorChecker>(model, objectives, actionsWithNegativeReward, ecActions, possiblyRecurrentStates)); + } + + template + typename SparsePcaaQuery::WeightVector SparsePcaaQuery::findSeparatingVector(Point const& pointToBeSeparated) { + STORM_LOG_DEBUG("Searching a weight vector to seperate the point given by " << storm::utility::vector::toString(storm::utility::vector::convertNumericVector(pointToBeSeparated)) << "."); + + if(underApproximation->isEmpty()) { + // In this case, every weight vector is separating + uint_fast64_t objIndex = diracWeightVectorsToBeChecked.getNextSetIndex(0) % pointToBeSeparated.size(); + WeightVector result(pointToBeSeparated.size(), storm::utility::zero()); + result[objIndex] = storm::utility::one(); + diracWeightVectorsToBeChecked.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, Dirac weight vectors that only assign a non-zero weight to a single objective take precedence. + STORM_LOG_ASSERT(!underApproximation->contains(pointToBeSeparated), "Tried to find a separating point but the point is already contained in the underApproximation"); + std::vector> halfspaces = underApproximation->getHalfspaces(); + uint_fast64_t farestHalfspaceIndex = halfspaces.size(); + GeometryValueType farestDistance = -storm::utility::one(); + bool foundSeparatingDiracVector = false; + for(uint_fast64_t halfspaceIndex = 0; halfspaceIndex < halfspaces.size(); ++halfspaceIndex) { + GeometryValueType distance = halfspaces[halfspaceIndex].euclideanDistance(pointToBeSeparated); + if(!storm::utility::isZero(distance)) { + storm::storage::BitVector nonZeroVectorEntries = ~storm::utility::vector::filterZero(halfspaces[halfspaceIndex].normalVector()); + bool isSingleObjectiveVector = nonZeroVectorEntries.getNumberOfSetBits() == 1 && diracWeightVectorsToBeChecked.get(nonZeroVectorEntries.getNextSetIndex(0)); + // Check if this halfspace is a better candidate than the current one + if((!foundSeparatingDiracVector && isSingleObjectiveVector ) || (foundSeparatingDiracVector==isSingleObjectiveVector && distance>farestDistance)) { + foundSeparatingDiracVector = foundSeparatingDiracVector || isSingleObjectiveVector; + farestHalfspaceIndex = halfspaceIndex; + farestDistance = distance; + } + } + } + if(foundSeparatingDiracVector) { + diracWeightVectorsToBeChecked &= storm::utility::vector::filterZero(halfspaces[farestHalfspaceIndex].normalVector()); + } + + STORM_LOG_THROW(farestHalfspaceIndex(halfspaces[farestHalfspaceIndex].normalVector())) << "."); + return halfspaces[farestHalfspaceIndex].normalVector(); + } + + template + void SparsePcaaQuery::performRefinementStep(WeightVector&& direction) { + // Normalize the direction vector so that the entries sum up to one + storm::utility::vector::scaleVectorInPlace(direction, storm::utility::one() / std::accumulate(direction.begin(), direction.end(), storm::utility::zero())); + weightVectorChecker->check(storm::utility::vector::convertNumericVector(direction)); + STORM_LOG_DEBUG("weighted objectives checker result (lower bounds) is " << storm::utility::vector::toString(storm::utility::vector::convertNumericVector(weightVectorChecker->getLowerBoundsOfInitialStateResults()))); + RefinementStep step; + step.weightVector = direction; + step.lowerBoundPoint = storm::utility::vector::convertNumericVector(weightVectorChecker->getLowerBoundsOfInitialStateResults()); + step.upperBoundPoint = storm::utility::vector::convertNumericVector(weightVectorChecker->getUpperBoundsOfInitialStateResults()); + refinementSteps.push_back(std::move(step)); + + updateOverApproximation(); + updateUnderApproximation(); + } + + template + void SparsePcaaQuery::updateOverApproximation() { + storm::storage::geometry::Halfspace h(refinementSteps.back().weightVector, storm::utility::vector::dotProduct(refinementSteps.back().weightVector, refinementSteps.back().upperBoundPoint)); + + // 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. + GeometryValueType maximumOffset = h.offset(); + for(auto const& step : refinementSteps){ + maximumOffset = std::max(maximumOffset, storm::utility::vector::dotProduct(h.normalVector(), step.lowerBoundPoint)); + } + 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(h.invert().euclideanDistance(refinementSteps.back().upperBoundPoint)) << "."); + } + overApproximation = overApproximation->intersection(h); + STORM_LOG_DEBUG("Updated OverApproximation to " << overApproximation->toString(true)); + } + + template + void SparsePcaaQuery::updateUnderApproximation() { + std::vector paretoPoints; + paretoPoints.reserve(refinementSteps.size()); + for(auto const& step : refinementSteps) { + paretoPoints.push_back(step.lowerBoundPoint); + } + underApproximation = storm::storage::geometry::Polytope::createDownwardClosure(paretoPoints); + STORM_LOG_DEBUG("Updated UnderApproximation to " << underApproximation->toString(true)); + } + + template + bool SparsePcaaQuery::maxStepsPerformed() const { + return storm::settings::getModule().isMaxStepsSet() && + this->refinementSteps.size() >= storm::settings::getModule().getMaxSteps(); + } + + + template + typename SparsePcaaQuery::Point SparsePcaaQuery::transformPointToOriginalModel(Point const& point) const { + Point result; + result.reserve(point.size()); + for(uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) { + result.push_back(point[objIndex] * storm::utility::convertNumber(this->objectives[objIndex].toOriginalValueTransformationFactor) + storm::utility::convertNumber(this->objectives[objIndex].toOriginalValueTransformationOffset)); + } + return result; + } + + template + std::shared_ptr> SparsePcaaQuery::transformPolytopeToOriginalModel(std::shared_ptr> const& polytope) const { + if(polytope->isEmpty()) { + return storm::storage::geometry::Polytope::createEmptyPolytope(); + } + if(polytope->isUniversal()) { + return storm::storage::geometry::Polytope::createUniversalPolytope(); + } + uint_fast64_t numObjectives = this->objectives.size(); + std::vector> transformationMatrix(numObjectives, std::vector(numObjectives, storm::utility::zero())); + std::vector transformationVector; + transformationVector.reserve(numObjectives); + for(uint_fast64_t objIndex = 0; objIndex < numObjectives; ++objIndex) { + transformationMatrix[objIndex][objIndex] = storm::utility::convertNumber(this->objectives[objIndex].toOriginalValueTransformationFactor); + transformationVector.push_back(storm::utility::convertNumber(this->objectives[objIndex].toOriginalValueTransformationOffset)); + } + return polytope->affineTransformation(transformationMatrix, transformationVector); + } + + template + void SparsePcaaQuery::exportPlotOfCurrentApproximation(std::string const& destinationDir) const { + + STORM_LOG_ERROR_COND(objectives.size()==2, "Exporting plot requested but this is only implemented for the two-dimensional case."); + + auto transformedUnderApprox = transformPolytopeToOriginalModel(underApproximation); + auto transformedOverApprox = transformPolytopeToOriginalModel(overApproximation); + + // Get pareto points as well as a hyperrectangle that is used to guarantee that the resulting polytopes are bounded. + storm::storage::geometry::Hyperrectangle boundaries(std::vector(objectives.size(), storm::utility::zero()), std::vector(objectives.size(), storm::utility::zero())); + std::vector> paretoPoints; + paretoPoints.reserve(refinementSteps.size()); + for(auto const& step : refinementSteps) { + paretoPoints.push_back(transformPointToOriginalModel(step.lowerBoundPoint)); + boundaries.enlarge(paretoPoints.back()); + } + auto underApproxVertices = transformedUnderApprox->getVertices(); + for(auto const& v : underApproxVertices) { + boundaries.enlarge(v); + } + auto overApproxVertices = transformedOverApprox->getVertices(); + for(auto const& v : overApproxVertices) { + boundaries.enlarge(v); + } + + //Further enlarge the boundaries a little + storm::utility::vector::scaleVectorInPlace(boundaries.lowerBounds(), GeometryValueType(15) / GeometryValueType(10)); + storm::utility::vector::scaleVectorInPlace(boundaries.upperBounds(), GeometryValueType(15) / GeometryValueType(10)); + + auto boundariesAsPolytope = boundaries.asPolytope(); + std::vector columnHeaders = {"x", "y"}; + + std::vector> pointsForPlotting; + underApproxVertices = transformedUnderApprox->intersection(boundariesAsPolytope)->getVerticesInClockwiseOrder(); + pointsForPlotting.reserve(underApproxVertices.size()); + for(auto const& v : underApproxVertices) { + pointsForPlotting.push_back(storm::utility::vector::convertNumericVector(v)); + } + storm::utility::exportDataToCSVFile(destinationDir + "underapproximation.csv", pointsForPlotting, columnHeaders); + + pointsForPlotting.clear(); + overApproxVertices = transformedOverApprox->intersection(boundariesAsPolytope)->getVerticesInClockwiseOrder(); + pointsForPlotting.reserve(overApproxVertices.size()); + for(auto const& v : overApproxVertices) { + pointsForPlotting.push_back(storm::utility::vector::convertNumericVector(v)); + } + storm::utility::exportDataToCSVFile(destinationDir + "overapproximation.csv", pointsForPlotting, columnHeaders); + + pointsForPlotting.clear(); + pointsForPlotting.reserve(paretoPoints.size()); + for(auto const& v : paretoPoints) { + pointsForPlotting.push_back(storm::utility::vector::convertNumericVector(v)); + } + storm::utility::exportDataToCSVFile(destinationDir + "paretopoints.csv", pointsForPlotting, columnHeaders); + + pointsForPlotting.clear(); + auto boundVertices = boundariesAsPolytope->getVerticesInClockwiseOrder(); + pointsForPlotting.reserve(4); + for(auto const& v : boundVertices) { + pointsForPlotting.push_back(storm::utility::vector::convertNumericVector(v)); + } + storm::utility::exportDataToCSVFile(destinationDir + "boundaries.csv", pointsForPlotting, columnHeaders); + } + +#ifdef STORM_HAVE_CARL + template class SparsePcaaQuery, storm::RationalNumber>; + template class SparsePcaaQuery, storm::RationalNumber>; + + template class SparsePcaaQuery, storm::RationalNumber>; +#endif + } + } +} diff --git a/src/modelchecker/multiobjective/pcaa/SparsePcaaQuery.h b/src/modelchecker/multiobjective/pcaa/SparsePcaaQuery.h new file mode 100644 index 000000000..962686d71 --- /dev/null +++ b/src/modelchecker/multiobjective/pcaa/SparsePcaaQuery.h @@ -0,0 +1,130 @@ +#ifndef STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEPCAAQUERY_H_ +#define STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEPCAAQUERY_H_ + +#include "src/modelchecker/results/CheckResult.h" +#include "src/modelchecker/multiobjective/pcaa/SparsePcaaPreprocessorReturnType.h" +#include "src/modelchecker/multiobjective/pcaa/SparsePcaaWeightVectorChecker.h" +#include "src/storage/geometry/Polytope.h" + +namespace storm { + namespace modelchecker { + namespace multiobjective { + + /* + * This class represents a query for the Pareto curve approximation algorithm (Pcaa). + * It implements the necessary computations for the different query types. + */ + template + class SparsePcaaQuery { + public: + + // Typedefs for simple geometric objects + typedef std::vector Point; + typedef std::vector WeightVector; + + /* + * Invokes the computation and retrieves the result + */ + virtual std::unique_ptr check() = 0; + + /* + * Exports the current approximations and the currently processed points into respective .csv files located at the given directory. + * The polytopes are represented as the set of vertices. + * Note that the approximations will be intersected with a (sufficiently large) hyperrectangle in order to ensure that the polytopes are bounded + * This only works for 2 dimensional queries. + */ + void exportPlotOfCurrentApproximation(std::string const& destinationDir) const; + + protected: + + /* + * Initializes the weight vector checker with the provided data from preprocessing + */ + void initializeWeightVectorChecker(SparseModelType const& model, + std::vector> const& objectives, + storm::storage::BitVector const& actionsWithNegativeReward, + storm::storage::BitVector const& ecActions, + storm::storage::BitVector const& possiblyRecurrentStates); + + /* + * Represents the information obtained in a single iteration of the algorithm + */ + struct RefinementStep { + WeightVector weightVector; + Point lowerBoundPoint; + Point upperBoundPoint; + }; + + /* + * Creates a new query for the Pareto curve approximation algorithm (Pcaa) + * @param preprocessorResult the result from preprocessing + */ + SparsePcaaQuery(SparsePcaaPreprocessorReturnType& preprocessorResult); + + /* + * 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 + */ + WeightVector findSeparatingVector(Point const& pointToBeSeparated); + + /* + * Refines the current result w.r.t. the given direction vector. + */ + void performRefinementStep(WeightVector&& direction); + + /* + * Updates the overapproximation after a refinement step has been performed + * + * @note The last entry of this->refinementSteps should be the newest step whose information is not yet included in the approximation. + */ + void updateOverApproximation(); + + /* + * Updates the underapproximation after a refinement step has been performed + * + * @note The last entry of this->refinementSteps should be the newest step whose information is not yet included in the approximation. + */ + void updateUnderApproximation(); + + /* + * Returns true iff the maximum number of refinement steps (as possibly specified in the settings) has been reached + */ + bool maxStepsPerformed() const; + + /* + * Transforms the given point (or polytope) to values w.r.t. the original model (e.g. negates negative rewards for minimizing objectives). + */ + Point transformPointToOriginalModel(Point const& polytope) const; + std::shared_ptr> transformPolytopeToOriginalModel(std::shared_ptr> const& polytope) const; + + + SparseModelType const& originalModel; + storm::logic::MultiObjectiveFormula const& originalFormula; + + SparseModelType preprocessedModel; + std::vector> objectives; + + + // The corresponding weight vector checker + std::unique_ptr> weightVectorChecker; + + //The results in each iteration of the algorithm + std::vector refinementSteps; + //Overapproximation of the set of achievable values + std::shared_ptr> overApproximation; + //Underapproximation of the set of achievable values + std::shared_ptr> underApproximation; + + // stores for each objective whether it still makes sense to check for this objective individually (i.e., with weight vector given by w_{i}>0 iff i=objIndex ) + storm::storage::BitVector diracWeightVectorsToBeChecked; + + + }; + + } + } +} + +#endif /* STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEPCAAQUERY_H_ */ diff --git a/src/modelchecker/multiobjective/pcaa/SparsePcaaWeightVectorChecker.cpp b/src/modelchecker/multiobjective/pcaa/SparsePcaaWeightVectorChecker.cpp new file mode 100644 index 000000000..9da82e9fa --- /dev/null +++ b/src/modelchecker/multiobjective/pcaa/SparsePcaaWeightVectorChecker.cpp @@ -0,0 +1,317 @@ +#include "src/modelchecker/multiobjective/pcaa/SparsePcaaWeightVectorChecker.h" + +#include + +#include "src/adapters/CarlAdapter.h" +#include "src/models/sparse/Mdp.h" +#include "src/models/sparse/MarkovAutomaton.h" +#include "src/models/sparse/StandardRewardModel.h" +#include "src/modelchecker/prctl/helper/SparseDtmcPrctlHelper.h" +#include "src/solver/MinMaxLinearEquationSolver.h" +#include "src/transformer/EndComponentEliminator.h" +#include "src/utility/graph.h" +#include "src/utility/macros.h" +#include "src/utility/vector.h" + +#include "src/exceptions/IllegalFunctionCallException.h" +#include "src/exceptions/UnexpectedException.h" +#include "src/exceptions/NotImplementedException.h" + +namespace storm { + namespace modelchecker { + namespace multiobjective { + + + template + SparsePcaaWeightVectorChecker::SparsePcaaWeightVectorChecker(SparseModelType const& model, + std::vector> const& objectives, + storm::storage::BitVector const& actionsWithNegativeReward, + storm::storage::BitVector const& ecActions, + storm::storage::BitVector const& possiblyRecurrentStates) : + model(model), + objectives(objectives), + actionsWithNegativeReward(actionsWithNegativeReward), + ecActions(ecActions), + possiblyRecurrentStates(possiblyRecurrentStates), + objectivesWithNoUpperTimeBound(objectives.size()), + discreteActionRewards(objectives.size()), + checkHasBeenCalled(false), + objectiveResults(objectives.size()), + offsetsToLowerBound(objectives.size()), + offsetsToUpperBound(objectives.size()) { + + // set the unbounded objectives + for(uint_fast64_t objIndex = 0; objIndex < objectives.size(); ++objIndex) { + objectivesWithNoUpperTimeBound.set(objIndex, !objectives[objIndex].upperTimeBound); + } + } + + + template + void SparsePcaaWeightVectorChecker::check(std::vector const& weightVector) { + checkHasBeenCalled=true; + STORM_LOG_DEBUG("Invoked WeightVectorChecker with weights " << std::endl << "\t" << storm::utility::vector::toString(storm::utility::vector::convertNumericVector(weightVector))); + std::vector weightedRewardVector(model.getTransitionMatrix().getRowCount(), storm::utility::zero()); + for(auto objIndex : objectivesWithNoUpperTimeBound) { + storm::utility::vector::addScaledVector(weightedRewardVector, discreteActionRewards[objIndex], weightVector[objIndex]); + } + unboundedWeightedPhase(weightedRewardVector); + unboundedIndividualPhase(weightVector); + // Only invoke boundedPhase if necessarry, i.e., if there is at least one objective with a time bound + for(auto const& obj : this->objectives) { + if(obj.lowerTimeBound || obj.upperTimeBound) { + boundedPhase(weightVector, weightedRewardVector); + break; + } + } + STORM_LOG_DEBUG("Weight vector check done. Lower bounds for results in initial state: " << storm::utility::vector::toString(storm::utility::vector::convertNumericVector(getLowerBoundsOfInitialStateResults()))); + // Validate that the results are sufficiently precise + ValueType resultingWeightedPrecision = storm::utility::vector::dotProduct(getUpperBoundsOfInitialStateResults(), weightVector) - storm::utility::vector::dotProduct(getLowerBoundsOfInitialStateResults(), weightVector); + STORM_LOG_THROW(resultingWeightedPrecision >= storm::utility::zero(), storm::exceptions::UnexpectedException, "The distance between the lower and the upper result is negative."); + resultingWeightedPrecision /= storm::utility::sqrt(storm::utility::vector::dotProduct(weightVector, weightVector)); + STORM_LOG_THROW(resultingWeightedPrecision <= weightedPrecision, storm::exceptions::UnexpectedException, "The desired precision was not reached"); + } + + template + void SparsePcaaWeightVectorChecker::setWeightedPrecision(ValueType const& weightedPrecision) { + this->weightedPrecision = weightedPrecision; + } + + template + typename SparsePcaaWeightVectorChecker::ValueType const& SparsePcaaWeightVectorChecker::getWeightedPrecision() const { + return this->weightedPrecision; + } + + template + std::vector::ValueType> SparsePcaaWeightVectorChecker::getLowerBoundsOfInitialStateResults() const { + STORM_LOG_THROW(checkHasBeenCalled, storm::exceptions::IllegalFunctionCallException, "Tried to retrieve results but check(..) has not been called before."); + uint_fast64_t initstate = *this->model.getInitialStates().begin(); + std::vector res; + res.reserve(this->objectives.size()); + for(uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) { + res.push_back(this->objectiveResults[objIndex][initstate] + this->offsetsToLowerBound[objIndex]); + } + return res; + } + + template + std::vector::ValueType> SparsePcaaWeightVectorChecker::getUpperBoundsOfInitialStateResults() const { + STORM_LOG_THROW(checkHasBeenCalled, storm::exceptions::IllegalFunctionCallException, "Tried to retrieve results but check(..) has not been called before."); + uint_fast64_t initstate = *this->model.getInitialStates().begin(); + std::vector res; + res.reserve(this->objectives.size()); + for(uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) { + res.push_back(this->objectiveResults[objIndex][initstate] + this->offsetsToUpperBound[objIndex]); + } + return res; + } + + template + storm::storage::TotalScheduler const& SparsePcaaWeightVectorChecker::getScheduler() const { + STORM_LOG_THROW(this->checkHasBeenCalled, storm::exceptions::IllegalFunctionCallException, "Tried to retrieve results but check(..) has not been called before."); + for(auto const& obj : this->objectives) { + STORM_LOG_THROW(!obj.lowerTimeBound && !obj.upperTimeBound, storm::exceptions::NotImplementedException, "Scheduler retrival is not implemented for timeBounded objectives."); + } + return scheduler; + } + + template + void SparsePcaaWeightVectorChecker::unboundedWeightedPhase(std::vector const& weightedRewardVector) { + if(this->objectivesWithNoUpperTimeBound.empty()) { + this->weightedResult = std::vector(model.getNumberOfStates(), storm::utility::zero()); + this->scheduler = storm::storage::TotalScheduler(model.getNumberOfStates()); + return; + } + + + // Only consider the states from which a transition with non-zero reward is reachable. (The remaining states always have reward zero). + storm::storage::BitVector zeroRewardActions = storm::utility::vector::filterZero(weightedRewardVector); + storm::storage::BitVector nonZeroRewardActions = ~zeroRewardActions; + storm::storage::BitVector nonZeroRewardStates(model.getNumberOfStates(), false); + for(uint_fast64_t state = 0; state < model.getNumberOfStates(); ++state){ + if(nonZeroRewardActions.getNextSetIndex(model.getTransitionMatrix().getRowGroupIndices()[state]) < model.getTransitionMatrix().getRowGroupIndices()[state+1]) { + nonZeroRewardStates.set(state); + } + } + storm::storage::BitVector subsystemStates = storm::utility::graph::performProbGreater0E(model.getTransitionMatrix(), model.getTransitionMatrix().getRowGroupIndices(), model.getTransitionMatrix().transpose(true), storm::storage::BitVector(model.getNumberOfStates(), true), nonZeroRewardStates); + + // Remove neutral end components, i.e., ECs in which no reward is earned. + auto ecEliminatorResult = storm::transformer::EndComponentEliminator::transform(model.getTransitionMatrix(), subsystemStates, ecActions & zeroRewardActions, possiblyRecurrentStates); + + std::vector subRewardVector(ecEliminatorResult.newToOldRowMapping.size()); + storm::utility::vector::selectVectorValues(subRewardVector, ecEliminatorResult.newToOldRowMapping, weightedRewardVector); + std::vector subResult(ecEliminatorResult.matrix.getRowGroupCount()); + + storm::solver::GeneralMinMaxLinearEquationSolverFactory solverFactory; + std::unique_ptr> solver = solverFactory.create(ecEliminatorResult.matrix); + solver->setOptimizationDirection(storm::solver::OptimizationDirection::Maximize); + solver->setTrackScheduler(true); + solver->solveEquations(subResult, subRewardVector); + + this->weightedResult = std::vector(model.getNumberOfStates()); + std::vector optimalChoices(model.getNumberOfStates()); + + transformReducedSolutionToOriginalModel(ecEliminatorResult.matrix, subResult, solver->getScheduler()->getChoices(), ecEliminatorResult.newToOldRowMapping, ecEliminatorResult.oldToNewStateMapping, this->weightedResult, optimalChoices); + + this->scheduler = storm::storage::TotalScheduler(std::move(optimalChoices)); + } + + template + void SparsePcaaWeightVectorChecker::unboundedIndividualPhase(std::vector const& weightVector) { + + storm::storage::SparseMatrix deterministicMatrix = model.getTransitionMatrix().selectRowsFromRowGroups(this->scheduler.getChoices(), true); + storm::storage::SparseMatrix deterministicBackwardTransitions = deterministicMatrix.transpose(); + std::vector deterministicStateRewards(deterministicMatrix.getRowCount()); + storm::solver::GeneralLinearEquationSolverFactory linearEquationSolverFactory; + + // We compute an estimate for the results of the individual objectives which is obtained from the weighted result and the result of the objectives computed so far. + // Note that weightedResult = Sum_{i=1}^{n} w_i * objectiveResult_i. + std::vector weightedSumOfUncheckedObjectives = weightedResult; + ValueType sumOfWeightsOfUncheckedObjectives = storm::utility::vector::sum_if(weightVector, objectivesWithNoUpperTimeBound); + + for(uint_fast64_t const& objIndex : storm::utility::vector::getSortedIndices(weightVector)) { + if(objectivesWithNoUpperTimeBound.get(objIndex)){ + offsetsToLowerBound[objIndex] = storm::utility::zero(); + offsetsToUpperBound[objIndex] = storm::utility::zero(); + storm::utility::vector::selectVectorValues(deterministicStateRewards, this->scheduler.getChoices(), model.getTransitionMatrix().getRowGroupIndices(), discreteActionRewards[objIndex]); + storm::storage::BitVector statesWithRewards = ~storm::utility::vector::filterZero(deterministicStateRewards); + // As target states, we pick the states from which no reward is reachable. + storm::storage::BitVector targetStates = ~storm::utility::graph::performProbGreater0(deterministicBackwardTransitions, storm::storage::BitVector(deterministicMatrix.getRowCount(), true), statesWithRewards); + + // Compute the estimate for this objective + if(!storm::utility::isZero(weightVector[objIndex])) { + objectiveResults[objIndex] = weightedSumOfUncheckedObjectives; + storm::utility::vector::scaleVectorInPlace(objectiveResults[objIndex], storm::utility::one() / sumOfWeightsOfUncheckedObjectives); + } + + // Make sure that the objectiveResult is initialized in some way + objectiveResults[objIndex].resize(model.getNumberOfStates(), storm::utility::zero()); + + // Invoke the linear equation solver + objectiveResults[objIndex] = storm::modelchecker::helper::SparseDtmcPrctlHelper::computeReachabilityRewards(deterministicMatrix, + deterministicBackwardTransitions, + deterministicStateRewards, + targetStates, + false, //no qualitative checking, + linearEquationSolverFactory, + objectiveResults[objIndex]); + // Update the estimate for the next objectives. + if(!storm::utility::isZero(weightVector[objIndex])) { + storm::utility::vector::addScaledVector(weightedSumOfUncheckedObjectives, objectiveResults[objIndex], -weightVector[objIndex]); + sumOfWeightsOfUncheckedObjectives -= weightVector[objIndex]; + } + } else { + objectiveResults[objIndex] = std::vector(model.getNumberOfStates(), storm::utility::zero()); + } + } + } + + template + void SparsePcaaWeightVectorChecker::transformReducedSolutionToOriginalModel(storm::storage::SparseMatrix const& reducedMatrix, + std::vector const& reducedSolution, + std::vector const& reducedOptimalChoices, + std::vector const& reducedToOriginalChoiceMapping, + std::vector const& originalToReducedStateMapping, + std::vector& originalSolution, + std::vector& originalOptimalChoices) const { + + storm::storage::BitVector recurrentStates(model.getTransitionMatrix().getRowGroupCount(), false); + storm::storage::BitVector statesThatShouldStayInTheirEC(model.getTransitionMatrix().getRowGroupCount(), false); + storm::storage::BitVector statesWithUndefSched(model.getTransitionMatrix().getRowGroupCount(), false); + + // Handle all the states for which the choice in the original model is uniquely given by the choice in the reduced model + // Also store some information regarding the remaining states + for(uint_fast64_t state = 0; state < model.getTransitionMatrix().getRowGroupCount(); ++state) { + // Check if the state exists in the reduced model, i.e., the mapping retrieves a valid index + uint_fast64_t stateInReducedModel = originalToReducedStateMapping[state]; + if(stateInReducedModel < reducedMatrix.getRowGroupCount()) { + originalSolution[state] = reducedSolution[stateInReducedModel]; + uint_fast64_t chosenRowInReducedModel = reducedMatrix.getRowGroupIndices()[stateInReducedModel] + reducedOptimalChoices[stateInReducedModel]; + uint_fast64_t chosenRowInOriginalModel = reducedToOriginalChoiceMapping[chosenRowInReducedModel]; + // Check if the state is recurrent, i.e., the chosen row stays inside this EC. + bool stateIsRecurrent = possiblyRecurrentStates.get(state); + for(auto const& entry : model.getTransitionMatrix().getRow(chosenRowInOriginalModel)) { + stateIsRecurrent &= originalToReducedStateMapping[entry.getColumn()] == stateInReducedModel; + } + if(stateIsRecurrent) { + recurrentStates.set(state); + statesThatShouldStayInTheirEC.set(state); + } else { + // Check if the chosen row originaly belonged to the current state (and not to another state of the EC) + if(chosenRowInOriginalModel >= model.getTransitionMatrix().getRowGroupIndices()[state] && + chosenRowInOriginalModel < model.getTransitionMatrix().getRowGroupIndices()[state+1]) { + originalOptimalChoices[state] = chosenRowInOriginalModel - model.getTransitionMatrix().getRowGroupIndices()[state]; + } else { + statesWithUndefSched.set(state); + statesThatShouldStayInTheirEC.set(state); + } + } + } else { + // if the state does not exist in the reduced model, it means that the (weighted) result is always zero, independent of the scheduler. + originalSolution[state] = storm::utility::zero(); + // However, it might be the case that infinite reward is induced for an objective with weight 0. + // To avoid this, all possibly recurrent states are made recurrent and the remaining states have to reach a recurrent state with prob. one + if(possiblyRecurrentStates.get(state)) { + recurrentStates.set(state); + } else { + statesWithUndefSched.set(state); + } + } + } + + // Handle recurrent states + for(auto state : recurrentStates) { + bool foundRowForState = false; + // Find a row with zero rewards that only leads to recurrent states. + // If the state should stay in its EC, we also need to make sure that all successors map to the same state in the reduced model + uint_fast64_t stateInReducedModel = originalToReducedStateMapping[state]; + for(uint_fast64_t row = model.getTransitionMatrix().getRowGroupIndices()[state]; row < model.getTransitionMatrix().getRowGroupIndices()[state+1]; ++row) { + bool rowOnlyLeadsToRecurrentStates = true; + bool rowStaysInEC = true; + for( auto const& entry : model.getTransitionMatrix().getRow(row)) { + rowOnlyLeadsToRecurrentStates &= recurrentStates.get(entry.getColumn()); + rowStaysInEC &= originalToReducedStateMapping[entry.getColumn()] == stateInReducedModel; + } + if(rowOnlyLeadsToRecurrentStates && (rowStaysInEC || !statesThatShouldStayInTheirEC.get(state)) && !actionsWithNegativeReward.get(row)) { + foundRowForState = true; + originalOptimalChoices[state] = row - model.getTransitionMatrix().getRowGroupIndices()[state]; + break; + } + } + STORM_LOG_ASSERT(foundRowForState, "Could not find a suitable choice for a recurrent state."); + } + + // Handle remaining states with still undef. scheduler (either EC states or non-subsystem states) + while(!statesWithUndefSched.empty()) { + for(auto state : statesWithUndefSched) { + // Try to find a choice such that at least one successor has a defined scheduler. + // This way, a non-recurrent state will never become recurrent + uint_fast64_t stateInReducedModel = originalToReducedStateMapping[state]; + for(uint_fast64_t row = model.getTransitionMatrix().getRowGroupIndices()[state]; row < model.getTransitionMatrix().getRowGroupIndices()[state+1]; ++row) { + bool rowStaysInEC = true; + bool rowLeadsToDefinedScheduler = false; + for(auto const& entry : model.getTransitionMatrix().getRow(row)) { + rowStaysInEC &= ( stateInReducedModel == originalToReducedStateMapping[entry.getColumn()]); + rowLeadsToDefinedScheduler |= !statesWithUndefSched.get(entry.getColumn()); + } + if(rowLeadsToDefinedScheduler && (rowStaysInEC || !statesThatShouldStayInTheirEC.get(state))) { + originalOptimalChoices[state] = row - model.getTransitionMatrix().getRowGroupIndices()[state]; + statesWithUndefSched.set(state, false); + } + } + } + } + } + + + + template class SparsePcaaWeightVectorChecker>; + template class SparsePcaaWeightVectorChecker>; +#ifdef STORM_HAVE_CARL + template class SparsePcaaWeightVectorChecker>; + template class SparsePcaaWeightVectorChecker>; +#endif + + } + } +} diff --git a/src/modelchecker/multiobjective/pcaa/SparsePcaaWeightVectorChecker.h b/src/modelchecker/multiobjective/pcaa/SparsePcaaWeightVectorChecker.h new file mode 100644 index 000000000..c13854818 --- /dev/null +++ b/src/modelchecker/multiobjective/pcaa/SparsePcaaWeightVectorChecker.h @@ -0,0 +1,164 @@ +#ifndef STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEPCAAWEIGHTVECTORCHECKER_H_ +#define STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEPCAAWEIGHTVECTORCHECKER_H_ + + +#include "src/storage/BitVector.h" +#include "src/storage/SparseMatrix.h" +#include "src/storage/TotalScheduler.h" +#include "src/modelchecker/multiobjective/pcaa/PcaaObjective.h" +#include "src/utility/vector.h" + +namespace storm { + namespace modelchecker { + namespace multiobjective { + + /*! + * Helper Class that takes preprocessed Pcaa data and a weight vector and ... + * - computes the maximal expected reward w.r.t. the weighted sum of the rewards of the individual objectives + * - extracts the scheduler that induces this maximum + * - computes for each objective the value induced by this scheduler + */ + template + class SparsePcaaWeightVectorChecker { + public: + typedef typename SparseModelType::ValueType ValueType; + + /* + * Creates a weight vextor checker. + * + * @param model The (preprocessed) model + * @param objectives The (preprocessed) objectives + * @param actionsWithNegativeReward The actions that have negative reward assigned for at least one objective + * @param ecActions The actions that are part of an EC + * @param possiblyRecurrentStates The states for which it is posible to visit them infinitely often (without inducing inf. reward) + * + */ + + SparsePcaaWeightVectorChecker(SparseModelType const& model, + std::vector> const& objectives, + storm::storage::BitVector const& actionsWithNegativeReward, + storm::storage::BitVector const& ecActions, + storm::storage::BitVector const& possiblyRecurrentStates); + + /*! + * - computes the maximal expected reward w.r.t. the weighted sum of the rewards of the individual objectives + * - extracts the scheduler that induces this maximum + * - computes for each objective the value induced by this scheduler + */ + void check(std::vector const& weightVector); + + /*! + * Retrieves the results of the individual objectives at the initial state of the given model. + * Note that check(..) has to be called before retrieving results. Otherwise, an exception is thrown. + * Also note that there is no guarantee that the lower/upper bounds are sound + * as long as the underlying solution methods are unsound (e.g., standard value iteration). + */ + std::vector getLowerBoundsOfInitialStateResults() const; + std::vector getUpperBoundsOfInitialStateResults() const; + + + /*! + * Sets the precision of this weight vector checker. After calling check() the following will hold: + * Let h_lower and h_upper be two hyperplanes such that + * * the normal vector is the provided weight-vector + * * getLowerBoundsOfInitialStateResults() lies on h_lower and + * * getUpperBoundsOfInitialStateResults() lies on h_upper. + * Then the distance between the two hyperplanes is at most weightedPrecision + */ + void setWeightedPrecision(ValueType const& weightedPrecision); + + /*! + * Returns the precision of this weight vector checker. + */ + ValueType const& getWeightedPrecision() const; + + /*! + * Retrieves a scheduler that induces the current values + * Note that check(..) has to be called before retrieving the scheduler. Otherwise, an exception is thrown. + */ + storm::storage::TotalScheduler const& getScheduler() const; + + + protected: + + /*! + * Determines the scheduler that maximizes the weighted reward vector of the unbounded objectives + * + * @param weightedRewardVector the weighted rewards (only considering the unbounded objectives) + */ + void unboundedWeightedPhase(std::vector const& weightedRewardVector); + + /*! + * Computes the values of the objectives that do not have a stepBound w.r.t. the scheduler computed in the unboundedWeightedPhase + * + * @param weightVector the weight vector of the current check + */ + void unboundedIndividualPhase(std::vector const& weightVector); + + /*! + * For each time epoch (starting with the maximal stepBound occurring in the objectives), this method + * - determines the objectives that are relevant in the current time epoch + * - determines the maximizing scheduler for the weighted reward vector of these objectives + * - computes the values of these objectives w.r.t. this scheduler + * + * @param weightVector the weight vector of the current check + * @param weightedRewardVector the weighted rewards considering the unbounded objectives. Will be invalidated after calling this. + */ + virtual void boundedPhase(std::vector const& weightVector, std::vector& weightedRewardVector) = 0; + + /*! + * Transforms the results of a min-max-solver that considers a reduced model (without end components) to a result for the original (unreduced) model + */ + void transformReducedSolutionToOriginalModel(storm::storage::SparseMatrix const& reducedMatrix, + std::vector const& reducedSolution, + std::vector const& reducedOptimalChoices, + std::vector const& reducedToOriginalChoiceMapping, + std::vector const& originalToReducedStateMapping, + std::vector& originalSolution, + std::vector& originalOptimalChoices) const; + + // The (preprocessed) model + SparseModelType const& model; + // The (preprocessed) objectives + std::vector> const& objectives; + + // The actions that have negative reward assigned for at least one objective + storm::storage::BitVector actionsWithNegativeReward; + // The actions that are part of an EC + storm::storage::BitVector ecActions; + // The states for which it is allowed to visit them infinitely often + // Put differently, if one of the states is part of a neutral EC, it is possible to + // stay in this EC forever (withoud inducing infinite reward for some objective). + storm::storage::BitVector possiblyRecurrentStates; + // stores the indices of the objectives for which there is no upper time bound + storm::storage::BitVector objectivesWithNoUpperTimeBound; + // stores the (discretized) state action rewards for each objective. + std::vector> discreteActionRewards; + /* stores the precision of this weight vector checker. After calling check() the following will hold: + * Let h_lower and h_upper be two hyperplanes such that + * * the normal vector is the provided weight-vector + * * getLowerBoundsOfInitialStateResults() lies on h_lower and + * * getUpperBoundsOfInitialStateResults() lies on h_upper. + * Then the distance between the two hyperplanes is at most weightedPrecision */ + ValueType weightedPrecision; + // Memory for the solution of the most recent call of check(..) + // becomes true after the first call of check(..) + bool checkHasBeenCalled; + // The result for the weighted reward vector (for all states of the model) + std::vector weightedResult; + // The results for the individual objectives (w.r.t. all states of the model) + std::vector> objectiveResults; + // Stores for each objective the distance between the computed result (w.r.t. the initial state) and a lower/upper bound for the actual result. + // The distances are stored as a (possibly negative) offset that has to be added (+) to to the objectiveResults. + std::vector offsetsToLowerBound; + std::vector offsetsToUpperBound; + // The scheduler that maximizes the weighted rewards + storm::storage::TotalScheduler scheduler; + + }; + + } + } +} + +#endif /* STORM_MODELCHECKER_MULTIOBJECTIVE_PCAA_SPARSEPCAAWEIGHTVECTORCHECKER_H_ */ diff --git a/src/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp b/src/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp index c0bbe6efa..c2c48e3ec 100644 --- a/src/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp +++ b/src/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp @@ -14,6 +14,8 @@ #include "src/modelchecker/prctl/helper/SparseMdpPrctlHelper.h" +#include "src/modelchecker/multiobjective/pcaa.h" + #include "src/solver/LpSolver.h" #include "src/settings/modules/GeneralSettings.h" @@ -41,7 +43,15 @@ namespace storm { template bool SparseMdpPrctlModelChecker::canHandle(CheckTask const& checkTask) const { storm::logic::Formula const& formula = checkTask.getFormula(); - return formula.isInFragment(storm::logic::prctl().setLongRunAverageRewardFormulasAllowed(false).setLongRunAverageProbabilitiesAllowed(true).setConditionalProbabilityFormulasAllowed(true).setOnlyEventuallyFormuluasInConditionalFormulasAllowed(true)); + if(formula.isInFragment(storm::logic::prctl().setLongRunAverageRewardFormulasAllowed(false).setLongRunAverageProbabilitiesAllowed(true).setConditionalProbabilityFormulasAllowed(true).setOnlyEventuallyFormuluasInConditionalFormulasAllowed(true))) { + return true; + } else { + // Check whether we consider a multi-objective formula + // For multi-objective model checking, each initial state requires an individual scheduler (in contrast to single-objective model checking). Let's exclude multiple initial states. + if(this->getModel().getInitialStates().getNumberOfSetBits() > 1) return false; + if(!checkTask.isOnlyInitialStatesRelevantSet()) return false; + return formula.isInFragment(storm::logic::multiObjective().setCumulativeRewardFormulasAllowed(true)); + } } template @@ -145,7 +155,12 @@ namespace storm { std::vector numericResult = storm::modelchecker::helper::SparseMdpPrctlHelper::computeLongRunAverageProbabilities(checkTask.getOptimizationDirection(), this->getModel().getTransitionMatrix(), this->getModel().getBackwardTransitions(), subResult.getTruthValuesVector(), checkTask.isQualitativeSet(), *minMaxLinearEquationSolverFactory); return std::unique_ptr(new ExplicitQuantitativeCheckResult(std::move(numericResult))); } - + + template + std::unique_ptr SparseMdpPrctlModelChecker::checkMultiObjectiveFormula(CheckTask const& checkTask) { + return multiobjective::performPcaa(this->getModel(), checkTask.getFormula()); + } + template class SparseMdpPrctlModelChecker>; #ifdef STORM_HAVE_CARL diff --git a/src/modelchecker/prctl/SparseMdpPrctlModelChecker.h b/src/modelchecker/prctl/SparseMdpPrctlModelChecker.h index 4ff9f153d..bd819541c 100644 --- a/src/modelchecker/prctl/SparseMdpPrctlModelChecker.h +++ b/src/modelchecker/prctl/SparseMdpPrctlModelChecker.h @@ -27,6 +27,7 @@ namespace storm { virtual std::unique_ptr computeInstantaneousRewards(storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; virtual std::unique_ptr computeReachabilityRewards(storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; virtual std::unique_ptr computeLongRunAverageProbabilities(CheckTask const& checkTask) override; + virtual std::unique_ptr checkMultiObjectiveFormula(CheckTask const& checkTask) override; private: // An object that is used for retrieving solvers for systems of linear equations that are the result of nondeterministic choices. diff --git a/src/modelchecker/results/CheckResult.cpp b/src/modelchecker/results/CheckResult.cpp index 3fb587a1e..28e85d119 100644 --- a/src/modelchecker/results/CheckResult.cpp +++ b/src/modelchecker/results/CheckResult.cpp @@ -8,6 +8,7 @@ #include "src/modelchecker/results/SymbolicQualitativeCheckResult.h" #include "src/modelchecker/results/SymbolicQuantitativeCheckResult.h" #include "src/modelchecker/results/HybridQuantitativeCheckResult.h" +#include "src/modelchecker/results/ParetoCurveCheckResult.h" #include "src/utility/macros.h" #include "src/exceptions/InvalidOperationException.h" @@ -63,6 +64,10 @@ namespace storm { return false; } + bool CheckResult::isParetoCurveCheckResult() const { + return false; + } + ExplicitQualitativeCheckResult& CheckResult::asExplicitQualitativeCheckResult() { return dynamic_cast(*this); } @@ -120,6 +125,16 @@ namespace storm { return dynamic_cast const&>(*this); } + template + ParetoCurveCheckResult& CheckResult::asParetoCurveCheckResult() { + return dynamic_cast&>(*this); + } + + template + ParetoCurveCheckResult const& CheckResult::asParetoCurveCheckResult() const { + return dynamic_cast const&>(*this); + } + // Explicitly instantiate the template functions. template ExplicitQuantitativeCheckResult& CheckResult::asExplicitQuantitativeCheckResult(); template ExplicitQuantitativeCheckResult const& CheckResult::asExplicitQuantitativeCheckResult() const; @@ -137,6 +152,9 @@ namespace storm { template SymbolicQuantitativeCheckResult const& CheckResult::asSymbolicQuantitativeCheckResult() const; template HybridQuantitativeCheckResult& CheckResult::asHybridQuantitativeCheckResult(); template HybridQuantitativeCheckResult const& CheckResult::asHybridQuantitativeCheckResult() const; + + template ParetoCurveCheckResult& CheckResult::asParetoCurveCheckResult(); + template ParetoCurveCheckResult const& CheckResult::asParetoCurveCheckResult() const; #ifdef STORM_HAVE_CARL template ExplicitQuantitativeCheckResult& CheckResult::asExplicitQuantitativeCheckResult(); @@ -144,6 +162,9 @@ namespace storm { template ExplicitQuantitativeCheckResult& CheckResult::asExplicitQuantitativeCheckResult(); template ExplicitQuantitativeCheckResult const& CheckResult::asExplicitQuantitativeCheckResult() const; + + template ParetoCurveCheckResult& CheckResult::asParetoCurveCheckResult(); + template ParetoCurveCheckResult const& CheckResult::asParetoCurveCheckResult() const; #endif } } diff --git a/src/modelchecker/results/CheckResult.h b/src/modelchecker/results/CheckResult.h index 9cbea96f7..195bfcf25 100644 --- a/src/modelchecker/results/CheckResult.h +++ b/src/modelchecker/results/CheckResult.h @@ -27,6 +27,9 @@ namespace storm { template class HybridQuantitativeCheckResult; + template + class ParetoCurveCheckResult; + // The base class of all check results. class CheckResult { public: @@ -51,6 +54,7 @@ namespace storm { virtual bool isSymbolicQualitativeCheckResult() const; virtual bool isSymbolicQuantitativeCheckResult() const; virtual bool isHybridQuantitativeCheckResult() const; + virtual bool isParetoCurveCheckResult() const; virtual bool isResultForAllStates() const; QualitativeCheckResult& asQualitativeCheckResult(); @@ -92,6 +96,12 @@ namespace storm { template HybridQuantitativeCheckResult const& asHybridQuantitativeCheckResult() const; + + template + ParetoCurveCheckResult& asParetoCurveCheckResult(); + + template + ParetoCurveCheckResult const& asParetoCurveCheckResult() const; virtual std::ostream& writeToStream(std::ostream& out) const = 0; }; @@ -100,4 +110,4 @@ namespace storm { } } -#endif /* STORM_MODELCHECKER_CHECKRESULT_H_ */ \ No newline at end of file +#endif /* STORM_MODELCHECKER_CHECKRESULT_H_ */ diff --git a/src/modelchecker/results/ParetoCurveCheckResult.cpp b/src/modelchecker/results/ParetoCurveCheckResult.cpp new file mode 100644 index 000000000..66b704443 --- /dev/null +++ b/src/modelchecker/results/ParetoCurveCheckResult.cpp @@ -0,0 +1,88 @@ +#include "src/modelchecker/results/ParetoCurveCheckResult.h" + +#include "src/adapters/CarlAdapter.h" +#include "src/modelchecker/results/ExplicitQualitativeCheckResult.h" +#include "src/utility/macros.h" +#include "src/utility/vector.h" + +#include "src/exceptions/InvalidOperationException.h" + +namespace storm { + namespace modelchecker { + template + ParetoCurveCheckResult::ParetoCurveCheckResult() { + // Intentionally left empty. + } + + template + ParetoCurveCheckResult::ParetoCurveCheckResult(storm::storage::sparse::state_type const& state, std::vector const& points, polytope_type const& underApproximation, polytope_type const& overApproximation) : state(state), points(points), underApproximation(underApproximation), overApproximation(overApproximation) { + // Intentionally left empty. + } + + template + ParetoCurveCheckResult::ParetoCurveCheckResult(storm::storage::sparse::state_type const& state, std::vector&& points, polytope_type&& underApproximation, polytope_type&& overApproximation) : state(state), points(points), underApproximation(underApproximation), overApproximation(overApproximation) { + // Intentionally left empty. + } + + template + bool ParetoCurveCheckResult::isParetoCurveCheckResult() const { + return true; + } + + template + void ParetoCurveCheckResult::filter(QualitativeCheckResult const& filter) { + STORM_LOG_THROW(filter.isExplicitQualitativeCheckResult(), storm::exceptions::InvalidOperationException, "Cannot filter explicit check result with non-explicit filter."); + STORM_LOG_THROW(filter.isResultForAllStates(), storm::exceptions::InvalidOperationException, "Cannot filter check result with non-complete filter."); + ExplicitQualitativeCheckResult const& explicitFilter = filter.asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult::vector_type const& filterTruthValues = explicitFilter.getTruthValuesVector(); + + STORM_LOG_THROW(filterTruthValues.getNumberOfSetBits() == 1 && filterTruthValues.get(state), storm::exceptions::InvalidOperationException, "The check result fails to contain some results referred to by the filter."); + } + + template + storm::storage::sparse::state_type const& ParetoCurveCheckResult:: getState() const { + return state; + } + + template + std::vector::point_type> const& ParetoCurveCheckResult::getPoints() const { + return points; + } + + template + typename ParetoCurveCheckResult::polytope_type const& ParetoCurveCheckResult::getUnderApproximation() const { + return underApproximation; + } + + template + typename ParetoCurveCheckResult::polytope_type const& ParetoCurveCheckResult::getOverApproximation() const { + return overApproximation; + } + + + template + std::ostream& ParetoCurveCheckResult::writeToStream(std::ostream& out) const { + out << std::endl; + out << "Underapproximation of achievable values: " << underApproximation->toString() << std::endl; + out << "Overapproximation of achievable values: " << overApproximation->toString() << std::endl; + out << points.size() << " pareto optimal points found (Note that these points are safe, i.e., contained in the underapproximation, but there is no guarantee for optimality):" << std::endl; + for(auto const& p : points) { + out << " ("; + for(auto it = p.begin(); it != p.end(); ++it){ + if(it != p.begin()){ + out << ", "; + } + out << std::setw(10) << *it; + } + out << " )" << std::endl; + } + return out; + } + + template class ParetoCurveCheckResult; + +#ifdef STORM_HAVE_CARL + template class ParetoCurveCheckResult; +#endif + } +} diff --git a/src/modelchecker/results/ParetoCurveCheckResult.h b/src/modelchecker/results/ParetoCurveCheckResult.h new file mode 100644 index 000000000..6781e9454 --- /dev/null +++ b/src/modelchecker/results/ParetoCurveCheckResult.h @@ -0,0 +1,56 @@ +#ifndef STORM_MODELCHECKER_PARETOCURVECHECKRESULT_H_ +#define STORM_MODELCHECKER_PARETOCURVECHECKRESULT_H_ + +#include + +#include "src/modelchecker/results/CheckResult.h" +#include "src/utility/OsDetection.h" +#include "src/storage/sparse/StateType.h" +#include "src/storage/geometry/Polytope.h" + +namespace storm { + namespace modelchecker { + template + class ParetoCurveCheckResult : public CheckResult { + public: + typedef std::vector point_type; + typedef std::shared_ptr> polytope_type; + + ParetoCurveCheckResult(); + ParetoCurveCheckResult(storm::storage::sparse::state_type const& state, std::vector const& points, polytope_type const& underApproximation, polytope_type const& overApproximation); + ParetoCurveCheckResult(storm::storage::sparse::state_type const& state, std::vector&& points, polytope_type&& underApproximation, polytope_type&& overApproximation); + + ParetoCurveCheckResult(ParetoCurveCheckResult const& other) = default; + ParetoCurveCheckResult& operator=(ParetoCurveCheckResult const& other) = default; + ParetoCurveCheckResult(ParetoCurveCheckResult&& other) = default; + ParetoCurveCheckResult& operator=(ParetoCurveCheckResult&& other) = default; + virtual ~ParetoCurveCheckResult() = default; + + virtual bool isParetoCurveCheckResult() const override; + + virtual void filter(QualitativeCheckResult const& filter) override; + + storm::storage::sparse::state_type const& getState() const; + std::vector const& getPoints() const; + polytope_type const& getUnderApproximation() const; + polytope_type const& getOverApproximation() const; + + virtual std::ostream& writeToStream(std::ostream& out) const override; + + private: + // The state of the checked model to which the result applies + storm::storage::sparse::state_type state; + + // The pareto optimal points that have been found. + std::vector points; + + // An underapproximation of the set of achievable values + polytope_type underApproximation; + + // An overapproximation of the set of achievable values + polytope_type overApproximation; + }; + } +} + +#endif /* STORM_MODELCHECKER_PARETOCURVECHECKRESULT_H_ */ diff --git a/src/models/sparse/MarkovAutomaton.cpp b/src/models/sparse/MarkovAutomaton.cpp index 32525ad5e..80ed7b6cd 100644 --- a/src/models/sparse/MarkovAutomaton.cpp +++ b/src/models/sparse/MarkovAutomaton.cpp @@ -360,6 +360,7 @@ namespace storm { this->printModelInformationHeaderToStream(out); out << "Choices: \t" << this->getNumberOfChoices() << std::endl; out << "Markovian St.: \t" << this->getMarkovianStates().getNumberOfSetBits() << std::endl; + out << "Max. Rate.: \t" << this->getMaximalExitRate() << std::endl; this->printModelInformationFooterToStream(out); } diff --git a/src/models/sparse/StateLabeling.cpp b/src/models/sparse/StateLabeling.cpp index 70561c8cb..c2281d4b4 100644 --- a/src/models/sparse/StateLabeling.cpp +++ b/src/models/sparse/StateLabeling.cpp @@ -96,6 +96,18 @@ namespace storm { STORM_LOG_THROW(this->containsLabel(label), storm::exceptions::InvalidArgumentException, "The label " << label << " is invalid for the labeling of the model."); return this->labelings[nameToLabelingIndexMap.at(label)]; } + + void StateLabeling::setStates(std::string const& label, storage::BitVector const& labeling) { + STORM_LOG_THROW(this->containsLabel(label), storm::exceptions::InvalidArgumentException, "The label " << label << " is invalid for the labeling of the model."); + STORM_LOG_THROW(labeling.size() == stateCount, storm::exceptions::InvalidArgumentException, "Labeling vector has invalid size."); + this->labelings[nameToLabelingIndexMap.at(label)] = labeling; + } + + void StateLabeling::setStates(std::string const& label, storage::BitVector&& labeling) { + STORM_LOG_THROW(this->containsLabel(label), storm::exceptions::InvalidArgumentException, "The label " << label << " is invalid for the labeling of the model."); + STORM_LOG_THROW(labeling.size() == stateCount, storm::exceptions::InvalidArgumentException, "Labeling vector has invalid size."); + this->labelings[nameToLabelingIndexMap.at(label)] = labeling; + } std::size_t StateLabeling::getSizeInBytes() const { std::size_t result = sizeof(*this); @@ -113,4 +125,4 @@ namespace storm { } } } -} \ No newline at end of file +} diff --git a/src/models/sparse/StateLabeling.h b/src/models/sparse/StateLabeling.h index ac8ac730a..2a7305103 100644 --- a/src/models/sparse/StateLabeling.h +++ b/src/models/sparse/StateLabeling.h @@ -130,6 +130,22 @@ namespace storm { */ storm::storage::BitVector const& getStates(std::string const& label) const; + /*! + * Sets the labeling of states associated with the given label. + * + * @param label The name of the label. + * @param labeling A bit vector that represents the set of states that will get this label. + */ + void setStates(std::string const& label, storage::BitVector const& labeling); + + /*! + * Sets the labeling of states associated with the given label. + * + * @param label The name of the label. + * @param labeling A bit vector that represents the set of states that will get this label. + */ + void setStates(std::string const& label, storage::BitVector&& labeling); + /*! * Returns (an approximation of) the size of the labeling measured in bytes. * diff --git a/src/parser/FormulaParserGrammar.cpp b/src/parser/FormulaParserGrammar.cpp index a4bc1bc73..79d5324c0 100644 --- a/src/parser/FormulaParserGrammar.cpp +++ b/src/parser/FormulaParserGrammar.cpp @@ -21,7 +21,10 @@ namespace storm { cumulativeRewardFormula = (qi::lit("C<=") >> strict_double)[qi::_val = phoenix::bind(&FormulaParserGrammar::createCumulativeRewardFormula, phoenix::ref(*this), qi::_1)] | (qi::lit("C<=") > qi::uint_)[qi::_val = phoenix::bind(&FormulaParserGrammar::createCumulativeRewardFormula, phoenix::ref(*this), qi::_1)]; cumulativeRewardFormula.name("cumulative reward formula"); - rewardPathFormula = longRunAverageRewardFormula | conditionalFormula(storm::logic::FormulaContext::Reward) | eventuallyFormula(storm::logic::FormulaContext::Reward) | cumulativeRewardFormula | instantaneousRewardFormula; + totalRewardFormula = (qi::lit("C"))[qi::_val = phoenix::bind(&FormulaParserGrammar::createTotalRewardFormula, phoenix::ref(*this))]; + totalRewardFormula.name("total reward formula"); + + rewardPathFormula = longRunAverageRewardFormula | conditionalFormula(storm::logic::FormulaContext::Reward) | eventuallyFormula(storm::logic::FormulaContext::Reward) | cumulativeRewardFormula | instantaneousRewardFormula | totalRewardFormula; rewardPathFormula.name("reward path formula"); expressionFormula = expressionParser[qi::_val = phoenix::bind(&FormulaParserGrammar::createAtomicExpressionFormula, phoenix::ref(*this), qi::_1)]; @@ -96,7 +99,10 @@ namespace storm { orStateFormula = andStateFormula[qi::_val = qi::_1] >> *(qi::lit("|") >> andStateFormula)[qi::_val = phoenix::bind(&FormulaParserGrammar::createBinaryBooleanStateFormula, phoenix::ref(*this), qi::_val, qi::_1, storm::logic::BinaryBooleanStateFormula::OperatorType::Or)]; orStateFormula.name("or state formula"); - stateFormula = (orStateFormula); + multiObjectiveFormula = (qi::lit("multi") > qi::lit("(") >> (stateFormula % qi::lit(",")) >> qi::lit(")"))[qi::_val = phoenix::bind(&FormulaParserGrammar::createMultiObjectiveFormula, phoenix::ref(*this), qi::_1)]; + multiObjectiveFormula.name("Multi-objective formula"); + + stateFormula = (orStateFormula | multiObjectiveFormula); stateFormula.name("state formula"); start = qi::eps > (stateFormula % +(qi::char_("\n;"))) >> qi::skip(boost::spirit::ascii::space | qi::lit("//") >> *(qi::char_ - (qi::eol | qi::eoi)))[qi::eps] >> qi::eoi; @@ -124,7 +130,9 @@ namespace storm { debug(expressionFormula); debug(rewardPathFormula); debug(cumulativeRewardFormula); + debug(totalRewardFormula); debug(instantaneousRewardFormula); + debug(multiObjectiveFormula); */ // Enable error reporting. @@ -150,7 +158,9 @@ namespace storm { qi::on_error(expressionFormula, handler(qi::_1, qi::_2, qi::_3, qi::_4)); qi::on_error(rewardPathFormula, handler(qi::_1, qi::_2, qi::_3, qi::_4)); qi::on_error(cumulativeRewardFormula, handler(qi::_1, qi::_2, qi::_3, qi::_4)); + qi::on_error(totalRewardFormula, handler(qi::_1, qi::_2, qi::_3, qi::_4)); qi::on_error(instantaneousRewardFormula, handler(qi::_1, qi::_2, qi::_3, qi::_4)); + qi::on_error(multiObjectiveFormula, handler(qi::_1, qi::_2, qi::_3, qi::_4)); } void FormulaParserGrammar::addIdentifierExpression(std::string const& identifier, storm::expressions::Expression const& expression) { @@ -177,6 +187,10 @@ namespace storm { } } + std::shared_ptr FormulaParserGrammar::createTotalRewardFormula() const { + return std::shared_ptr(new storm::logic::TotalRewardFormula()); + } + std::shared_ptr FormulaParserGrammar::createLongRunAverageRewardFormula() const { return std::shared_ptr(new storm::logic::LongRunAverageRewardFormula()); } @@ -276,6 +290,9 @@ namespace storm { return subformula; } } - + + std::shared_ptr FormulaParserGrammar::createMultiObjectiveFormula(std::vector> const& subformulas) { + return std::shared_ptr(new storm::logic::MultiObjectiveFormula(subformulas)); + } } -} \ No newline at end of file +} diff --git a/src/parser/FormulaParserGrammar.h b/src/parser/FormulaParserGrammar.h index 227c5aa47..ebb608000 100644 --- a/src/parser/FormulaParserGrammar.h +++ b/src/parser/FormulaParserGrammar.h @@ -43,7 +43,8 @@ namespace storm { ("max", 4) ("F", 5) ("G", 6) - ("X", 7); + ("X", 7) + ("multi", 8); } }; @@ -151,15 +152,19 @@ namespace storm { qi::rule(), Skipper> rewardPathFormula; qi::rule(), Skipper> cumulativeRewardFormula; + qi::rule(), Skipper> totalRewardFormula; qi::rule(), Skipper> instantaneousRewardFormula; qi::rule(), Skipper> longRunAverageRewardFormula; + qi::rule(), Skipper> multiObjectiveFormula; + // Parser that is used to recognize doubles only (as opposed to Spirit's double_ parser). boost::spirit::qi::real_parser> strict_double; // Methods that actually create the expression objects. std::shared_ptr createInstantaneousRewardFormula(boost::variant const& timeBound) const; std::shared_ptr createCumulativeRewardFormula(boost::variant const& timeBound) const; + std::shared_ptr createTotalRewardFormula() const; std::shared_ptr createLongRunAverageRewardFormula() const; std::shared_ptr createAtomicExpressionFormula(storm::expressions::Expression const& expression) const; std::shared_ptr createBooleanLiteralFormula(bool literal) const; @@ -176,10 +181,11 @@ namespace storm { std::shared_ptr createProbabilityOperatorFormula(storm::logic::OperatorInformation const& operatorInformation, std::shared_ptr const& subformula); std::shared_ptr createBinaryBooleanStateFormula(std::shared_ptr const& leftSubformula, std::shared_ptr const& rightSubformula, storm::logic::BinaryBooleanStateFormula::OperatorType operatorType); std::shared_ptr createUnaryBooleanStateFormula(std::shared_ptr const& subformula, boost::optional const& operatorType); + std::shared_ptr createMultiObjectiveFormula(std::vector> const& subformulas); // An error handler function. phoenix::function handler; }; } -} \ No newline at end of file +} diff --git a/src/settings/ArgumentValidators.h b/src/settings/ArgumentValidators.h index 60a36b55b..d68916637 100644 --- a/src/settings/ArgumentValidators.h +++ b/src/settings/ArgumentValidators.h @@ -10,6 +10,7 @@ #include #include #include +#include #include "src/settings/Argument.h" #include "src/utility/macros.h" @@ -171,6 +172,25 @@ namespace storm { }; } + /*! + * Creates a validation function that checks whether a given string corresponds to a path to non-existing file in which we can write + * + * @return The resulting validation function. + */ + static std::function writableFileValidator() { + return [] (std::string const fileName) -> bool { + struct stat info; + STORM_LOG_THROW(stat (fileName.c_str(), &info) != 0, storm::exceptions::IllegalArgumentValueException , "Could not open file '" << fileName << "' for writing because file or directory already exists."); + + std::ofstream filestream(fileName); + STORM_LOG_THROW(filestream.is_open(), storm::exceptions::IllegalArgumentValueException , "Could not open file '" << fileName << "' for writing."); + filestream.close(); + std::remove(fileName.c_str()); + + return true; + }; + } + /*! * Creates a validation function that checks whether a given string is in a provided list of strings. * @@ -252,4 +272,4 @@ namespace storm { } } -#endif // STORM_SETTINGS_ARGUMENTVALIDATORS_H_ \ No newline at end of file +#endif // STORM_SETTINGS_ARGUMENTVALIDATORS_H_ diff --git a/src/settings/SettingsManager.cpp b/src/settings/SettingsManager.cpp index 2aba99492..1cf4d6886 100644 --- a/src/settings/SettingsManager.cpp +++ b/src/settings/SettingsManager.cpp @@ -34,6 +34,7 @@ #include "src/settings/modules/TopologicalValueIterationEquationSolverSettings.h" #include "src/settings/modules/ExplorationSettings.h" #include "src/settings/modules/JaniExportSettings.h" +#include "src/settings/modules/MultiObjectiveSettings.h" #include "src/utility/macros.h" #include "src/settings/Option.h" @@ -526,6 +527,7 @@ namespace storm { storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); + storm::settings::addModule(); } } diff --git a/src/settings/modules/MultiObjectiveSettings.cpp b/src/settings/modules/MultiObjectiveSettings.cpp new file mode 100644 index 000000000..3adb29e95 --- /dev/null +++ b/src/settings/modules/MultiObjectiveSettings.cpp @@ -0,0 +1,57 @@ +#include "src/settings/modules/MultiObjectiveSettings.h" + +#include "src/settings/Option.h" +#include "src/settings/OptionBuilder.h" +#include "src/settings/ArgumentBuilder.h" +#include "src/settings/Argument.h" +#include "src/settings/ArgumentValidators.h" + + +namespace storm { + namespace settings { + namespace modules { + + const std::string MultiObjectiveSettings::moduleName = "multiobjective"; + const std::string MultiObjectiveSettings::exportPlotOptionName = "exportplot"; + const std::string MultiObjectiveSettings::precisionOptionName = "precision"; + const std::string MultiObjectiveSettings::maxStepsOptionName = "maxsteps"; + + MultiObjectiveSettings::MultiObjectiveSettings() : ModuleSettings(moduleName) { + this->addOption(storm::settings::OptionBuilder(moduleName, exportPlotOptionName, true, "Saves data for plotting of pareto curves and achievable values.") + .addArgument(storm::settings::ArgumentBuilder::createStringArgument("directory", "A path to a directory in which the results will be saved.").build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, precisionOptionName, true, "The precision used for the approximation of numerical- and pareto queries.") + .addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("value", "The precision. Default is 1e-04.").setDefaultValueDouble(1e-04).addValidationFunctionDouble(storm::settings::ArgumentValidators::doubleRangeValidatorExcluding(0.0, 1.0)).build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, maxStepsOptionName, true, "Aborts the computation after the given number of refinement steps (= computed pareto optimal points).") + .addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("value", "the threshold for the number of refinement steps to be performed.").build()).build()); + } + + bool MultiObjectiveSettings::isExportPlotSet() const { + return this->getOption(exportPlotOptionName).getHasOptionBeenSet(); + } + + std::string MultiObjectiveSettings::getExportPlotDirectory() const { + return this->getOption(exportPlotOptionName).getArgumentByName("directory").getValueAsString(); + } + double MultiObjectiveSettings::getPrecision() const { + return this->getOption(precisionOptionName).getArgumentByName("value").getValueAsDouble(); + } + + bool MultiObjectiveSettings::isMaxStepsSet() const { + return this->getOption(maxStepsOptionName).getHasOptionBeenSet(); + } + + uint_fast64_t MultiObjectiveSettings::getMaxSteps() const { + return this->getOption(maxStepsOptionName).getArgumentByName("value").getValueAsUnsignedInteger(); + } + + bool MultiObjectiveSettings::check() const { + return !isExportPlotSet() + || (ArgumentValidators::writableFileValidator()(getExportPlotDirectory() + "boundaries.csv") + && ArgumentValidators::writableFileValidator()(getExportPlotDirectory() + "overapproximation.csv") + && ArgumentValidators::writableFileValidator()(getExportPlotDirectory() + "underapproximation.csv") + && ArgumentValidators::writableFileValidator()(getExportPlotDirectory() + "paretopoints.csv")); + } + + } // namespace modules + } // namespace settings +} // namespace storm diff --git a/src/settings/modules/MultiObjectiveSettings.h b/src/settings/modules/MultiObjectiveSettings.h new file mode 100644 index 000000000..ba410184c --- /dev/null +++ b/src/settings/modules/MultiObjectiveSettings.h @@ -0,0 +1,75 @@ +#ifndef STORM_SETTINGS_MODULES_MULTIOBJECTIVESETTINGS_H_ +#define STORM_SETTINGS_MODULES_MULTIOBJECTIVESETTINGS_H_ + +#include "src/settings/modules/ModuleSettings.h" + +namespace storm { + namespace settings { + namespace modules { + + /*! + * This class represents the settings for multi-objective model checking. + */ + class MultiObjectiveSettings : public ModuleSettings { + public: + + /*! + * Creates a new set of multi-objective model checking settings. + */ + MultiObjectiveSettings(); + + /*! + * Retrieves whether the data for plotting should be exported. + * @return True iff the data for plotting should be exported. + */ + bool isExportPlotSet() const; + + /*! + * The path to a directory in which the plot data should be stored. + * @return A path to a directory. + */ + std::string getExportPlotDirectory() const; + + /** + * Retrieves the desired precision for quantitative- and pareto queries. + * @return the desired precision for quantitative- and pareto queries. + */ + double getPrecision() const; + + /*! + * Retrieves whether or not a threshold for the number of performed refinement steps is given. + * + * @return True if a threshold for the number of performed refinement steps is given. + */ + bool isMaxStepsSet() const; + + + /*! + * Retrieves The maximum number of refinement steps that should be performed (if given). + * + * @return the maximum number of refinement steps that should be performed (if given). + */ + uint_fast64_t getMaxSteps() const; + + + /*! + * Checks whether the settings are consistent. If they are inconsistent, an exception is thrown. + * + * @return True if the settings are consistent. + */ + virtual bool check() const override; + + + const static std::string moduleName; + + private: + const static std::string exportPlotOptionName; + const static std::string precisionOptionName; + const static std::string maxStepsOptionName; + }; + + } // namespace modules + } // namespace settings +} // namespace storm + +#endif /* STORM_SETTINGS_MODULES_MULTIOBJECTIVESETTINGS_H_ */ diff --git a/src/solver/EigenLinearEquationSolver.cpp b/src/solver/EigenLinearEquationSolver.cpp index eb19b57e2..bdc6e6a07 100644 --- a/src/solver/EigenLinearEquationSolver.cpp +++ b/src/solver/EigenLinearEquationSolver.cpp @@ -117,6 +117,7 @@ namespace storm { template void EigenLinearEquationSolver::setMatrix(storm::storage::SparseMatrix const& A) { eigenA = storm::adapters::EigenAdapter::toEigenSparseMatrix(A); + this->resetAuxiliaryData(); } template @@ -124,6 +125,7 @@ namespace storm { // Take ownership of the matrix so it is destroyed after we have translated it to Eigen's format. storm::storage::SparseMatrix localA(std::move(A)); this->setMatrix(localA); + this->resetAuxiliaryData(); } template diff --git a/src/solver/EliminationLinearEquationSolver.cpp b/src/solver/EliminationLinearEquationSolver.cpp index e35402e95..e46148b18 100644 --- a/src/solver/EliminationLinearEquationSolver.cpp +++ b/src/solver/EliminationLinearEquationSolver.cpp @@ -47,12 +47,14 @@ namespace storm { void EliminationLinearEquationSolver::setMatrix(storm::storage::SparseMatrix const& A) { this->A = &A; localA.reset(); + this->resetAuxiliaryData(); } template void EliminationLinearEquationSolver::setMatrix(storm::storage::SparseMatrix&& A) { localA = std::make_unique>(std::move(A)); this->A = localA.get(); + this->resetAuxiliaryData(); } template diff --git a/src/solver/GmmxxLinearEquationSolver.cpp b/src/solver/GmmxxLinearEquationSolver.cpp index a1f57beec..7c58241b5 100644 --- a/src/solver/GmmxxLinearEquationSolver.cpp +++ b/src/solver/GmmxxLinearEquationSolver.cpp @@ -111,12 +111,12 @@ namespace storm { } template - GmmxxLinearEquationSolver::GmmxxLinearEquationSolver(storm::storage::SparseMatrix const& A, GmmxxLinearEquationSolverSettings const& settings) : localA(nullptr), A(nullptr), gmmxxMatrix(nullptr), settings(settings), auxiliaryJacobiMemory(nullptr) { + GmmxxLinearEquationSolver::GmmxxLinearEquationSolver(storm::storage::SparseMatrix const& A, GmmxxLinearEquationSolverSettings const& settings) : localA(nullptr), A(nullptr), settings(settings) { this->setMatrix(A); } template - GmmxxLinearEquationSolver::GmmxxLinearEquationSolver(storm::storage::SparseMatrix&& A, GmmxxLinearEquationSolverSettings const& settings) : localA(nullptr), A(nullptr), gmmxxMatrix(nullptr), settings(settings), auxiliaryJacobiMemory(nullptr) { + GmmxxLinearEquationSolver::GmmxxLinearEquationSolver(storm::storage::SparseMatrix&& A, GmmxxLinearEquationSolverSettings const& settings) : localA(nullptr), A(nullptr), settings(settings) { this->setMatrix(std::move(A)); } @@ -124,14 +124,14 @@ namespace storm { void GmmxxLinearEquationSolver::setMatrix(storm::storage::SparseMatrix const& A) { localA.reset(); this->A = &A; - gmmxxMatrix = storm::adapters::GmmxxAdapter::toGmmxxSparseMatrix(A); + resetAuxiliaryData(); } template void GmmxxLinearEquationSolver::setMatrix(storm::storage::SparseMatrix&& A) { localA = std::make_unique>(std::move(A)); this->A = localA.get(); - gmmxxMatrix = storm::adapters::GmmxxAdapter::toGmmxxSparseMatrix(*localA); + resetAuxiliaryData(); } template @@ -144,32 +144,46 @@ namespace storm { } if (method == GmmxxLinearEquationSolverSettings::SolutionMethod::Bicgstab || method == GmmxxLinearEquationSolverSettings::SolutionMethod::Qmr || method == GmmxxLinearEquationSolverSettings::SolutionMethod::Gmres) { + + // Translate the matrix into gmm++ format (if not already done) + if(!gmmxxA) { + gmmxxA = storm::adapters::GmmxxAdapter::toGmmxxSparseMatrix(*A); + } + + // Make sure that the requested preconditioner is available + if (preconditioner == GmmxxLinearEquationSolverSettings::Preconditioner::Ilu && !iluPreconditioner) { + iluPreconditioner = std::make_unique>>(*gmmxxA); + } else if (preconditioner == GmmxxLinearEquationSolverSettings::Preconditioner::Diagonal) { + diagonalPreconditioner = std::make_unique>>(*gmmxxA); + } + // Prepare an iteration object that determines the accuracy and the maximum number of iterations. gmm::iteration iter(this->getSettings().getPrecision(), 0, this->getSettings().getMaximalNumberOfIterations()); + // Invoke gmm with the corresponding settings if (method == GmmxxLinearEquationSolverSettings::SolutionMethod::Bicgstab) { if (preconditioner == GmmxxLinearEquationSolverSettings::Preconditioner::Ilu) { - gmm::bicgstab(*gmmxxMatrix, x, b, gmm::ilu_precond>(*gmmxxMatrix), iter); + gmm::bicgstab(*gmmxxA, x, b, *iluPreconditioner, iter); } else if (preconditioner == GmmxxLinearEquationSolverSettings::Preconditioner::Diagonal) { - gmm::bicgstab(*gmmxxMatrix, x, b, gmm::diagonal_precond>(*gmmxxMatrix), iter); + gmm::bicgstab(*gmmxxA, x, b, *diagonalPreconditioner, iter); } else if (preconditioner == GmmxxLinearEquationSolverSettings::Preconditioner::None) { - gmm::bicgstab(*gmmxxMatrix, x, b, gmm::identity_matrix(), iter); + gmm::bicgstab(*gmmxxA, x, b, gmm::identity_matrix(), iter); } } else if (method == GmmxxLinearEquationSolverSettings::SolutionMethod::Qmr) { if (preconditioner == GmmxxLinearEquationSolverSettings::Preconditioner::Ilu) { - gmm::qmr(*gmmxxMatrix, x, b, gmm::ilu_precond>(*gmmxxMatrix), iter); + gmm::qmr(*gmmxxA, x, b, *iluPreconditioner, iter); } else if (preconditioner == GmmxxLinearEquationSolverSettings::Preconditioner::Diagonal) { - gmm::qmr(*gmmxxMatrix, x, b, gmm::diagonal_precond>(*gmmxxMatrix), iter); + gmm::qmr(*gmmxxA, x, b, *diagonalPreconditioner, iter); } else if (preconditioner == GmmxxLinearEquationSolverSettings::Preconditioner::None) { - gmm::qmr(*gmmxxMatrix, x, b, gmm::identity_matrix(), iter); + gmm::qmr(*gmmxxA, x, b, gmm::identity_matrix(), iter); } } else if (method == GmmxxLinearEquationSolverSettings::SolutionMethod::Gmres) { if (preconditioner == GmmxxLinearEquationSolverSettings::Preconditioner::Ilu) { - gmm::gmres(*gmmxxMatrix, x, b, gmm::ilu_precond>(*gmmxxMatrix), this->getSettings().getNumberOfIterationsUntilRestart(), iter); + gmm::gmres(*gmmxxA, x, b, *iluPreconditioner, this->getSettings().getNumberOfIterationsUntilRestart(), iter); } else if (preconditioner == GmmxxLinearEquationSolverSettings::Preconditioner::Diagonal) { - gmm::gmres(*gmmxxMatrix, x, b, gmm::diagonal_precond>(*gmmxxMatrix), this->getSettings().getNumberOfIterationsUntilRestart(), iter); + gmm::gmres(*gmmxxA, x, b, *diagonalPreconditioner, this->getSettings().getNumberOfIterationsUntilRestart(), iter); } else if (preconditioner == GmmxxLinearEquationSolverSettings::Preconditioner::None) { - gmm::gmres(*gmmxxMatrix, x, b, gmm::identity_matrix(), this->getSettings().getNumberOfIterationsUntilRestart(), iter); + gmm::gmres(*gmmxxA, x, b, gmm::identity_matrix(), this->getSettings().getNumberOfIterationsUntilRestart(), iter); } } @@ -182,7 +196,7 @@ namespace storm { return false; } } else if (method == GmmxxLinearEquationSolverSettings::SolutionMethod::Jacobi) { - uint_fast64_t iterations = solveLinearEquationSystemWithJacobi(*A, x, b); + uint_fast64_t iterations = solveLinearEquationSystemWithJacobi(x, b); // Check if the solver converged and issue a warning otherwise. if (iterations < this->getSettings().getMaximalNumberOfIterations()) { @@ -199,28 +213,35 @@ namespace storm { template void GmmxxLinearEquationSolver::multiply(std::vector& x, std::vector const* b, std::vector& result) const { + if(!gmmxxA) { + gmmxxA = storm::adapters::GmmxxAdapter::toGmmxxSparseMatrix(*A); + } if (b) { - gmm::mult_add(*gmmxxMatrix, x, *b, result); + gmm::mult_add(*gmmxxA, x, *b, result); } else { - gmm::mult(*gmmxxMatrix, x, result); + gmm::mult(*gmmxxA, x, result); } } template - uint_fast64_t GmmxxLinearEquationSolver::solveLinearEquationSystemWithJacobi(storm::storage::SparseMatrix const& A, std::vector& x, std::vector const& b) const { - bool allocatedAuxMemory = !this->hasAuxMemory(LinearEquationSolverOperation::SolveEquations); - if (allocatedAuxMemory) { - this->allocateAuxMemory(LinearEquationSolverOperation::SolveEquations); - } - - // Get a Jacobi decomposition of the matrix A. - std::pair, std::vector> jacobiDecomposition = A.getJacobiDecomposition(); + uint_fast64_t GmmxxLinearEquationSolver::solveLinearEquationSystemWithJacobi(std::vector& x, std::vector const& b) const { - // Convert the LU matrix to gmm++'s format. - std::unique_ptr> gmmLU = storm::adapters::GmmxxAdapter::toGmmxxSparseMatrix(std::move(jacobiDecomposition.first)); + // Get a Jacobi decomposition of the matrix A (if not already available). + if(!jacobiDecomposition) { + std::pair, std::vector> nativeJacobiDecomposition = A->getJacobiDecomposition(); + // Convert the LU matrix to gmm++'s format. + jacobiDecomposition = std::make_unique, std::vector>>(*storm::adapters::GmmxxAdapter::toGmmxxSparseMatrix(std::move(nativeJacobiDecomposition.first)), + std::move(nativeJacobiDecomposition.second)); + } + gmm::csr_matrix const& jacobiLU = jacobiDecomposition->first; + std::vector const& jacobiD = jacobiDecomposition->second; std::vector* currentX = &x; - std::vector* nextX = auxiliaryJacobiMemory.get(); + + if(!this->auxiliaryRowVector) { + this->auxiliaryRowVector = std::make_unique>(getMatrixRowCount()); + } + std::vector* nextX = this->auxiliaryRowVector.get(); // Set up additional environment variables. uint_fast64_t iterationCount = 0; @@ -228,8 +249,8 @@ namespace storm { while (!converged && iterationCount < this->getSettings().getMaximalNumberOfIterations() && !(this->hasCustomTerminationCondition() && this->getTerminationCondition().terminateNow(*currentX))) { // Compute D^-1 * (b - LU * x) and store result in nextX. - gmm::mult_add(*gmmLU, gmm::scaled(*currentX, -storm::utility::one()), b, *nextX); - storm::utility::vector::multiplyVectorsPointwise(jacobiDecomposition.second, *nextX, *nextX); + gmm::mult_add(jacobiLU, gmm::scaled(*currentX, -storm::utility::one()), b, *nextX); + storm::utility::vector::multiplyVectorsPointwise(jacobiD, *nextX, *nextX); // Now check if the process already converged within our precision. converged = storm::utility::vector::equalModuloPrecision(*currentX, *nextX, this->getSettings().getPrecision(), this->getSettings().getRelativeTerminationCriterion()); @@ -243,21 +264,16 @@ namespace storm { // If the last iteration did not write to the original x we have to swap the contents, because the // output has to be written to the input parameter x. - if (currentX == auxiliaryJacobiMemory.get()) { + if (currentX == this->auxiliaryRowVector.get()) { std::swap(x, *currentX); } - // If we allocated auxiliary memory, we need to dispose of it now. - if (allocatedAuxMemory) { - this->deallocateAuxMemory(LinearEquationSolverOperation::SolveEquations); - } - return iterationCount; } template - GmmxxLinearEquationSolverSettings& GmmxxLinearEquationSolver::getSettings() { - return settings; + void GmmxxLinearEquationSolver::setSettings(GmmxxLinearEquationSolverSettings const& newSettings) { + settings = newSettings; } template @@ -266,54 +282,12 @@ namespace storm { } template - bool GmmxxLinearEquationSolver::allocateAuxMemory(LinearEquationSolverOperation operation) const { - bool result = false; - if (operation == LinearEquationSolverOperation::SolveEquations) { - if (this->getSettings().getSolutionMethod() == GmmxxLinearEquationSolverSettings::SolutionMethod::Jacobi) { - if (!auxiliaryJacobiMemory) { - auxiliaryJacobiMemory = std::make_unique>(this->getMatrixRowCount()); - result = true; - } - } - } - result |= LinearEquationSolver::allocateAuxMemory(operation); - return result; - } - - template - bool GmmxxLinearEquationSolver::deallocateAuxMemory(LinearEquationSolverOperation operation) const { - bool result = false; - if (operation == LinearEquationSolverOperation::SolveEquations) { - if (auxiliaryJacobiMemory) { - result = true; - auxiliaryJacobiMemory.reset(); - } - } - result |= LinearEquationSolver::deallocateAuxMemory(operation); - return result; - } - - template - bool GmmxxLinearEquationSolver::reallocateAuxMemory(LinearEquationSolverOperation operation) const { - bool result = false; - if (operation == LinearEquationSolverOperation::SolveEquations) { - if (auxiliaryJacobiMemory) { - result = auxiliaryJacobiMemory->size() != this->getMatrixColumnCount(); - auxiliaryJacobiMemory->resize(this->getMatrixRowCount()); - } - } - result |= LinearEquationSolver::reallocateAuxMemory(operation); - return result; - } - - template - bool GmmxxLinearEquationSolver::hasAuxMemory(LinearEquationSolverOperation operation) const { - bool result = false; - if (operation == LinearEquationSolverOperation::SolveEquations) { - result |= static_cast(auxiliaryJacobiMemory); - } - result |= LinearEquationSolver::hasAuxMemory(operation); - return result; + void GmmxxLinearEquationSolver::resetAuxiliaryData() const { + gmmxxA.reset(); + iluPreconditioner.reset(); + diagonalPreconditioner.reset(); + jacobiDecomposition.reset(); + LinearEquationSolver::resetAuxiliaryData(); } template diff --git a/src/solver/GmmxxLinearEquationSolver.h b/src/solver/GmmxxLinearEquationSolver.h index 7d4c09ad1..09a50a92e 100644 --- a/src/solver/GmmxxLinearEquationSolver.h +++ b/src/solver/GmmxxLinearEquationSolver.h @@ -95,29 +95,26 @@ namespace storm { virtual bool solveEquations(std::vector& x, std::vector const& b) const override; virtual void multiply(std::vector& x, std::vector const* b, std::vector& result) const override; - GmmxxLinearEquationSolverSettings& getSettings(); + void setSettings(GmmxxLinearEquationSolverSettings const& newSettings); GmmxxLinearEquationSolverSettings const& getSettings() const; - virtual bool allocateAuxMemory(LinearEquationSolverOperation operation) const override; - virtual bool deallocateAuxMemory(LinearEquationSolverOperation operation) const override; - virtual bool reallocateAuxMemory(LinearEquationSolverOperation operation) const override; - virtual bool hasAuxMemory(LinearEquationSolverOperation operation) const override; + /* + * Clears auxiliary data that has possibly been stored during previous calls of the solver. + */ + virtual void resetAuxiliaryData() const override; private: /*! * Solves the linear equation system A*x = b given by the parameters using the Jacobi method. * - * @param A The matrix specifying the coefficients of the linear equations. * @param x The solution vector x. The initial values of x represent a guess of the real values to the * solver, but may be set to zero. * @param b The right-hand side of the equation system. * @return The number of iterations needed until convergence if the solver converged and * maximalNumberOfIteration otherwise. - * @param multiplyResult If non-null, this memory is used as a scratch memory. If given, the length of this - * vector must be equal to the number of rows of A. */ - uint_fast64_t solveLinearEquationSystemWithJacobi(storm::storage::SparseMatrix const& A, std::vector& x, std::vector const& b) const; + uint_fast64_t solveLinearEquationSystemWithJacobi(std::vector& x, std::vector const& b) const; virtual uint64_t getMatrixRowCount() const override; virtual uint64_t getMatrixColumnCount() const override; @@ -130,14 +127,14 @@ namespace storm { // the pointer refers to localA. storm::storage::SparseMatrix const* A; - // The (gmm++) matrix associated with this equation solver. - std::unique_ptr> gmmxxMatrix; - // The settings used by the solver. GmmxxLinearEquationSolverSettings settings; - // Auxiliary storage for the Jacobi method. - mutable std::unique_ptr> auxiliaryJacobiMemory; + // Auxiliary data obtained during solving + mutable std::unique_ptr> gmmxxA; + mutable std::unique_ptr>> iluPreconditioner; + mutable std::unique_ptr>> diagonalPreconditioner; + mutable std::unique_ptr, std::vector>> jacobiDecomposition; }; template diff --git a/src/solver/LinearEquationSolver.cpp b/src/solver/LinearEquationSolver.cpp index c4d46cd46..029e596fc 100644 --- a/src/solver/LinearEquationSolver.cpp +++ b/src/solver/LinearEquationSolver.cpp @@ -14,21 +14,21 @@ namespace storm { namespace solver { template - LinearEquationSolver::LinearEquationSolver() : auxiliaryRepeatedMultiplyMemory(nullptr) { + LinearEquationSolver::LinearEquationSolver() { // Intentionally left empty. } template void LinearEquationSolver::repeatedMultiply(std::vector& x, std::vector const* b, uint_fast64_t n) const { - bool allocatedAuxMemory = !this->hasAuxMemory(LinearEquationSolverOperation::MultiplyRepeatedly); - if (allocatedAuxMemory) { - this->allocateAuxMemory(LinearEquationSolverOperation::MultiplyRepeatedly); + + if(!auxiliaryRowVector) { + auxiliaryRowVector = std::make_unique>(getMatrixRowCount()); } // Set up some temporary variables so that we can just swap pointers instead of copying the result after // each iteration. std::vector* currentX = &x; - std::vector* nextX = auxiliaryRepeatedMultiplyMemory.get(); + std::vector* nextX = auxiliaryRowVector.get(); // Now perform matrix-vector multiplication as long as we meet the bound. for (uint_fast64_t i = 0; i < n; ++i) { @@ -38,56 +38,16 @@ namespace storm { // If we performed an odd number of repetitions, we need to swap the contents of currentVector and x, // because the output is supposed to be stored in the input vector x. - if (currentX == auxiliaryRepeatedMultiplyMemory.get()) { + if (currentX == auxiliaryRowVector.get()) { std::swap(x, *currentX); } - - // If we allocated auxiliary memory, we need to dispose of it now. - if (allocatedAuxMemory) { - this->deallocateAuxMemory(LinearEquationSolverOperation::MultiplyRepeatedly); - } } template - bool LinearEquationSolver::allocateAuxMemory(LinearEquationSolverOperation operation) const { - if (!auxiliaryRepeatedMultiplyMemory) { - auxiliaryRepeatedMultiplyMemory = std::make_unique>(this->getMatrixColumnCount()); - return true; - } - return false; + void LinearEquationSolver::resetAuxiliaryData() const { + auxiliaryRowVector.reset(); } - - template - bool LinearEquationSolver::deallocateAuxMemory(LinearEquationSolverOperation operation) const { - if (operation == LinearEquationSolverOperation::MultiplyRepeatedly) { - if (auxiliaryRepeatedMultiplyMemory) { - auxiliaryRepeatedMultiplyMemory.reset(); - return true; - } - } - return false; - } - - template - bool LinearEquationSolver::reallocateAuxMemory(LinearEquationSolverOperation operation) const { - bool result = false; - if (operation == LinearEquationSolverOperation::MultiplyRepeatedly) { - if (auxiliaryRepeatedMultiplyMemory) { - result = auxiliaryRepeatedMultiplyMemory->size() != this->getMatrixColumnCount(); - auxiliaryRepeatedMultiplyMemory->resize(this->getMatrixColumnCount()); - } - } - return result; - } - - template - bool LinearEquationSolver::hasAuxMemory(LinearEquationSolverOperation operation) const { - if (operation == LinearEquationSolverOperation::MultiplyRepeatedly) { - return static_cast(auxiliaryRepeatedMultiplyMemory); - } - return false; - } - + template std::unique_ptr> LinearEquationSolverFactory::create(storm::storage::SparseMatrix&& matrix) const { return create(matrix); diff --git a/src/solver/LinearEquationSolver.h b/src/solver/LinearEquationSolver.h index b52778051..7988d2c6a 100644 --- a/src/solver/LinearEquationSolver.h +++ b/src/solver/LinearEquationSolver.h @@ -68,40 +68,15 @@ namespace storm { */ void repeatedMultiply(std::vector& x, std::vector const* b, uint_fast64_t n) const; - // Methods related to allocating/freeing auxiliary storage. - - /*! - * Allocates auxiliary memory that can be used to perform the provided operation. Repeated calls to the - * corresponding function can then be run without allocating/deallocating this memory repeatedly. - * Note: Since the allocated memory is fit to the currently selected options of the solver, they must not - * be changed any more after allocating the auxiliary memory until it is deallocated again. - * - * @return True iff auxiliary memory was allocated. - */ - virtual bool allocateAuxMemory(LinearEquationSolverOperation operation) const; - - /*! - * Destroys previously allocated auxiliary memory for the provided operation. - * - * @return True iff auxiliary memory was deallocated. - */ - virtual bool deallocateAuxMemory(LinearEquationSolverOperation operation) const; - - /*! - * If the matrix dimensions changed and auxiliary memory was allocated, this function needs to be called to - * update the auxiliary memory. - * - * @return True iff the auxiliary memory was reallocated. - */ - virtual bool reallocateAuxMemory(LinearEquationSolverOperation operation) const; - - /*! - * Checks whether the solver has allocated auxiliary memory for the provided operation. - * - * @return True iff auxiliary memory was previously allocated (and not yet deallocated). + /* + * Clears auxiliary data that has possibly been stored during previous calls of the solver. */ - virtual bool hasAuxMemory(LinearEquationSolverOperation operation) const; + virtual void resetAuxiliaryData() const; + protected: + // auxiliary storage. If set, this vector has getMatrixRowCount() entries. + mutable std::unique_ptr> auxiliaryRowVector; + private: /*! * Retrieves the row count of the matrix associated with this solver. @@ -113,8 +88,6 @@ namespace storm { */ virtual uint64_t getMatrixColumnCount() const = 0; - // Auxiliary memory for repeated matrix-vector multiplication. - mutable std::unique_ptr> auxiliaryRepeatedMultiplyMemory; }; template diff --git a/src/solver/MinMaxLinearEquationSolver.cpp b/src/solver/MinMaxLinearEquationSolver.cpp index 9b7d65ff9..9c35a8a29 100644 --- a/src/solver/MinMaxLinearEquationSolver.cpp +++ b/src/solver/MinMaxLinearEquationSolver.cpp @@ -80,19 +80,10 @@ namespace storm { } template - bool MinMaxLinearEquationSolver::allocateAuxMemory(MinMaxLinearEquationSolverOperation operation) const { - return false; - } - - template - bool MinMaxLinearEquationSolver::deallocateAuxMemory(MinMaxLinearEquationSolverOperation operation) const { - return false; + void MinMaxLinearEquationSolver::resetAuxiliaryData() const { + // Intentionally left empty. } - template - bool MinMaxLinearEquationSolver::hasAuxMemory(MinMaxLinearEquationSolverOperation operation) const { - return false; - } template MinMaxLinearEquationSolverFactory::MinMaxLinearEquationSolverFactory(bool trackScheduler) : trackScheduler(trackScheduler) { diff --git a/src/solver/MinMaxLinearEquationSolver.h b/src/solver/MinMaxLinearEquationSolver.h index 6be77c93b..f3b332f3c 100644 --- a/src/solver/MinMaxLinearEquationSolver.h +++ b/src/solver/MinMaxLinearEquationSolver.h @@ -23,10 +23,6 @@ namespace storm { namespace solver { - enum class MinMaxLinearEquationSolverOperation { - SolveEquations, MultiplyRepeatedly - }; - /*! * A class representing the interface that all min-max linear equation solvers shall implement. */ @@ -133,31 +129,10 @@ namespace storm { */ virtual bool getRelative() const = 0; - // Methods related to allocating/freeing auxiliary storage. - - /*! - * Allocates auxiliary storage that can be used to perform the provided operation. Repeated calls to the - * corresponding function can then be run without allocating/deallocating this storage repeatedly. - * Note: Since the allocated storage is fit to the currently selected options of the solver, they must not - * be changed any more after allocating the auxiliary storage until the storage is deallocated again. - * - * @return True iff auxiliary storage was allocated. - */ - virtual bool allocateAuxMemory(MinMaxLinearEquationSolverOperation operation) const; - - /*! - * Destroys previously allocated auxiliary storage for the provided operation. - * - * @return True iff auxiliary storage was deallocated. - */ - virtual bool deallocateAuxMemory(MinMaxLinearEquationSolverOperation operation) const; - - /*! - * Checks whether the solver has allocated auxiliary storage for the provided operation. - * - * @return True iff auxiliary storage was previously allocated (and not yet deallocated). + /* + * Clears auxiliary data that has possibly been stored during previous calls of the solver. */ - virtual bool hasAuxMemory(MinMaxLinearEquationSolverOperation operation) const; + virtual void resetAuxiliaryData() const; protected: diff --git a/src/solver/NativeLinearEquationSolver.cpp b/src/solver/NativeLinearEquationSolver.cpp index 42e861f21..31fe74227 100644 --- a/src/solver/NativeLinearEquationSolver.cpp +++ b/src/solver/NativeLinearEquationSolver.cpp @@ -84,12 +84,12 @@ namespace storm { } template - NativeLinearEquationSolver::NativeLinearEquationSolver(storm::storage::SparseMatrix const& A, NativeLinearEquationSolverSettings const& settings) : localA(nullptr), A(nullptr), settings(settings), auxiliarySolvingMemory(nullptr) { + NativeLinearEquationSolver::NativeLinearEquationSolver(storm::storage::SparseMatrix const& A, NativeLinearEquationSolverSettings const& settings) : localA(nullptr), A(nullptr), settings(settings) { this->setMatrix(A); } template - NativeLinearEquationSolver::NativeLinearEquationSolver(storm::storage::SparseMatrix&& A, NativeLinearEquationSolverSettings const& settings) : localA(nullptr), A(nullptr), settings(settings), auxiliarySolvingMemory(nullptr) { + NativeLinearEquationSolver::NativeLinearEquationSolver(storm::storage::SparseMatrix&& A, NativeLinearEquationSolverSettings const& settings) : localA(nullptr), A(nullptr), settings(settings) { this->setMatrix(std::move(A)); } @@ -97,20 +97,21 @@ namespace storm { void NativeLinearEquationSolver::setMatrix(storm::storage::SparseMatrix const& A) { localA.reset(); this->A = &A; + resetAuxiliaryData(); } template void NativeLinearEquationSolver::setMatrix(storm::storage::SparseMatrix&& A) { localA = std::make_unique>(std::move(A)); this->A = localA.get(); + resetAuxiliaryData(); } template bool NativeLinearEquationSolver::solveEquations(std::vector& x, std::vector const& b) const { - bool allocatedAuxStorage = !this->hasAuxMemory(LinearEquationSolverOperation::SolveEquations); - if (allocatedAuxStorage) { - this->allocateAuxMemory(LinearEquationSolverOperation::SolveEquations); + if(!this->auxiliaryRowVector) { + this->auxiliaryRowVector = std::make_unique>(getMatrixRowCount()); } if (this->getSettings().getSolutionMethod() == NativeLinearEquationSolverSettings::SolutionMethod::SOR || this->getSettings().getSolutionMethod() == NativeLinearEquationSolverSettings::SolutionMethod::GaussSeidel) { @@ -125,29 +126,29 @@ namespace storm { A->performSuccessiveOverRelaxationStep(omega, x, b); // Now check if the process already converged within our precision. - converged = storm::utility::vector::equalModuloPrecision(*auxiliarySolvingMemory, x, static_cast(this->getSettings().getPrecision()), this->getSettings().getRelativeTerminationCriterion()) || (this->hasCustomTerminationCondition() && this->getTerminationCondition().terminateNow(x)); + converged = storm::utility::vector::equalModuloPrecision(*this->auxiliaryRowVector, x, static_cast(this->getSettings().getPrecision()), this->getSettings().getRelativeTerminationCriterion()) || (this->hasCustomTerminationCondition() && this->getTerminationCondition().terminateNow(x)); // If we did not yet converge, we need to backup the contents of x. if (!converged) { - *auxiliarySolvingMemory = x; + *this->auxiliaryRowVector = x; } // Increase iteration count so we can abort if convergence is too slow. ++iterationCount; } - // If we allocated auxiliary memory, we need to dispose of it now. - if (allocatedAuxStorage) { - this->deallocateAuxMemory(LinearEquationSolverOperation::SolveEquations); - } return converged; } else { // Get a Jacobi decomposition of the matrix A. - std::pair, std::vector> jacobiDecomposition = A->getJacobiDecomposition(); + if(!jacobiDecomposition) { + jacobiDecomposition = std::make_unique, std::vector>>(A->getJacobiDecomposition()); + } + storm::storage::SparseMatrix const& jacobiLU = jacobiDecomposition->first; + std::vector const& jacobiD = jacobiDecomposition->second; std::vector* currentX = &x; - std::vector* nextX = auxiliarySolvingMemory.get(); + std::vector* nextX = this->auxiliaryRowVector.get(); // Set up additional environment variables. uint_fast64_t iterationCount = 0; @@ -155,9 +156,9 @@ namespace storm { while (!converged && iterationCount < this->getSettings().getMaximalNumberOfIterations() && !(this->hasCustomTerminationCondition() && this->getTerminationCondition().terminateNow(*currentX))) { // Compute D^-1 * (b - LU * x) and store result in nextX. - jacobiDecomposition.first.multiplyWithVector(*currentX, *nextX); + jacobiLU.multiplyWithVector(*currentX, *nextX); storm::utility::vector::subtractVectors(b, *nextX, *nextX); - storm::utility::vector::multiplyVectorsPointwise(jacobiDecomposition.second, *nextX, *nextX); + storm::utility::vector::multiplyVectorsPointwise(jacobiD, *nextX, *nextX); // Now check if the process already converged within our precision. converged = storm::utility::vector::equalModuloPrecision(*currentX, *nextX, static_cast(this->getSettings().getPrecision()), this->getSettings().getRelativeTerminationCriterion()); @@ -171,18 +172,12 @@ namespace storm { // If the last iteration did not write to the original x we have to swap the contents, because the // output has to be written to the input parameter x. - if (currentX == auxiliarySolvingMemory.get()) { + if (currentX == this->auxiliaryRowVector.get()) { std::swap(x, *currentX); } - - // If we allocated auxiliary memory, we need to dispose of it now. - if (allocatedAuxStorage) { - this->deallocateAuxMemory(LinearEquationSolverOperation::SolveEquations); - } + return iterationCount < this->getSettings().getMaximalNumberOfIterations(); } - - } template @@ -194,17 +189,21 @@ namespace storm { } } else { // If the two vectors are aliases, we need to create a temporary. - std::vector tmp(result.size()); - A->multiplyWithVector(x, tmp); + if(!this->auxiliaryRowVector) { + this->auxiliaryRowVector = std::make_unique>(getMatrixRowCount()); + } + A->multiplyWithVector(x, *this->auxiliaryRowVector); if (b != nullptr) { - storm::utility::vector::addVectors(tmp, *b, result); + storm::utility::vector::addVectors(*this->auxiliaryRowVector, *b, result); + } else { + result.swap(*this->auxiliaryRowVector); } } } template - NativeLinearEquationSolverSettings& NativeLinearEquationSolver::getSettings() { - return settings; + void NativeLinearEquationSolver::setSettings(NativeLinearEquationSolverSettings const& newSettings) { + settings = newSettings; } template @@ -213,52 +212,9 @@ namespace storm { } template - bool NativeLinearEquationSolver::allocateAuxMemory(LinearEquationSolverOperation operation) const { - bool result = false; - if (operation == LinearEquationSolverOperation::SolveEquations) { - if (!auxiliarySolvingMemory) { - auxiliarySolvingMemory = std::make_unique>(this->getMatrixRowCount()); - result = true; - } - } - result |= LinearEquationSolver::allocateAuxMemory(operation); - return result; - } - - template - bool NativeLinearEquationSolver::deallocateAuxMemory(LinearEquationSolverOperation operation) const { - bool result = false; - if (operation == LinearEquationSolverOperation::SolveEquations) { - if (auxiliarySolvingMemory) { - result = true; - auxiliarySolvingMemory.reset(); - } - } - result |= LinearEquationSolver::deallocateAuxMemory(operation); - return result; - } - - template - bool NativeLinearEquationSolver::reallocateAuxMemory(LinearEquationSolverOperation operation) const { - bool result = false; - if (operation == LinearEquationSolverOperation::SolveEquations) { - if (auxiliarySolvingMemory) { - result = auxiliarySolvingMemory->size() != this->getMatrixColumnCount(); - auxiliarySolvingMemory->resize(this->getMatrixRowCount()); - } - } - result |= LinearEquationSolver::reallocateAuxMemory(operation); - return result; - } - - template - bool NativeLinearEquationSolver::hasAuxMemory(LinearEquationSolverOperation operation) const { - bool result = false; - if (operation == LinearEquationSolverOperation::SolveEquations) { - result |= static_cast(auxiliarySolvingMemory); - } - result |= LinearEquationSolver::hasAuxMemory(operation); - return result; + void NativeLinearEquationSolver::resetAuxiliaryData() const { + jacobiDecomposition.reset(); + LinearEquationSolver::resetAuxiliaryData(); } template @@ -301,4 +257,4 @@ namespace storm { template class NativeLinearEquationSolver; template class NativeLinearEquationSolverFactory; } -} \ No newline at end of file +} diff --git a/src/solver/NativeLinearEquationSolver.h b/src/solver/NativeLinearEquationSolver.h index 4bf9275dd..df1b91a13 100644 --- a/src/solver/NativeLinearEquationSolver.h +++ b/src/solver/NativeLinearEquationSolver.h @@ -52,15 +52,14 @@ namespace storm { virtual bool solveEquations(std::vector& x, std::vector const& b) const override; virtual void multiply(std::vector& x, std::vector const* b, std::vector& result) const override; - NativeLinearEquationSolverSettings& getSettings(); + void setSettings(NativeLinearEquationSolverSettings const& newSettings); NativeLinearEquationSolverSettings const& getSettings() const; - virtual bool allocateAuxMemory(LinearEquationSolverOperation operation) const override; - virtual bool deallocateAuxMemory(LinearEquationSolverOperation operation) const override; - virtual bool reallocateAuxMemory(LinearEquationSolverOperation operation) const override; - virtual bool hasAuxMemory(LinearEquationSolverOperation operation) const override; + /* + * Clears auxiliary data that has possibly been stored during previous calls of the solver. + */ + virtual void resetAuxiliaryData() const override; - storm::storage::SparseMatrix const& getMatrix() const { return *A; } private: virtual uint64_t getMatrixRowCount() const override; virtual uint64_t getMatrixColumnCount() const override; @@ -76,8 +75,7 @@ namespace storm { // The settings used by the solver. NativeLinearEquationSolverSettings settings; - // Auxiliary memory for the equation solving methods. - mutable std::unique_ptr> auxiliarySolvingMemory; + mutable std::unique_ptr, std::vector>> jacobiDecomposition; }; template diff --git a/src/solver/StandardMinMaxLinearEquationSolver.cpp b/src/solver/StandardMinMaxLinearEquationSolver.cpp index 6e0313cf9..454b59b3d 100644 --- a/src/solver/StandardMinMaxLinearEquationSolver.cpp +++ b/src/solver/StandardMinMaxLinearEquationSolver.cpp @@ -98,8 +98,11 @@ namespace storm { // Create the initial scheduler. std::vector scheduler(this->A.getRowGroupCount()); - // Create a vector for storing the right-hand side of the inner equation system. - std::vector subB(this->A.getRowGroupCount()); + // Get a vector for storing the right-hand side of the inner equation system. + if(!auxiliaryRowGroupVector) { + auxiliaryRowGroupVector = std::make_unique>(this->A.getRowGroupCount()); + } + std::vector& subB = *auxiliaryRowGroupVector; // Resolve the nondeterminism according to the current scheduler. storm::storage::SparseMatrix submatrix = this->A.selectRowsFromRowGroups(scheduler, true); @@ -108,7 +111,6 @@ namespace storm { // Create a solver that we will use throughout the procedure. We will modify the matrix in each iteration. auto solver = linearEquationSolverFactory->create(std::move(submatrix)); - solver->allocateAuxMemory(LinearEquationSolverOperation::SolveEquations); Status status = Status::InProgress; uint64_t iterations = 0; @@ -136,6 +138,8 @@ namespace storm { choiceValue += b[choice]; // If the value is strictly better than the solution of the inner system, we need to improve the scheduler. + // TODO: If the underlying solver is not precise, this might run forever (i.e. when a state has two choices where the (exact) values are equal). + // only changing the scheduler if the values are not equal (modulo precision) would make this unsound. if (valueImproved(dir, x[group], choiceValue)) { schedulerImproved = true; scheduler[group] = choice - this->A.getRowGroupIndices()[group]; @@ -165,7 +169,7 @@ namespace storm { if (this->isTrackSchedulerSet()) { this->scheduler = std::make_unique(std::move(scheduler)); } - + if(status == Status::Converged || status == Status::TerminatedEarly) { return true; } else{ @@ -202,14 +206,21 @@ namespace storm { template bool StandardMinMaxLinearEquationSolver::solveEquationsValueIteration(OptimizationDirection dir, std::vector& x, std::vector const& b) const { - std::unique_ptr> solver = linearEquationSolverFactory->create(A); - bool allocatedAuxMemory = !this->hasAuxMemory(MinMaxLinearEquationSolverOperation::SolveEquations); - if (allocatedAuxMemory) { - this->allocateAuxMemory(MinMaxLinearEquationSolverOperation::SolveEquations); + if(!linEqSolverA) { + linEqSolverA = linearEquationSolverFactory->create(A); } + if (!auxiliaryRowVector.get()) { + auxiliaryRowVector = std::make_unique>(A.getRowCount()); + } + std::vector& multiplyResult = *auxiliaryRowVector; + + if (!auxiliaryRowGroupVector.get()) { + auxiliaryRowGroupVector = std::make_unique>(A.getRowGroupCount()); + } + std::vector* newX = auxiliaryRowGroupVector.get(); + std::vector* currentX = &x; - std::vector* newX = auxiliarySolvingVectorMemory.get(); // Proceed with the iterations as long as the method did not converge or reach the maximum number of iterations. uint64_t iterations = 0; @@ -217,10 +228,10 @@ namespace storm { Status status = Status::InProgress; while (status == Status::InProgress) { // Compute x' = A*x + b. - solver->multiply(*currentX, &b, *auxiliarySolvingMultiplyMemory); + linEqSolverA->multiply(*currentX, &b, multiplyResult); // Reduce the vector x' by applying min/max for all non-deterministic choices. - storm::utility::vector::reduceVectorMinOrMax(dir, *auxiliarySolvingMultiplyMemory, *newX, this->A.getRowGroupIndices()); + storm::utility::vector::reduceVectorMinOrMax(dir, multiplyResult, *newX, this->A.getRowGroupIndices()); // Determine whether the method converged. if (storm::utility::vector::equalModuloPrecision(*currentX, *newX, this->getSettings().getPrecision(), this->getSettings().getRelativeTerminationCriterion())) { @@ -238,27 +249,20 @@ namespace storm { // If we performed an odd number of iterations, we need to swap the x and currentX, because the newest result // is currently stored in currentX, but x is the output vector. - if (currentX == auxiliarySolvingVectorMemory.get()) { + if (currentX == auxiliaryRowGroupVector.get()) { std::swap(x, *currentX); } // If requested, we store the scheduler for retrieval. if (this->isTrackSchedulerSet()) { if(iterations==0){ //may happen due to custom termination condition. Then we need to compute x'= A*x+b - solver->multiply(x, &b, *auxiliarySolvingMultiplyMemory); + linEqSolverA->multiply(x, &b, multiplyResult); } std::vector choices(this->A.getRowGroupCount()); // Reduce the multiplyResult and keep track of the choices made - storm::utility::vector::reduceVectorMinOrMax(dir, *auxiliarySolvingMultiplyMemory, x, this->A.getRowGroupIndices(), &choices); + storm::utility::vector::reduceVectorMinOrMax(dir, multiplyResult, x, this->A.getRowGroupIndices(), &choices); this->scheduler = std::make_unique(std::move(choices)); } - // If we allocated auxiliary memory, we need to dispose of it now. - if (allocatedAuxMemory) { - this->deallocateAuxMemory(MinMaxLinearEquationSolverOperation::SolveEquations); - } - - - if(status == Status::Converged || status == Status::TerminatedEarly) { return true; @@ -269,24 +273,21 @@ namespace storm { template void StandardMinMaxLinearEquationSolver::repeatedMultiply(OptimizationDirection dir, std::vector& x, std::vector* b, uint_fast64_t n) const { - bool allocatedAuxMemory = !this->hasAuxMemory(MinMaxLinearEquationSolverOperation::MultiplyRepeatedly); - if (allocatedAuxMemory) { - this->allocateAuxMemory(MinMaxLinearEquationSolverOperation::MultiplyRepeatedly); + if(!linEqSolverA) { + linEqSolverA = linearEquationSolverFactory->create(A); } - std::unique_ptr> solver = linearEquationSolverFactory->create(A); + if (!auxiliaryRowVector.get()) { + auxiliaryRowVector = std::make_unique>(A.getRowCount()); + } + std::vector& multiplyResult = *auxiliaryRowVector; for (uint64_t i = 0; i < n; ++i) { - solver->multiply(x, b, *auxiliaryRepeatedMultiplyMemory); + linEqSolverA->multiply(x, b, multiplyResult); // Reduce the vector x' by applying min/max for all non-deterministic choices as given by the topmost // element of the min/max operator stack. - storm::utility::vector::reduceVectorMinOrMax(dir, *auxiliaryRepeatedMultiplyMemory, x, this->A.getRowGroupIndices()); - } - - // If we allocated auxiliary memory, we need to dispose of it now. - if (allocatedAuxMemory) { - this->deallocateAuxMemory(MinMaxLinearEquationSolverOperation::MultiplyRepeatedly); + storm::utility::vector::reduceVectorMinOrMax(dir, multiplyResult, x, this->A.getRowGroupIndices()); } } @@ -319,77 +320,18 @@ namespace storm { } template - StandardMinMaxLinearEquationSolverSettings& StandardMinMaxLinearEquationSolver::getSettings() { - return settings; - } - - template - bool StandardMinMaxLinearEquationSolver::allocateAuxMemory(MinMaxLinearEquationSolverOperation operation) const { - bool result = false; - if (operation == MinMaxLinearEquationSolverOperation::SolveEquations) { - if (this->getSettings().getSolutionMethod() == StandardMinMaxLinearEquationSolverSettings::SolutionMethod::ValueIteration) { - if (!auxiliarySolvingMultiplyMemory) { - result = true; - auxiliarySolvingMultiplyMemory = std::make_unique>(this->A.getRowCount()); - } - if (!auxiliarySolvingVectorMemory) { - result = true; - auxiliarySolvingVectorMemory = std::make_unique>(this->A.getRowGroupCount()); - } - } else if (this->getSettings().getSolutionMethod() == StandardMinMaxLinearEquationSolverSettings::SolutionMethod::PolicyIteration) { - // Nothing to do in this case. - } else { - STORM_LOG_ASSERT(false, "Cannot allocate aux storage for this method."); - } - } else { - if (!auxiliaryRepeatedMultiplyMemory) { - result = true; - auxiliaryRepeatedMultiplyMemory = std::make_unique>(this->A.getRowCount()); - } - } - return result; - } - - template - bool StandardMinMaxLinearEquationSolver::deallocateAuxMemory(MinMaxLinearEquationSolverOperation operation) const { - bool result = false; - if (operation == MinMaxLinearEquationSolverOperation::SolveEquations) { - if (this->getSettings().getSolutionMethod() == StandardMinMaxLinearEquationSolverSettings::SolutionMethod::ValueIteration) { - if (auxiliarySolvingMultiplyMemory) { - result = true; - auxiliarySolvingMultiplyMemory.reset(); - } - if (auxiliarySolvingVectorMemory) { - result = true; - auxiliarySolvingVectorMemory.reset(); - } - } else if (this->getSettings().getSolutionMethod() == StandardMinMaxLinearEquationSolverSettings::SolutionMethod::PolicyIteration) { - // Nothing to do in this case. - } else { - STORM_LOG_ASSERT(false, "Cannot allocate aux storage for this method."); - } - } else { - if (auxiliaryRepeatedMultiplyMemory) { - result = true; - auxiliaryRepeatedMultiplyMemory.reset(); - } - } - return result; + void StandardMinMaxLinearEquationSolver::setSettings(StandardMinMaxLinearEquationSolverSettings const& newSettings) { + settings = newSettings; } template - bool StandardMinMaxLinearEquationSolver::hasAuxMemory(MinMaxLinearEquationSolverOperation operation) const { - if (operation == MinMaxLinearEquationSolverOperation::SolveEquations) { - if (this->getSettings().getSolutionMethod() == StandardMinMaxLinearEquationSolverSettings::SolutionMethod::ValueIteration) { - return auxiliarySolvingMultiplyMemory && auxiliarySolvingVectorMemory; - } else { - return false; - } - } else { - return static_cast(auxiliaryRepeatedMultiplyMemory); - } + void StandardMinMaxLinearEquationSolver::resetAuxiliaryData() const { + linEqSolverA.reset(); + auxiliaryRowVector.reset(); + auxiliaryRowGroupVector.reset(); + MinMaxLinearEquationSolver::resetAuxiliaryData(); } - + template StandardMinMaxLinearEquationSolverFactory::StandardMinMaxLinearEquationSolverFactory(bool trackScheduler) : MinMaxLinearEquationSolverFactory(trackScheduler), linearEquationSolverFactory(nullptr) { // Intentionally left empty. diff --git a/src/solver/StandardMinMaxLinearEquationSolver.h b/src/solver/StandardMinMaxLinearEquationSolver.h index d6d36b272..4cc381904 100644 --- a/src/solver/StandardMinMaxLinearEquationSolver.h +++ b/src/solver/StandardMinMaxLinearEquationSolver.h @@ -42,11 +42,9 @@ namespace storm { virtual void repeatedMultiply(OptimizationDirection dir, std::vector& x, std::vector* b, uint_fast64_t n) const override; StandardMinMaxLinearEquationSolverSettings const& getSettings() const; - StandardMinMaxLinearEquationSolverSettings& getSettings(); + void setSettings(StandardMinMaxLinearEquationSolverSettings const& newSettings); - virtual bool allocateAuxMemory(MinMaxLinearEquationSolverOperation operation) const override; - virtual bool deallocateAuxMemory(MinMaxLinearEquationSolverOperation operation) const override; - virtual bool hasAuxMemory(MinMaxLinearEquationSolverOperation operation) const override; + virtual void resetAuxiliaryData() const override; virtual ValueType getPrecision() const override; virtual bool getRelative() const override; @@ -59,6 +57,10 @@ namespace storm { enum class Status { Converged, TerminatedEarly, MaximalIterationsExceeded, InProgress }; + + mutable std::unique_ptr> linEqSolverA; + mutable std::unique_ptr> auxiliaryRowVector; // A.rowCount() entries + mutable std::unique_ptr> auxiliaryRowGroupVector; // A.rowGroupCount() entries Status updateStatusIfNotConverged(Status status, std::vector const& x, uint64_t iterations) const; void reportStatus(Status status, uint64_t iterations) const; @@ -77,12 +79,6 @@ namespace storm { // the reference refers to localA. storm::storage::SparseMatrix const& A; - // Auxiliary memory for equation solving. - mutable std::unique_ptr> auxiliarySolvingMultiplyMemory; - mutable std::unique_ptr> auxiliarySolvingVectorMemory; - - // Auxiliary memory for repeated matrix-vector multiplication. - mutable std::unique_ptr> auxiliaryRepeatedMultiplyMemory; }; template @@ -129,4 +125,4 @@ namespace storm { }; } -} \ No newline at end of file +} diff --git a/src/storage/SparseMatrix.cpp b/src/storage/SparseMatrix.cpp index 0bb4ebe10..55cc36dca 100644 --- a/src/storage/SparseMatrix.cpp +++ b/src/storage/SparseMatrix.cpp @@ -554,6 +554,17 @@ namespace storm { return rowGroupIndices.get(); } + template + storm::storage::BitVector SparseMatrix::getRowIndicesOfRowGroups(storm::storage::BitVector const& groups) const { + storm::storage::BitVector res(this->getRowCount(), false); + for(auto group : groups) { + for(uint_fast64_t row = this->getRowGroupIndices()[group]; row < this->getRowGroupIndices()[group+1]; ++row) { + res.set(row, true); + } + } + return res; + } + template void SparseMatrix::makeRowsAbsorbing(storm::storage::BitVector const& rows) { for (auto row : rows) { diff --git a/src/storage/SparseMatrix.h b/src/storage/SparseMatrix.h index b9cf3831c..81b175e53 100644 --- a/src/storage/SparseMatrix.h +++ b/src/storage/SparseMatrix.h @@ -564,6 +564,14 @@ namespace storm { */ std::vector const& getRowGroupIndices() const; + /*! + * Returns the indices of the rows that belong to one of the selected row groups. + * + * @param groups the selected row groups + * @return a bit vector that is true at position i iff the row group of row i is selected. + */ + storm::storage::BitVector getRowIndicesOfRowGroups(storm::storage::BitVector const& groups) const; + /*! * This function makes the given rows absorbing. * diff --git a/src/storage/TotalScheduler.cpp b/src/storage/TotalScheduler.cpp index 6ae99d212..d689ae050 100644 --- a/src/storage/TotalScheduler.cpp +++ b/src/storage/TotalScheduler.cpp @@ -1,5 +1,6 @@ #include "src/storage/TotalScheduler.h" #include "src/exceptions/InvalidArgumentException.h" +#include "src/utility/Hash.h" namespace storm { namespace storage { @@ -15,6 +16,10 @@ namespace storm { // Intentionally left empty. } + bool TotalScheduler::operator==(storm::storage::TotalScheduler const& other) const { + return this->choices == other.choices; + } + void TotalScheduler::setChoice(uint_fast64_t state, uint_fast64_t choice) { if (state > choices.size()) { throw storm::exceptions::InvalidArgumentException() << "Invalid call to TotalScheduler::setChoice: scheduler cannot not define a choice for state " << state << "."; @@ -34,6 +39,10 @@ namespace storm { return choices[state]; } + std::vector const& TotalScheduler::getChoices() const { + return choices; + } + std::ostream& operator<<(std::ostream& out, TotalScheduler const& scheduler) { out << "total scheduler (defined on " << scheduler.choices.size() << " states) [ "; for (uint_fast64_t state = 0; state < scheduler.choices.size() - 1; ++state) { @@ -44,6 +53,11 @@ namespace storm { } return out; } - } -} \ No newline at end of file +} + +namespace std { + std::size_t hash::operator()(storm::storage::TotalScheduler const& totalScheduler) const { + return storm::utility::Hash::getHash(totalScheduler.choices); + } +} diff --git a/src/storage/TotalScheduler.h b/src/storage/TotalScheduler.h index 6f95edf71..f44c91205 100644 --- a/src/storage/TotalScheduler.h +++ b/src/storage/TotalScheduler.h @@ -11,6 +11,12 @@ namespace storm { class TotalScheduler : public Scheduler { public: + + TotalScheduler(TotalScheduler const& other) = default; + TotalScheduler(TotalScheduler&& other) = default; + TotalScheduler& operator=(TotalScheduler const& other) = default; + TotalScheduler& operator=(TotalScheduler&& other) = default; + /*! * Creates a total scheduler that defines a choice for the given number of states. By default, all choices * are initialized to zero. @@ -32,6 +38,8 @@ namespace storm { * @param choices A vector whose i-th entry defines the choice of state i. */ TotalScheduler(std::vector&& choices); + + bool operator==(TotalScheduler const& other) const; void setChoice(uint_fast64_t state, uint_fast64_t choice) override; @@ -39,7 +47,10 @@ namespace storm { uint_fast64_t getChoice(uint_fast64_t state) const override; + std::vector const& getChoices() const; + friend std::ostream& operator<<(std::ostream& out, TotalScheduler const& scheduler); + friend struct std::hash; private: // A vector that stores the choice for each state. @@ -48,4 +59,12 @@ namespace storm { } // namespace storage } // namespace storm + +namespace std { + template <> + struct hash { + std::size_t operator()(storm::storage::TotalScheduler const& totalScheduler) const; + }; +} + #endif /* STORM_STORAGE_TOTALSCHEDULER_H_ */ diff --git a/src/storage/geometry/Halfspace.h b/src/storage/geometry/Halfspace.h new file mode 100644 index 000000000..2b5705049 --- /dev/null +++ b/src/storage/geometry/Halfspace.h @@ -0,0 +1,127 @@ +#ifndef STORM_STORAGE_GEOMETRY_HALFSPACE_H_ +#define STORM_STORAGE_GEOMETRY_HALFSPACE_H_ + +#include +#include +#include "src/utility/constants.h" +#include "src/utility/vector.h" + +namespace storm { + namespace storage { + namespace geometry { + + /* + * This class represents a closed Halfspace, i.e., the set { x | a*x<=c } for a normalVector a and an offset c + */ + + template + class Halfspace { + + public: + + Halfspace(std::vector const& normalVector, ValueType const& offset) : mNormalVector(normalVector), mOffset(offset) { + //Intentionally left empty + } + + Halfspace(std::vector&& normalVector, ValueType&& offset) : mNormalVector(normalVector), mOffset(offset) { + //Intentionally left empty + } + + /* + * Returns true iff the given point is contained in this halfspace, i.e., normalVector*point <= offset holds. + */ + bool contains(std::vector const& point) const { + return storm::utility::vector::dotProduct(point, normalVector()) <= offset(); + } + + /* + * Returns the (scaled) distance of the given point from this halfspace. + * If the point is inside this halfspace, the distance is 0. + * The returned value is the euclidean distance times the 2-norm of the normalVector. + * In contrast to the euclideanDistance method, there are no inaccuracies introduced (providing ValueType is exact for +, -, and *) + */ + ValueType distance(std::vector const& point) const { + return std::max(storm::utility::zero(), storm::utility::vector::dotProduct(point, normalVector()) - offset()); + } + + /* + * Returns the euclidean distance of the point from this halfspace. + * If the point is inside this halfspace, the distance is 0. + * Note that the euclidean distance is in general not a rational number (which can introduce inaccuracies). + */ + ValueType euclideanDistance(std::vector const& point) const { + // divide the distance with the 2-norm of the normal vector + return distance(point) / storm::utility::sqrt(storm::utility::vector::dotProduct(normalVector(), normalVector())); + } + + /* + * Returns true iff the given point lies on the boundary of this halfspace (i.e., on the hyperplane given by normalVector()*x =offset + */ + bool isPointOnBoundary(std::vector const& point) const { + return storm::utility::vector::dotProduct(point, normalVector()) == offset(); + } + + /* + * Returns the inverted Halfspace of this which represents the set (R^n \ this) union { x | x is on the boundary of this} + */ + Halfspace invert() const { + std::vector resNormalVector = normalVector(); + storm::utility::vector::scaleVectorInPlace(resNormalVector, -storm::utility::one()); + return Halfspace(std::move(resNormalVector), -offset()); + } + + /* + * Returns a string representation of this Halfspace. + * If the given flag is true, the occurring numbers are converted to double before printing to increase readability + */ + std::string toString(bool numbersAsDouble = false) const { + std::stringstream stream; + stream << "("; + for(auto it = normalVector().begin(); it != normalVector().end(); ++it){ + if(it != normalVector().begin()){ + stream << ", "; + } + std::stringstream numberStream; + if(numbersAsDouble) { + numberStream << storm::utility::convertNumber(*it); + } else { + numberStream << *it; + } + stream << std::setw(10) << numberStream.str(); + } + stream << ") * x <= "; + if(numbersAsDouble) { + stream << storm::utility::convertNumber(offset()); + } else { + stream << offset(); + } + return stream.str(); + } + + std::vector const& normalVector() const { + return mNormalVector; + } + + std::vector& normalVector(){ + return mNormalVector; + } + + ValueType const& offset() const { + return mOffset; + } + + ValueType& offset(){ + return mOffset; + } + + private: + + std::vector mNormalVector; + ValueType mOffset; + + }; + } + } +} + +#endif /* STORM_STORAGE_GEOMETRY_HALFSPACE_H_ */ diff --git a/src/storage/geometry/Hyperrectangle.h b/src/storage/geometry/Hyperrectangle.h new file mode 100644 index 000000000..58c666a90 --- /dev/null +++ b/src/storage/geometry/Hyperrectangle.h @@ -0,0 +1,88 @@ +#ifndef STORM_STORAGE_GEOMETRY_HYPERRECTANGLE_H_ +#define STORM_STORAGE_GEOMETRY_HYPERRECTANGLE_H_ + +#include +#include + +#include "src/utility/macros.h" +#include "src/storage/geometry/Polytope.h" +#include "src/storage/geometry/Halfspace.h" +#include "src/exceptions/InvalidArgumentException.h" + +namespace storm { + namespace storage { + namespace geometry { + + /* + * This class represents a hyperrectangle, i.e., the intersection of finitely many intervals + */ + + template + class Hyperrectangle { + + public: + + Hyperrectangle(std::vector const& lowerBounds, std::vector const& upperBounds) : mLowerBounds(lowerBounds), mUpperBounds(upperBounds) { + STORM_LOG_THROW(lowerBounds.size() == upperBounds.size(), storm::exceptions::InvalidArgumentException, "Tried to construct a hyperrectangle but the number of given lower bounds does not equal the number of given upper bounds."); + } + + Hyperrectangle(std::vector&& lowerBounds, std::vector&& upperBounds) : mLowerBounds(lowerBounds), mUpperBounds(upperBounds) { + STORM_LOG_THROW(lowerBounds.size() == upperBounds.size(), storm::exceptions::InvalidArgumentException, "Tried to construct a hyperrectangle but the number of given lower bounds does not equal the number of given upper bounds."); + } + + std::vector const& lowerBounds() const { + return mLowerBounds; + } + + std::vector& lowerBounds() { + return mLowerBounds; + } + + std::vector const& upperBounds() const { + return mUpperBounds; + } + + std::vector& upperBounds() { + return mUpperBounds; + } + + /* + * Enlarges this hyperrectangle such that it contains the given point + */ + void enlarge(std::vector const& point) { + STORM_LOG_THROW(point.size() == lowerBounds().size() && point.size() == upperBounds().size(), storm::exceptions::InvalidArgumentException, "Tried to enlarge a hyperrectangle but the dimension of the given point does not match."); + for(uint_fast64_t i = 0; i> asPolytope() const { + STORM_LOG_THROW(lowerBounds().size() == upperBounds().size(), storm::exceptions::InvalidArgumentException, "Tried to construct a polytope form a hyperrectangle but the numbers of given lower and upper bounds do not match."); + std::vector> halfspaces; + halfspaces.reserve(2*lowerBounds().size()); + for(uint_fast64_t i = 0; i direction(lowerBounds().size(), storm::utility::zero()); + direction[i] = -storm::utility::one(); + ValueType offset = -lowerBounds()[i]; + halfspaces.emplace_back(std::move(direction), std::move(offset)); + + direction = std::vector(lowerBounds().size(), storm::utility::zero()); + direction[i] = storm::utility::one(); + offset = upperBounds()[i]; + halfspaces.emplace_back(std::move(direction), std::move(offset)); + } + return Polytope::create(halfspaces); + } + + private: + + std::vector mLowerBounds; + std::vector mUpperBounds; + + }; + } + } +} + +#endif /* STORM_STORAGE_GEOMETRY_HYPERRECTANGLE_H_ */ diff --git a/src/storage/geometry/HyproPolytope.cpp b/src/storage/geometry/HyproPolytope.cpp new file mode 100644 index 000000000..5d28a65af --- /dev/null +++ b/src/storage/geometry/HyproPolytope.cpp @@ -0,0 +1,184 @@ +#include "src/storage/geometry/HyproPolytope.h" +# +#ifdef STORM_HAVE_HYPRO + +#include "src/utility/macros.h" +#include "src/exceptions/InvalidArgumentException.h" +#include "src/exceptions/UnexpectedException.h" + +namespace storm { + namespace storage { + namespace geometry { + + template + HyproPolytope::HyproPolytope(std::vector> const& halfspaces) { + if(halfspaces.empty()){ + internPolytope = HyproPolytopeType(); + } else { + std::vector> hyproHalfspaces; + hyproHalfspaces.reserve(halfspaces.size()); + for(auto& h : halfspaces) { + hyproHalfspaces.emplace_back(storm::adapters::toHypro(h)); + } + internPolytope = HyproPolytopeType(std::move(hyproHalfspaces)); + } + } + + template + HyproPolytope::HyproPolytope(std::vector const& points) { + if(points.empty()){ + internPolytope = HyproPolytopeType::Empty(); + } else { + std::vector> hyproPoints; + hyproPoints.reserve(points.size()); + for(auto const& p : points){ + hyproPoints.emplace_back(storm::adapters::toHypro(p)); + } + internPolytope = HyproPolytopeType(std::move(hyproPoints)); + } + } + + template + std::shared_ptr> HyproPolytope::create(boost::optional>> const& halfspaces, + boost::optional> const& points) { + if(halfspaces) { + STORM_LOG_WARN_COND(!points, "Creating a HyproPolytope where halfspaces AND points are given. The points will be ignored."); + return std::make_shared>(*halfspaces); + } else if(points) { + return std::make_shared>(*points); + } + STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Creating a HyproPolytope but no representation was given."); + return nullptr; + } + + template + HyproPolytope::HyproPolytope(HyproPolytope const& other) : internPolytope(other.internPolytope) { + // Intentionally left empty + } + + template + HyproPolytope::HyproPolytope(HyproPolytope&& other) : internPolytope(std::move(other.internPolytope)) { + // Intentionally left empty + } + + template + HyproPolytope::HyproPolytope(HyproPolytopeType const& p) : internPolytope(p) { + // Intentionally left empty + } + + template + HyproPolytope::HyproPolytope(HyproPolytopeType&& p) : internPolytope(p) { + // Intentionally left empty + } + + template + HyproPolytope::~HyproPolytope() { + // Intentionally left empty + } + + template + std::vector::Point> HyproPolytope::getVertices() const { + std::vector> hyproVertices = internPolytope.vertices(); + std::vector result; + result.reserve(hyproVertices.size()); + for(auto const& p : hyproVertices) { + result.push_back(storm::adapters::fromHypro(p.rawCoordinates())); + } + return result; + } + + template + std::vector> HyproPolytope::getHalfspaces() const { + std::vector> hyproHalfspaces = internPolytope.constraints(); + std::vector> result; + result.reserve(hyproHalfspaces.size()); + for(auto const& h : hyproHalfspaces) { + result.push_back(storm::adapters::fromHypro(h)); + } + return result; + } + + template + bool HyproPolytope::isEmpty() const { + return internPolytope.empty(); + } + + template + bool HyproPolytope::isUniversal() const { + return internPolytope.constraints().empty(); + } + + template + bool HyproPolytope::contains(Point const& point) const{ + return internPolytope.contains(storm::adapters::toHypro(point)); + } + + template + bool HyproPolytope::contains(std::shared_ptr> const& other) const { + STORM_LOG_THROW(other->isHyproPolytope(), storm::exceptions::InvalidArgumentException, "Invoked operation between a HyproPolytope and a different polytope implementation. This is not supported"); + return internPolytope.contains(dynamic_cast const&>(*other).internPolytope); + } + + template + std::shared_ptr> HyproPolytope::intersection(std::shared_ptr> const& rhs) const{ + STORM_LOG_THROW(rhs->isHyproPolytope(), storm::exceptions::InvalidArgumentException, "Invoked operation between a HyproPolytope and a different polytope implementation. This is not supported"); + return std::make_shared>(internPolytope.intersect(dynamic_cast const&>(*rhs).internPolytope)); + } + + template + std::shared_ptr> HyproPolytope::intersection(Halfspace const& halfspace) const{ + HyproPolytopeType result(internPolytope); + result.insert(storm::adapters::toHypro(halfspace)); + return std::make_shared>(std::move(result)); + } + + template + std::shared_ptr> HyproPolytope::convexUnion(std::shared_ptr> const& rhs) const{ + STORM_LOG_THROW(rhs->isHyproPolytope(), storm::exceptions::InvalidArgumentException, "Invoked operation between a HyproPolytope and a different polytope implementation. This is not supported"); + return std::make_shared>(internPolytope.unite(dynamic_cast const&>(*rhs).internPolytope)); + } + + template + std::shared_ptr> HyproPolytope::minkowskiSum(std::shared_ptr> const& rhs) const{ + STORM_LOG_THROW(rhs->isHyproPolytope(), storm::exceptions::InvalidArgumentException, "Invoked operation between a HyproPolytope and a different polytope implementation. This is not supported"); + return std::make_shared>(internPolytope.minkowskiSum(dynamic_cast const&>(*rhs).internPolytope)); + } + + template + std::shared_ptr> HyproPolytope::affineTransformation(std::vector const& matrix, Point const& vector) const{ + STORM_LOG_THROW(!matrix.empty(), storm::exceptions::InvalidArgumentException, "Invoked affine transformation with a matrix without rows."); + hypro::matrix_t hyproMatrix(matrix.size(), matrix.front().size()); + for(uint_fast64_t row = 0; row < matrix.size(); ++row) { + hyproMatrix.row(row) = storm::adapters::toHypro(matrix[row]); + } + return std::make_shared>(internPolytope.affineTransformation(std::move(hyproMatrix), storm::adapters::toHypro(vector))); + } + + template + std::pair::Point, bool> HyproPolytope::optimize(Point const& direction) const { + hypro::EvaluationResult evalRes = internPolytope.evaluate(storm::adapters::toHypro(direction)); + switch (evalRes.errorCode) { + case hypro::SOLUTION::FEAS: + return std::make_pair(storm::adapters::fromHypro(evalRes.optimumValue), true); + case hypro::SOLUTION::UNKNOWN: + STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected eror code for Polytope evaluation"); + return std::make_pair(Point(), false); + default: + //solution is infinity or infeasible + return std::make_pair(Point(), false); + } + } + + template + bool HyproPolytope::isHyproPolytope() const { + return true; + } + + template class HyproPolytope; +#ifdef STORM_HAVE_CARL + template class HyproPolytope; +#endif + } + } +} +#endif diff --git a/src/storage/geometry/HyproPolytope.h b/src/storage/geometry/HyproPolytope.h new file mode 100644 index 000000000..9388a6f45 --- /dev/null +++ b/src/storage/geometry/HyproPolytope.h @@ -0,0 +1,131 @@ +#ifndef STORM_STORAGE_GEOMETRY_HYPROPOLYTOPE_H_ +#define STORM_STORAGE_GEOMETRY_HYPROPOLYTOPE_H_ + +#include "src/storage/geometry/Polytope.h" +#include "src/adapters/HyproAdapter.h" + +#ifdef STORM_HAVE_HYPRO + +namespace storm { + namespace storage { + namespace geometry { + + template + class HyproPolytope : public Polytope { + public: + + typedef typename Polytope::Point Point; + typedef hypro::HPolytope HyproPolytopeType; + + /*! + * Creates a HyproPolytope from the given halfspaces or points. + * If both representations are given, one of them might be ignored + */ + static std::shared_ptr> create(boost::optional>> const& halfspaces, + boost::optional> const& points); + + /*! + * Creates a HyproPolytope from the given halfspaces + * The resulting polytope is defined as the intersection of the halfspaces. + */ + HyproPolytope(std::vector> const& halfspaces); + + /*! + * Creates a HyproPolytope from the given points. + * The resulting polytope is defined as the convex hull of the points' + */ + HyproPolytope(std::vector const& points); + + /*! + * Copy and move constructors + */ + HyproPolytope(HyproPolytope const& other); + HyproPolytope(HyproPolytope&& other); + + /*! + * Construction from a plain hypro polytope + */ + HyproPolytope(HyproPolytopeType const& polytope); + HyproPolytope(HyproPolytopeType&& polytope); + + ~HyproPolytope(); + + /*! + * Returns the vertices of this polytope. + */ + virtual std::vector getVertices() const override; + + /*! + * Returns the halfspaces of this polytope. + */ + virtual std::vector> getHalfspaces() const override; + + + /*! + * Returns whether this polytope is the empty set. + */ + virtual bool isEmpty() const override; + + /*! + * Returns whether this polytope is universal (i.e., equals R^n). + */ + virtual bool isUniversal() const override; + + /*! + * Returns true iff the given point is inside of the polytope. + */ + virtual bool contains(Point const& point) const override; + + /*! + * Returns true iff the given polytope is a subset of this polytope. + */ + virtual bool contains(std::shared_ptr> const& other) const override; + + /*! + * Intersects this polytope with rhs and returns the result. + */ + virtual std::shared_ptr> intersection(std::shared_ptr> const& rhs) const override; + virtual std::shared_ptr> intersection(Halfspace const& halfspace) const override; + + /*! + * Returns the convex union of this polytope and rhs. + */ + virtual std::shared_ptr> convexUnion(std::shared_ptr> const& rhs) const override; + + /*! + * Returns the minkowskiSum of this polytope and rhs. + */ + virtual std::shared_ptr> minkowskiSum(std::shared_ptr> const& rhs) const override; + + /*! + * Returns the affine transformation of this polytope P w.r.t. the given matrix A and vector b. + * The result is the set {A*x+b | x \in P} + * + * @param matrix the transformation matrix, given as vector of rows + * @param vector the transformation offset + */ + virtual std::shared_ptr> affineTransformation(std::vector const& matrix, Point const& vector) const override; + + /*! + * Finds an optimal point inside this polytope w.r.t. the given direction, i.e., + * a point that maximizes dotPorduct(point, direction). + * If such a point does not exist, the returned bool is false. There are two reasons for this: + * - The polytope is empty + * - The polytope is not bounded in the given direction + */ + virtual std::pair optimize(Point const& direction) const override; + + virtual bool isHyproPolytope() const override; + + private: + + HyproPolytopeType internPolytope; + }; + + } + } +} + +#endif /* STORM_HAVE_HYPRO */ + +#endif /* STORM_STORAGE_GEOMETRY_HYPROPOLYTOPE_H_ */ diff --git a/src/storage/geometry/Polytope.cpp b/src/storage/geometry/Polytope.cpp new file mode 100644 index 000000000..2228e00e8 --- /dev/null +++ b/src/storage/geometry/Polytope.cpp @@ -0,0 +1,278 @@ +#include "src/storage/geometry/Polytope.h" + +#include + +#include "src/adapters/CarlAdapter.h" +#include "src/adapters/HyproAdapter.h" +#include "src/storage/geometry/HyproPolytope.h" +#include "src/utility/macros.h" + +#include "src/exceptions/NotImplementedException.h" +#include "src/exceptions/IllegalFunctionCallException.h" + +namespace storm { + namespace storage { + namespace geometry { + + template + std::shared_ptr> Polytope::create(std::vector> const& halfspaces) { + return create(halfspaces, boost::none); + } + + template + std::shared_ptr> Polytope::create(std::vector const& points) { + return create(boost::none, points); + } + + template + std::shared_ptr> Polytope::createUniversalPolytope() { + return create(std::vector>(), boost::none); + } + + template + std::shared_ptr> Polytope::createEmptyPolytope() { + return create(boost::none, std::vector()); + } + + template + std::shared_ptr> Polytope::create(boost::optional>> const& halfspaces, + boost::optional> const& points) { +#ifdef STORM_HAVE_HYPRO + return HyproPolytope::create(halfspaces, points); +#endif + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "No polytope implementation specified."); + return nullptr; + } + + template + std::shared_ptr> Polytope::createDownwardClosure(std::vector const& points) { + if(points.empty()) { + // In this case, the downwardclosure is empty + return createEmptyPolytope(); + } + uint_fast64_t const dimensions = points.front().size(); + std::vector> halfspaces; + // We build the convex hull of the given points. + // However, auxiliary points (that will always be in the downward closure) are added. + // Then, the halfspaces of the resulting polytope are a superset of the halfspaces of the downward closure. + std::vector auxiliaryPoints = points; + auxiliaryPoints.reserve(auxiliaryPoints.size()*(1+dimensions)); + for(auto const& point : points) { + for(uint_fast64_t dim=0; dim(); + } + } + std::vector> auxiliaryHalfspaces = create(auxiliaryPoints)->getHalfspaces(); + // The downward closure is obtained by selecting the halfspaces for which the normal vector is non-negative (coordinate wise). + // Note that due to the auxiliary points, the polytope is never degenerated and thus there is always one unique halfspace-representation which is necessary: + // Consider, e.g., the convex hull of two points (1,0,0) and (0,1,1). + // There are multiple halfspace-representations for this set. In particular, there is one where all but one normalVectors have negative entries. + // However, the downward closure of this set can only be represented with 5 halfspaces. + for(auto& h : auxiliaryHalfspaces){ + bool allGreaterZero = true; + for(auto const& value : h.normalVector()) { + allGreaterZero &= (value >= storm::utility::zero()); + } + if(allGreaterZero){ + halfspaces.push_back(std::move(h)); + } + } + return create(halfspaces); + } + + template + Polytope::Polytope() { + // Intentionally left empty + } + + template + Polytope::~Polytope() { + // Intentionally left empty + } + + template + std::vector::Point> Polytope::getVertices() const{ + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Functionality not implemented."); + return std::vector(); + } + +#ifdef STORM_HAVE_CARL + template <> + std::vector::Point> Polytope::getVerticesInClockwiseOrder() const{ + std::vector vertices = getVertices(); + if(vertices.size()<=2) { + // In this case, every ordering is clockwise + return vertices; + } + STORM_LOG_THROW(vertices.front().size()==2, storm::exceptions::IllegalFunctionCallException, "Getting Vertices in clockwise order is only possible for a 2D-polytope."); + + std::vector neighborsOfVertices(vertices.size(), storm::storage::BitVector(vertices.size(), false)); + std::vector> halfspaces = getHalfspaces(); + for(auto const& h : halfspaces) { + storm::storage::BitVector verticesOnHalfspace(vertices.size(), false); + for(uint_fast64_t v = 0; v result; + result.reserve(vertices.size()); + storm::storage::BitVector unprocessedVertices(vertices.size(), true); + + uint_fast64_t currentVertex = 0; + for(uint_fast64_t v = 1; v + std::vector::Point> Polytope::getVerticesInClockwiseOrder() const{ + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Functionality not implemented."); + // Note that the implementation for RationalNumber above only works for exact ValueType since + // checking whether the distance between halfspace and point is zero is problematic otherwise. + return std::vector(); + } + + template + std::vector> Polytope::getHalfspaces() const { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Functionality not implemented."); + return std::vector>(); + } + + template + bool Polytope::isEmpty() const { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Functionality not implemented."); + return false; + } + + template + bool Polytope::isUniversal() const { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Functionality not implemented."); + return false; + } + + template + bool Polytope::contains(Point const& point) const { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Functionality not implemented."); + return false; + } + + template + bool Polytope::contains(std::shared_ptr> const& other) const { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Functionality not implemented."); + return false; + } + + template + std::shared_ptr> Polytope::intersection(std::shared_ptr> const& rhs) const { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Functionality not implemented."); + return nullptr; + } + + template + std::shared_ptr> Polytope::intersection(Halfspace const& halfspace) const { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Functionality not implemented."); + return nullptr; + } + + template + std::shared_ptr> Polytope::convexUnion(std::shared_ptr> const& rhs) const { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Functionality not implemented."); + return nullptr; + } + + template + std::shared_ptr> Polytope::minkowskiSum(std::shared_ptr> const& rhs) const { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Functionality not implemented."); + return nullptr; + } + + template + std::shared_ptr> Polytope::affineTransformation(std::vector const& matrix, Point const& vector) const { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Functionality not implemented."); + return nullptr; + } + + template + std::shared_ptr> Polytope::downwardClosure() const { + return createDownwardClosure(this->getVertices()); + } + + template + std::pair::Point, bool> Polytope::optimize(Point const& direction) const { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Functionality not implemented."); + return std::make_pair(Point(), false); + } + + template + template + std::shared_ptr> Polytope::convertNumberRepresentation() const { + if(isEmpty()) { + return Polytope::createEmptyPolytope(); + } + auto halfspaces = getHalfspaces(); + std::vector> halfspacesPrime; + halfspacesPrime.reserve(halfspaces.size()); + for(auto const& h : halfspaces) { + halfspacesPrime.emplace_back(storm::utility::vector::convertNumericVector(h.normalVector()), storm::utility::convertNumber(h.offset())); + } + + return Polytope::create(halfspacesPrime); + } + + template + std::string Polytope::toString(bool numbersAsDouble) const { + auto halfspaces = this->getHalfspaces(); + std::stringstream stream; + stream << "Polytope with " << halfspaces.size() << " Halfspaces" << (halfspaces.empty() ? "" : ":") << std::endl; + for (auto const& h : halfspaces) { + stream << " " << h.toString(numbersAsDouble) << std::endl; + } + return stream.str(); + } + + template + bool Polytope::isHyproPolytope() const { + return false; + } + + template class Polytope; + template std::shared_ptr> Polytope::convertNumberRepresentation() const; + +#ifdef STORM_HAVE_CARL + template class Polytope; + template std::shared_ptr> Polytope::convertNumberRepresentation() const; + template std::shared_ptr> Polytope::convertNumberRepresentation() const; + template std::shared_ptr> Polytope::convertNumberRepresentation() const; +#endif + } + } +} diff --git a/src/storage/geometry/Polytope.h b/src/storage/geometry/Polytope.h new file mode 100644 index 000000000..b27f83a3e --- /dev/null +++ b/src/storage/geometry/Polytope.h @@ -0,0 +1,158 @@ +#ifndef STORM_STORAGE_GEOMETRY_POLYTOPE_H_ +#define STORM_STORAGE_GEOMETRY_POLYTOPE_H_ + +#include +#include +#include + +#include "src/storage/geometry/Halfspace.h" + +namespace storm { + namespace storage { + namespace geometry { + + template + class Polytope { + public: + + typedef std::vector Point; + + ~Polytope(); + + /*! + * Creates a polytope from the given halfspaces. + * If the given vector of halfspaces is empty, the resulting polytope is universal (i.e., equals R^n). + */ + static std::shared_ptr> create(std::vector> const& halfspaces); + + /*! + * Creates a polytope from the given points (i.e., the convex hull of the points). + * If the vector of points is empty, the resulting polytope be empty. + */ + static std::shared_ptr> create(std::vector const& points); + + /*! + * Creates the universal polytope (i.e., the set R^n) + */ + static std::shared_ptr> createUniversalPolytope(); + + /*! + * Creates the empty polytope (i.e., emptyset) + */ + static std::shared_ptr> createEmptyPolytope(); + + /*! + * Creates the downward closure of the given points (i.e., the set { x | ex. y \in conv(points) : x<=y } + * If the vector of points is empty, the resulting polytope be empty. + */ + static std::shared_ptr> createDownwardClosure(std::vector const& points); + + /*! + * Returns the vertices of this polytope. + */ + virtual std::vector getVertices() const; + + /*! + * Returns the vertices of this 2D-polytope in clockwise order. + * An Exception is thrown if the dimension of this polytope is not two. + */ + virtual std::vector getVerticesInClockwiseOrder() const; + + /*! + * Returns the halfspaces of this polytope. + */ + virtual std::vector> getHalfspaces() const; + + /*! + * Returns whether this polytope is the empty set. + */ + virtual bool isEmpty() const; + + /*! + * Returns whether this polytope is universal (i.e., equals R^n). + */ + virtual bool isUniversal() const; + + /*! + * Returns true iff the given point is inside of the polytope. + */ + virtual bool contains(Point const& point) const; + + /*! + * Returns true iff the given polytope is a subset of this polytope. + */ + virtual bool contains(std::shared_ptr> const& other) const; + + /*! + * Intersects this polytope with rhs and returns the result. + */ + virtual std::shared_ptr> intersection(std::shared_ptr> const& rhs) const; + virtual std::shared_ptr> intersection(Halfspace const& halfspace) const; + + /*! + * Returns the convex union of this polytope and rhs. + */ + virtual std::shared_ptr> convexUnion(std::shared_ptr> const& rhs) const; + + /*! + * Returns the minkowskiSum of this polytope and rhs. + */ + virtual std::shared_ptr> minkowskiSum(std::shared_ptr> const& rhs) const; + + /*! + * Returns the affine transformation of this polytope P w.r.t. the given matrix A and vector b. + * The result is the set {A*x+b | x \in P} + * + * @param matrix the transformation matrix, given as vector of rows + * @param vector the transformation offset + */ + virtual std::shared_ptr> affineTransformation(std::vector const& matrix, Point const& vector) const; + + /*! + * Returns the downward closure of this, i.e., the set { x | ex. y \in P : x<=y} where P is this Polytope. + */ + virtual std::shared_ptr> downwardClosure() const; + + /*! + * Finds an optimal point inside this polytope w.r.t. the given direction, i.e., + * a point that maximizes dotPorduct(point, direction). + * If such a point does not exist, the returned bool is false. There are two reasons for this: + * - The polytope is empty + * - The polytope is not bounded in the given direction + */ + virtual std::pair optimize(Point const& direction) const; + + /*! + * converts the intern number representation of the polytope to the given target type + */ + template + std::shared_ptr> convertNumberRepresentation() const; + + /* + * Returns a string repre/Users/tim/storm/src/storage/geometry/Polytope.h:129:17: 'virtual' cannot be specified on member function templatessentation of this polytope. + * If the given flag is true, the occurring numbers are converted to double before printing to increase readability + */ + virtual std::string toString(bool numbersAsDouble = false) const; + + + virtual bool isHyproPolytope() const; + + protected: + + Polytope(); + + private: + /*! + * Creates a polytope from the given halfspaces or vertices. + */ + static std::shared_ptr> create(boost::optional>> const& halfspaces, + boost::optional> const& points); + + + }; + + } + } +} + +#endif /* STORM_STORAGE_GEOMETRY_POLYTOPE_H_ */ diff --git a/src/storage/jani/JSONExporter.cpp b/src/storage/jani/JSONExporter.cpp index e0c37fabc..185eb8b13 100644 --- a/src/storage/jani/JSONExporter.cpp +++ b/src/storage/jani/JSONExporter.cpp @@ -291,6 +291,11 @@ namespace storm { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Jani currently does not support conversion of an LRA reward formula"); } + + boost::any FormulaToJaniJson::visit(storm::logic::MultiObjectiveFormula const& f, boost::any const& data) const { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Jani currently does not support conversion of a multi-objective formula"); + } + boost::any FormulaToJaniJson::visit(storm::logic::NextFormula const& f, boost::any const& data) const { modernjson::json opDecl; opDecl["op"] = "U"; @@ -370,6 +375,11 @@ namespace storm { return opDecl; } + boost::any FormulaToJaniJson::visit(storm::logic::TotalRewardFormula const& f, boost::any const& data) const { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Jani currently does not support a total reward formula"); + } + + boost::any FormulaToJaniJson::visit(storm::logic::UnaryBooleanStateFormula const& f, boost::any const& data) const { modernjson::json opDecl; storm::logic::UnaryBooleanStateFormula::OperatorType op = f.getOperator(); diff --git a/src/storage/jani/JSONExporter.h b/src/storage/jani/JSONExporter.h index 3f82984be..99e229399 100644 --- a/src/storage/jani/JSONExporter.h +++ b/src/storage/jani/JSONExporter.h @@ -49,9 +49,11 @@ namespace storm { virtual boost::any visit(storm::logic::InstantaneousRewardFormula const& f, boost::any const& data) const; virtual boost::any visit(storm::logic::LongRunAverageOperatorFormula const& f, boost::any const& data) const; virtual boost::any visit(storm::logic::LongRunAverageRewardFormula const& f, boost::any const& data) const; + virtual boost::any visit(storm::logic::MultiObjectiveFormula const& f, boost::any const& data) const; virtual boost::any visit(storm::logic::NextFormula const& f, boost::any const& data) const; virtual boost::any visit(storm::logic::ProbabilityOperatorFormula const& f, boost::any const& data) const; virtual boost::any visit(storm::logic::RewardOperatorFormula const& f, boost::any const& data) const; + virtual boost::any visit(storm::logic::TotalRewardFormula const& f, boost::any const& data) const; virtual boost::any visit(storm::logic::UnaryBooleanStateFormula const& f, boost::any const& data) const; virtual boost::any visit(storm::logic::UntilFormula const& f, boost::any const& data) const; diff --git a/src/storm.cpp b/src/storm.cpp index 236d15a7b..822ae104e 100644 --- a/src/storm.cpp +++ b/src/storm.cpp @@ -1,12 +1,11 @@ // Include other headers. -#include #include "src/exceptions/BaseException.h" #include "src/utility/macros.h" #include "src/cli/cli.h" #include "src/utility/initialize.h" +#include "src/utility/Stopwatch.h" #include "src/settings/SettingsManager.h" -#include "src/settings/modules/GeneralSettings.h" /*! * Main entry point of the executable storm. @@ -14,7 +13,8 @@ int main(const int argc, const char** argv) { try { - auto start = std::chrono::high_resolution_clock::now(); + storm::utility::Stopwatch runtimeStopWatch; + storm::utility::setUp(); storm::cli::printHeader("Storm", argc, argv); storm::settings::initializeAll("Storm", "storm"); @@ -28,12 +28,11 @@ int main(const int argc, const char** argv) { // All operations have now been performed, so we clean up everything and terminate. storm::utility::cleanUp(); - auto end = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(end - start); - auto durationSec = std::chrono::duration_cast(end - start); + if(storm::settings::getModule().isPrintTimingsSet()) { - std::cout << "Overal runtime: " << duration.count() << " ms. (approximately " << durationSec.count() << " seconds)." << std::endl; + std::cout << "Overall runtime: " << runtimeStopWatch << " seconds." << std::endl; } + return 0; } catch (storm::exceptions::BaseException const& exception) { STORM_LOG_ERROR("An exception caused Storm to terminate. The message of the exception is: " << exception.what()); diff --git a/src/transformer/EndComponentEliminator.h b/src/transformer/EndComponentEliminator.h new file mode 100644 index 000000000..a73c5cf0e --- /dev/null +++ b/src/transformer/EndComponentEliminator.h @@ -0,0 +1,172 @@ +#ifndef STORM_TRANSFORMER_ENDCOMPONENTELIMINATOR_H +#define STORM_TRANSFORMER_ENDCOMPONENTELIMINATOR_H + + +#include "src/utility/constants.h" +#include "src/utility/macros.h" +#include "src/utility/graph.h" +#include "src/storage/MaximalEndComponentDecomposition.h" + +namespace storm { + namespace transformer { + + template + class EndComponentEliminator { + public: + + struct EndComponentEliminatorReturnType { + // The resulting matrix + storm::storage::SparseMatrix matrix; + // Index mapping that gives for each row of the resulting matrix the corresponding row in the original matrix. + // For the empty rows added to EC states, an arbitrary row of the original matrix that stays inside the EC is given. + std::vector newToOldRowMapping; + // Gives for each state (=rowGroup) of the original matrix the corresponding state in the resulting matrix. + // States of a removed ECs are mapped to the state that substitutes the EC. + // If the given state does not exist in the result (i.e., it is not in the provided subsystem), the returned value will be std::numeric_limits::max(), i.e., an invalid index. + std::vector oldToNewStateMapping; + }; + + /* + * Identifies end components and substitutes them by a single state. + * + * Only states in the given subsystem are kept. Transitions leading to a state outside of the subsystem will be + * removed (but the corresponding row is kept, possibly yielding empty rows). + * The ECs are then identified on the subsystem. + * + * Only ECs for which possibleECRows is true for all choices are considered. + * Furthermore, the rows that contain a transition leading outside of the subsystem are not considered for an EC. + * + * For each such EC (that is not contained in another EC), we add a new state and redirect all incoming and outgoing + * transitions of the EC to (and from) this state. + * If addEmptyRowStates is true for at least one state of an eliminated EC, an empty row is added to the new state (representing the choice to stay at the EC forever). + */ + static EndComponentEliminatorReturnType transform(storm::storage::SparseMatrix const& originalMatrix, storm::storage::BitVector const& subsystemStates, storm::storage::BitVector const& possibleECRows, storm::storage::BitVector const& addEmptyRowStates) { + STORM_LOG_DEBUG("Invoked EndComponentEliminator on matrix with " << originalMatrix.getRowGroupCount() << " row groups."); + + storm::storage::MaximalEndComponentDecomposition ecs = computeECs(originalMatrix, possibleECRows, subsystemStates); + + //further shrink the set of kept states by removing all states that are part of an EC + storm::storage::BitVector keptStates = subsystemStates; + for (auto const& ec : ecs) { + for (auto const& stateActionsPair : ec) { + keptStates.set(stateActionsPair.first, false); + } + } + STORM_LOG_DEBUG("Found " << ecs.size() << " end components to eliminate. Keeping " << keptStates.getNumberOfSetBits() << " of " << keptStates.size() << " original states."); + + EndComponentEliminatorReturnType result; + std::vector newRowGroupIndices; + result.oldToNewStateMapping = std::vector(originalMatrix.getRowGroupCount(), std::numeric_limits::max()); + storm::storage::BitVector emptyRows(originalMatrix.getRowCount(), false); // will be resized as soon as the rowCount of the resulting matrix is known + + for(auto keptState : keptStates) { + result.oldToNewStateMapping[keptState] = newRowGroupIndices.size(); // i.e., the current number of processed states + newRowGroupIndices.push_back(result.newToOldRowMapping.size()); // i.e., the current number of processed rows + for(uint_fast64_t oldRow = originalMatrix.getRowGroupIndices()[keptState]; oldRow < originalMatrix.getRowGroupIndices()[keptState + 1]; ++oldRow) { + result.newToOldRowMapping.push_back(oldRow); + } + } + for (auto const& ec : ecs) { + newRowGroupIndices.push_back(result.newToOldRowMapping.size()); + bool ecGetsEmptyRow = false; + for (auto const& stateActionsPair : ec) { + result.oldToNewStateMapping[stateActionsPair.first] = newRowGroupIndices.size()-1; + for(uint_fast64_t row = originalMatrix.getRowGroupIndices()[stateActionsPair.first]; row < originalMatrix.getRowGroupIndices()[stateActionsPair.first +1]; ++row) { + if(stateActionsPair.second.find(row) == stateActionsPair.second.end()) { + result.newToOldRowMapping.push_back(row); + } + } + ecGetsEmptyRow |= addEmptyRowStates.get(stateActionsPair.first); + } + if(ecGetsEmptyRow) { + STORM_LOG_ASSERT(result.newToOldRowMapping.size() < originalMatrix.getRowCount(), "Didn't expect to see more rows in the reduced matrix than in the original one."); + emptyRows.set(result.newToOldRowMapping.size(), true); + result.newToOldRowMapping.push_back(*ec.begin()->second.begin()); + } + } + newRowGroupIndices.push_back(result.newToOldRowMapping.size()); + emptyRows.resize(result.newToOldRowMapping.size()); + + result.matrix = buildTransformedMatrix(originalMatrix, newRowGroupIndices, result.newToOldRowMapping, result.oldToNewStateMapping, emptyRows); + STORM_LOG_DEBUG("EndComponentEliminator is done. Resulting matrix has " << result.matrix.getRowGroupCount() << " row groups."); + return result; + } + + private: + + static storm::storage::MaximalEndComponentDecomposition computeECs(storm::storage::SparseMatrix const& originalMatrix, storm::storage::BitVector const& possibleECRows, storm::storage::BitVector const& subsystemStates) { + // Get an auxiliary matrix to identify the correct end components w.r.t. the possible EC rows and the subsystem. + // This is done by redirecting choices that can never be part of an EC to a sink state. + // Such choices are either non-possible EC rows or rows that lead to a state that is not in the subsystem. + uint_fast64_t sinkState = originalMatrix.getRowGroupCount(); + storm::storage::SparseMatrixBuilder builder(originalMatrix.getRowCount() + 1, originalMatrix.getColumnCount() + 1, originalMatrix.getEntryCount() + 1, false, true, originalMatrix.getRowGroupCount()+1); + uint_fast64_t row = 0; + for(uint_fast64_t rowGroup = 0; rowGroup < originalMatrix.getRowGroupCount(); ++rowGroup) { + builder.newRowGroup(row); + for (; row < originalMatrix.getRowGroupIndices()[rowGroup+1]; ++row ){ + bool keepRow = possibleECRows.get(row); + if(keepRow) { //Also check whether all successors are in the subsystem + for(auto const& entry : originalMatrix.getRow(row)){ + keepRow &= subsystemStates.get(entry.getColumn()); + } + } + if(keepRow) { + for(auto const& entry : originalMatrix.getRow(row)){ + builder.addNextValue(row, entry.getColumn(), entry.getValue()); + } + } else { + builder.addNextValue(row, sinkState, storm::utility::one()); + } + } + } + builder.newRowGroup(row); + builder.addNextValue(row, sinkState, storm::utility::one()); + storm::storage::SparseMatrix auxiliaryMatrix = builder.build(originalMatrix.getRowCount() + 1, originalMatrix.getColumnCount()+1, originalMatrix.getRowGroupCount() +1); + storm::storage::SparseMatrix backwardsTransitions = auxiliaryMatrix.transpose(true); + storm::storage::BitVector sinkStateAsBitVector(auxiliaryMatrix.getRowGroupCount(), false); + sinkStateAsBitVector.set(sinkState); + storm::storage::BitVector auxSubsystemStates = subsystemStates; + auxSubsystemStates.resize(subsystemStates.size() + 1, true); + // The states for which sinkState is reachable under every scheduler can not be part of an EC + auxSubsystemStates &= ~(storm::utility::graph::performProbGreater0A(auxiliaryMatrix, auxiliaryMatrix.getRowGroupIndices(), backwardsTransitions, auxSubsystemStates, sinkStateAsBitVector)); + return storm::storage::MaximalEndComponentDecomposition(auxiliaryMatrix, backwardsTransitions, auxSubsystemStates); + } + + static storm::storage::SparseMatrix buildTransformedMatrix(storm::storage::SparseMatrix const& originalMatrix, + std::vector const& newRowGroupIndices, + std::vector const& newToOldRowMapping, + std::vector const& oldToNewStateMapping, + storm::storage::BitVector const& emptyRows) { + + uint_fast64_t numRowGroups = newRowGroupIndices.size()-1; + uint_fast64_t newRow = 0; + storm::storage::SparseMatrixBuilder builder(newToOldRowMapping.size(), numRowGroups, originalMatrix.getEntryCount(), false, true, numRowGroups); + for(uint_fast64_t newRowGroup = 0; newRowGroup < numRowGroups; ++newRowGroup) { + builder.newRowGroup(newRow); + for(; newRow < newRowGroupIndices[newRowGroup+1]; ++newRow) { + if(!emptyRows.get(newRow)) { + // Make sure that the entries for this row are inserted in the right order. + // Also, transitions to the same EC need to be merged and transitions to states that are erased need to be ignored + std::map sortedEntries; + for(auto const& entry : originalMatrix.getRow(newToOldRowMapping[newRow])){ + uint_fast64_t newColumn = oldToNewStateMapping[entry.getColumn()]; + if(newColumn < numRowGroups) { + auto insertResult = sortedEntries.insert(std::make_pair(newColumn, entry.getValue())); + if(!insertResult.second) { + // We have already seen an entry with this column. ==> merge transitions + insertResult.first->second += entry.getValue(); + } + } + } + for(auto const& sortedEntry : sortedEntries) { + builder.addNextValue(newRow, sortedEntry.first, sortedEntry.second); + } + } + } + } + return builder.build(newToOldRowMapping.size(), numRowGroups, numRowGroups); + } + }; + } +} +#endif // STORM_TRANSFORMER_ENDCOMPONENTREMOVER_H diff --git a/src/transformer/StateDuplicator.h b/src/transformer/StateDuplicator.h new file mode 100644 index 000000000..940ebef0b --- /dev/null +++ b/src/transformer/StateDuplicator.h @@ -0,0 +1,268 @@ +#ifndef STORM_TRANSFORMER_STATEDUPLICATOR_H +#define STORM_TRANSFORMER_STATEDUPLICATOR_H + + +#include +#include + +#include "src/models/sparse/StandardRewardModel.h" +#include "src/utility/constants.h" +#include "src/utility/graph.h" +#include "src/utility/macros.h" +#include "src/utility/vector.h" +#include "src/models/sparse/Dtmc.h" +#include "src/models/sparse/Mdp.h" +#include "src/models/sparse/Ctmc.h" +#include "src/models/sparse/MarkovAutomaton.h" + + +namespace storm { + namespace transformer { + + /* + * Duplicates the state space of the given model and redirects the incoming transitions of gateStates of the first copy to the gateStates of the second copy. + * Only states reachable from the initial states are kept. + */ + template + class StateDuplicator { + public: + + struct StateDuplicatorReturnType { + std::shared_ptr model; // The resulting model + storm::storage::BitVector firstCopy; // The states of the resulting model that correspond to the first copy + storm::storage::BitVector secondCopy; // The states of the resulting model that correspond to the second copy + storm::storage::BitVector gateStates; // The gate states of the resulting model + std::vector newToOldStateIndexMapping; // Gives for each state in the resulting model the corresponding state in the original model + std::vector firstCopyOldToNewStateIndexMapping; //Maps old indices of states in the first copy to their new indices + std::vector secondCopyOldToNewStateIndexMapping; //Maps old indices of states in the second copy to their new indices + storm::storage::BitVector duplicatedStates; // The states in the original model that have been duplicated + storm::storage::BitVector reachableStates; // The states in the original model that are reachable from the initial state + }; + + /* + * Duplicates the state space of the given model and redirects the incoming transitions of gateStates of the first copy to the gateStates of the second copy. + * + * Note that only reachable states are kept. + * Gate states will always belong to the second copy. + * Rewards and labels are duplicated accordingly. + * However, the non-gateStates in the second copy will not get the label for initial states. + * + * @param originalModel The model to be duplicated + * @param gateStates The states for which the incoming transitions are redirected + */ + static StateDuplicatorReturnType transform(SparseModelType const& originalModel, storm::storage::BitVector const& gateStates) { + STORM_LOG_DEBUG("Invoked state duplicator on model with " << originalModel.getNumberOfStates() << " states."); + StateDuplicatorReturnType result; + + // Collect some data for the result + initializeTransformation(originalModel, gateStates, result); + + // Transform the ingedients of the model + storm::storage::SparseMatrix matrix = transformMatrix(originalModel.getTransitionMatrix(), result, gateStates); + storm::models::sparse::StateLabeling labeling(matrix.getRowGroupCount()); + for(auto const& label : originalModel.getStateLabeling().getLabels()){ + storm::storage::BitVector newBitVectorForLabel = transformStateBitVector(originalModel.getStateLabeling().getStates(label), result); + if(label=="init"){ + newBitVectorForLabel &= (result.firstCopy | result.gateStates); + } + labeling.addLabel(label, std::move(newBitVectorForLabel)); + } + std::unordered_map rewardModels; + for(auto const& rewardModel : originalModel.getRewardModels()){ + rewardModels.insert(std::make_pair(rewardModel.first, transformRewardModel(rewardModel.second, originalModel.getTransitionMatrix().getRowGroupIndices(), result, gateStates))); + } + boost::optional> choiceLabeling; + if(originalModel.hasChoiceLabeling()){ + choiceLabeling = transformActionValueVector(originalModel.getChoiceLabeling(), originalModel.getTransitionMatrix().getRowGroupIndices(), result); + } + + result.model = std::make_shared(createTransformedModel(originalModel, result, matrix, labeling, rewardModels, choiceLabeling)); + STORM_LOG_DEBUG("State duplicator is done. Resulting model has " << result.model->getNumberOfStates() << " states, where " << result.firstCopy.getNumberOfSetBits() << " are in the first copy."); + return result; + } + + private: + + static void initializeTransformation(SparseModelType const& originalModel, storm::storage::BitVector const& gateStates, StateDuplicatorReturnType& result) { + + storm::storage::BitVector noStates(originalModel.getNumberOfStates(), false); + // Get the states that are reachable without visiting a gateState + storm::storage::BitVector statesForFirstCopy = storm::utility::graph::getReachableStates(originalModel.getTransitionMatrix(), originalModel.getInitialStates(), ~gateStates, noStates); + + // Get the states reachable from gateStates + storm::storage::BitVector statesForSecondCopy = storm::utility::graph::getReachableStates(originalModel.getTransitionMatrix(), gateStates, ~noStates, noStates); + + result.duplicatedStates = statesForFirstCopy & statesForSecondCopy; + result.reachableStates = statesForFirstCopy | statesForSecondCopy; + + uint_fast64_t numStates = statesForFirstCopy.getNumberOfSetBits() + statesForSecondCopy.getNumberOfSetBits(); + result.firstCopy = statesForFirstCopy % result.reachableStates; // only consider reachable states + result.firstCopy.resize(numStates, false); // the new states do NOT belong to the first copy + result.secondCopy = (statesForSecondCopy & (~statesForFirstCopy)) % result.reachableStates; // only consider reachable states + result.secondCopy.resize(numStates, true); // the new states DO belong to the second copy + STORM_LOG_ASSERT((result.firstCopy^result.secondCopy).full(), "firstCopy and secondCopy do not partition the state space."); + + // Get the state mappings. + // We initialize them with illegal values to assert that we don't get a valid + // state when given e.g. an unreachable state or a state from the other copy. + result.newToOldStateIndexMapping = std::vector(numStates, std::numeric_limits::max()); + result.firstCopyOldToNewStateIndexMapping = std::vector(originalModel.getNumberOfStates(), std::numeric_limits::max()); + result.secondCopyOldToNewStateIndexMapping = std::vector(originalModel.getNumberOfStates(), std::numeric_limits::max()); + uint_fast64_t newState = 0; + for(auto const& oldState : result.reachableStates){ + result.newToOldStateIndexMapping[newState] = oldState; + if(statesForFirstCopy.get(oldState)){ + result.firstCopyOldToNewStateIndexMapping[oldState] = newState; + } else { + result.secondCopyOldToNewStateIndexMapping[oldState] = newState; + } + ++newState; + } + // The remaining states are duplicates. All these states belong to the second copy. + + for(auto const& oldState : result.duplicatedStates) { + result.newToOldStateIndexMapping[newState] = oldState; + result.secondCopyOldToNewStateIndexMapping[oldState] = newState; + ++newState; + } + STORM_LOG_ASSERT(newState == numStates, "Unexpected state Indices"); + + result.gateStates = transformStateBitVector(gateStates, result); + } + + template + static typename std::enable_if>::value, RewardModelType>::type + transformRewardModel(RewardModelType const& originalRewardModel, std::vector const& originalRowGroupIndices, StateDuplicatorReturnType const& result, storm::storage::BitVector const& gateStates) { + boost::optional> stateRewardVector; + boost::optional> stateActionRewardVector; + boost::optional> transitionRewardMatrix; + if(originalRewardModel.hasStateRewards()){ + stateRewardVector = transformStateValueVector(originalRewardModel.getStateRewardVector(), result); + } + if(originalRewardModel.hasStateActionRewards()){ + stateActionRewardVector = transformActionValueVector(originalRewardModel.getStateActionRewardVector(), originalRowGroupIndices, result); + } + if(originalRewardModel.hasTransitionRewards()){ + transitionRewardMatrix = transformMatrix(originalRewardModel.getTransitionRewardMatrix(), result, gateStates); + } + return RewardModelType(std::move(stateRewardVector), std::move(stateActionRewardVector), std::move(transitionRewardMatrix)); + } + + template + static storm::storage::SparseMatrix transformMatrix(storm::storage::SparseMatrix const& originalMatrix, StateDuplicatorReturnType const& result, storm::storage::BitVector const& gateStates) { + // Build the builder + uint_fast64_t numStates = result.newToOldStateIndexMapping.size(); + uint_fast64_t numRows = 0; + uint_fast64_t numEntries = 0; + for(auto const& oldState : result.newToOldStateIndexMapping){ + numRows += originalMatrix.getRowGroupSize(oldState); + numEntries += originalMatrix.getRowGroupEntryCount(oldState); + } + storm::storage::SparseMatrixBuilder builder(numRows, numStates, numEntries, true, !originalMatrix.hasTrivialRowGrouping(), originalMatrix.hasTrivialRowGrouping() ? 0 : numStates); + + // Fill in the data + uint_fast64_t newRow = 0; + for(uint_fast64_t newState = 0; newState < numStates; ++newState){ + if(!originalMatrix.hasTrivialRowGrouping()){ + builder.newRowGroup(newRow); + } + uint_fast64_t oldState = result.newToOldStateIndexMapping[newState]; + for (uint_fast64_t oldRow = originalMatrix.getRowGroupIndices()[oldState]; oldRow < originalMatrix.getRowGroupIndices()[oldState+1]; ++oldRow){ + for(auto const& entry : originalMatrix.getRow(oldRow)){ + if(result.firstCopy.get(newState) && !gateStates.get(entry.getColumn())){ + builder.addNextValue(newRow, result.firstCopyOldToNewStateIndexMapping[entry.getColumn()], entry.getValue()); + } else if (!result.duplicatedStates.get(entry.getColumn())){ + builder.addNextValue(newRow, result.secondCopyOldToNewStateIndexMapping[entry.getColumn()], entry.getValue()); + } + } + //To add values in the right order, transitions to duplicated states have to be added in a second run. + for(auto const& entry : originalMatrix.getRow(oldRow)){ + if (result.secondCopy.get(newState) && result.duplicatedStates.get(entry.getColumn())){ + builder.addNextValue(newRow, result.secondCopyOldToNewStateIndexMapping[entry.getColumn()], entry.getValue()); + } + } + ++newRow; + } + } + + return builder.build(); + } + + + template + static std::vector transformActionValueVector(std::vectorconst& originalVector, std::vector const& originalRowGroupIndices, StateDuplicatorReturnType const& result) { + uint_fast64_t numActions = 0; + for(auto const& oldState : result.newToOldStateIndexMapping){ + numActions += originalRowGroupIndices[oldState+1] - originalRowGroupIndices[oldState]; + } + std::vector v; + v.reserve(numActions); + for(auto const& oldState : result.newToOldStateIndexMapping){ + for (uint_fast64_t oldAction = originalRowGroupIndices[oldState]; oldAction < originalRowGroupIndices[oldState+1]; ++oldAction){ + v.push_back(originalVector[oldAction]); + } + } + STORM_LOG_ASSERT(v.size() == numActions, "Unexpected vector size."); + return v; + } + + template + static std::vector transformStateValueVector(std::vector const& originalVector, StateDuplicatorReturnType const& result) { + uint_fast64_t numStates = result.newToOldStateIndexMapping.size(); + std::vector v; + v.reserve(numStates); + for(auto const& oldState : result.newToOldStateIndexMapping){ + v.push_back(originalVector[oldState]); + } + STORM_LOG_ASSERT(v.size() == numStates, "Unexpected vector size."); + return v; + } + + static storm::storage::BitVector transformStateBitVector(storm::storage::BitVector const& originalBitVector, StateDuplicatorReturnType const& result) { + uint_fast64_t numStates = result.newToOldStateIndexMapping.size(); + storm::storage::BitVector bv(numStates); + for(uint_fast64_t newState = 0; newState < numStates; ++newState){ + uint_fast64_t oldState = result.newToOldStateIndexMapping[newState]; + bv.set(newState, originalBitVector.get(oldState)); + } + return bv; + } + + + template + static typename std::enable_if< + std::is_same>::value || + std::is_same>::value || + std::is_same>::value, + MT>::type + createTransformedModel(MT const& originalModel, + StateDuplicatorReturnType const& result, + storm::storage::SparseMatrix& matrix, + storm::models::sparse::StateLabeling& stateLabeling, + std::unordered_map& rewardModels, + boost::optional>& choiceLabeling ) { + return MT(std::move(matrix), std::move(stateLabeling), std::move(rewardModels), std::move(choiceLabeling)); + } + + template + static typename std::enable_if< + std::is_same>::value, + MT>::type + createTransformedModel(MT const& originalModel, + StateDuplicatorReturnType const& result, + storm::storage::SparseMatrix& matrix, + storm::models::sparse::StateLabeling& stateLabeling, + std::unordered_map& rewardModels, + boost::optional>& choiceLabeling ) { + storm::storage::BitVector markovianStates = transformStateBitVector(originalModel.getMarkovianStates(), result); + std::vector exitRates = transformStateValueVector(originalModel.getExitRates(), result); + return MT(std::move(matrix), std::move(stateLabeling), std::move(markovianStates), std::move(exitRates), true, std::move(rewardModels), std::move(choiceLabeling)); + } + + + }; + } +} +#endif // STORM_TRANSFORMER_STATEDUPLICATOR_H diff --git a/src/transformer/SubsystemBuilder.h b/src/transformer/SubsystemBuilder.h new file mode 100644 index 000000000..01e70aea9 --- /dev/null +++ b/src/transformer/SubsystemBuilder.h @@ -0,0 +1,193 @@ +#ifndef STORM_TRANSFORMER_SUBSYSTEMBUILDER_H +#define STORM_TRANSFORMER_SUBSYSTEMBUILDER_H + + +#include +#include + +#include "src/models/sparse/StandardRewardModel.h" +#include "src/utility/constants.h" +#include "src/utility/graph.h" +#include "src/utility/macros.h" +#include "src/utility/vector.h" +#include "src/models/sparse/Dtmc.h" +#include "src/models/sparse/Mdp.h" +#include "src/models/sparse/Ctmc.h" +#include "src/models/sparse/MarkovAutomaton.h" + +#include "src/exceptions/InvalidArgumentException.h" +#include "src/exceptions/InvalidStateException.h" + +namespace storm { + namespace transformer { + + /* + * Removes all states that are not part of the subsystem + */ + template + class SubsystemBuilder { + public: + + struct SubsystemBuilderReturnType { + // The resulting model + std::shared_ptr model; + // Gives for each state in the resulting model the corresponding state in the original model. + std::vector newToOldStateIndexMapping; + // marks the actions of the original model that are still available in the subsystem + storm::storage::BitVector keptActions; + }; + + /* + * Removes all states and actions that are not part of the subsystem. + * A state is part of the subsystem iff it is selected in subsystemStates. + * An action is part of the subsystem iff + * * it is selected in subsystemActions AND + * * it originates from a state that is part of the subsystem AND + * * it does not contain a transition leading to a state outside of the subsystem. + * + * If this introduces a deadlock state (i.e., a state without an action) an exception is thrown. + * + * @param originalModel The original model. + * @param subsystemStates The selected states. + * @param subsystemActions The selected actions + */ + static SubsystemBuilderReturnType transform(SparseModelType const& originalModel, storm::storage::BitVector const& subsystemStates, storm::storage::BitVector const& subsystemActions) { + STORM_LOG_DEBUG("Invoked subsystem builder on model with " << originalModel.getNumberOfStates() << " states."); + STORM_LOG_THROW(!(originalModel.getInitialStates() & subsystemStates).empty(), storm::exceptions::InvalidArgumentException, "The subsystem would not contain any initial states"); + SubsystemBuilderReturnType result; + + uint_fast64_t subsystemStateCount = subsystemStates.getNumberOfSetBits(); + STORM_LOG_THROW(subsystemStateCount != 0, storm::exceptions::InvalidArgumentException, "Invoked SubsystemBuilder for an empty subsystem."); + if(subsystemStateCount == subsystemStates.size() && subsystemActions.full()) { + result.model = std::make_shared(originalModel); + result.newToOldStateIndexMapping = storm::utility::vector::buildVectorForRange(0, result.model->getNumberOfStates()); + result.keptActions = storm::storage::BitVector(result.model->getTransitionMatrix().getRowCount(), true); + return result; + } + + result.newToOldStateIndexMapping.reserve(subsystemStateCount); + result.keptActions = storm::storage::BitVector(originalModel.getTransitionMatrix().getRowCount(), false); + for(auto subsysState : subsystemStates) { + result.newToOldStateIndexMapping.push_back(subsysState); + bool stateHasOneChoiceLeft = false; + for(uint_fast64_t row = subsystemActions.getNextSetIndex(originalModel.getTransitionMatrix().getRowGroupIndices()[subsysState]); row < originalModel.getTransitionMatrix().getRowGroupIndices()[subsysState+1]; row = subsystemActions.getNextSetIndex(row+1)) { + bool allRowEntriesStayInSubsys = true; + for(auto const& entry : originalModel.getTransitionMatrix().getRow(row)) { + if(!subsystemStates.get(entry.getColumn())) { + allRowEntriesStayInSubsys = false; + break; + } + } + stateHasOneChoiceLeft |= allRowEntriesStayInSubsys; + result.keptActions.set(row, allRowEntriesStayInSubsys); + } + STORM_LOG_THROW(stateHasOneChoiceLeft, storm::exceptions::InvalidArgumentException, "The subsystem would contain a deadlock state."); + } + + // Transform the ingedients of the model + storm::storage::SparseMatrix matrix = transformMatrix(originalModel.getTransitionMatrix(), subsystemStates, result.keptActions); + storm::models::sparse::StateLabeling labeling(matrix.getRowGroupCount()); + for(auto const& label : originalModel.getStateLabeling().getLabels()){ + storm::storage::BitVector newBitVectorForLabel = transformStateBitVector(originalModel.getStateLabeling().getStates(label), subsystemStates); + labeling.addLabel(label, std::move(newBitVectorForLabel)); + } + std::unordered_map rewardModels; + for(auto const& rewardModel : originalModel.getRewardModels()){ + rewardModels.insert(std::make_pair(rewardModel.first, transformRewardModel(rewardModel.second, subsystemStates, result.keptActions))); + } + boost::optional> choiceLabeling; + if(originalModel.hasChoiceLabeling()){ + choiceLabeling = transformActionValueVector(originalModel.getChoiceLabeling(), result.keptActions); + } + result.model = std::make_shared(createTransformedModel(originalModel, subsystemStates, matrix, labeling, rewardModels, choiceLabeling)); + STORM_LOG_DEBUG("Subsystem Builder is done. Resulting model has " << result.model->getNumberOfStates() << " states."); + return result; + } + + private: + template + static typename std::enable_if>::value, RewardModelType>::type + transformRewardModel(RewardModelType const& originalRewardModel, storm::storage::BitVector const& subsystem, storm::storage::BitVector const& subsystemActions) { + boost::optional> stateRewardVector; + boost::optional> stateActionRewardVector; + boost::optional> transitionRewardMatrix; + if(originalRewardModel.hasStateRewards()){ + stateRewardVector = transformStateValueVector(originalRewardModel.getStateRewardVector(), subsystem); + } + if(originalRewardModel.hasStateActionRewards()){ + stateActionRewardVector = transformActionValueVector(originalRewardModel.getStateActionRewardVector(), subsystemActions); + } + if(originalRewardModel.hasTransitionRewards()){ + transitionRewardMatrix = transformMatrix(originalRewardModel.getTransitionRewardMatrix(), subsystem, subsystemActions); + } + return RewardModelType(std::move(stateRewardVector), std::move(stateActionRewardVector), std::move(transitionRewardMatrix)); + } + + template + static storm::storage::SparseMatrix transformMatrix(storm::storage::SparseMatrix const& originalMatrix, storm::storage::BitVector const& subsystem, storm::storage::BitVector const& subsystemActions) { + return originalMatrix.getSubmatrix(false, subsystemActions, subsystem); + } + + + template + static std::vector transformActionValueVector(std::vector const& originalVector, storm::storage::BitVector const& subsystemActions) { + std::vector v; + v.reserve(subsystemActions.getNumberOfSetBits()); + for(auto action : subsystemActions){ + v.push_back(originalVector[action]); + } + return v; + } + + template + static std::vector transformStateValueVector(std::vector const& originalVector, storm::storage::BitVector const& subsystem) { + std::vector v; + v.reserve(subsystem.getNumberOfSetBits()); + for(auto state : subsystem){ + v.push_back(originalVector[state]); + } + return v; + } + + static storm::storage::BitVector transformStateBitVector(storm::storage::BitVector const& originalBitVector, storm::storage::BitVector const& subsystem) { + return originalBitVector % subsystem; + } + + + template + static typename std::enable_if< + std::is_same>::value || + std::is_same>::value || + std::is_same>::value, + MT>::type + createTransformedModel(MT const& originalModel, + storm::storage::BitVector const& subsystem, + storm::storage::SparseMatrix& matrix, + storm::models::sparse::StateLabeling& stateLabeling, + std::unordered_map& rewardModels, + boost::optional>& choiceLabeling ) { + return MT(std::move(matrix), std::move(stateLabeling), std::move(rewardModels), std::move(choiceLabeling)); + } + + template + static typename std::enable_if< + std::is_same>::value, + MT>::type + createTransformedModel(MT const& originalModel, + storm::storage::BitVector const& subsystem, + storm::storage::SparseMatrix& matrix, + storm::models::sparse::StateLabeling& stateLabeling, + std::unordered_map& rewardModels, + boost::optional>& choiceLabeling ) { + storm::storage::BitVector markovianStates = transformStateBitVector(originalModel.getMarkovianStates(), subsystem); + std::vector exitRates = transformStateValueVector(originalModel.getExitRates(), subsystem); + return MT(std::move(matrix), std::move(stateLabeling), std::move(markovianStates), std::move(exitRates), true, std::move(rewardModels), std::move(choiceLabeling)); + } + + + }; + } +} +#endif // STORM_TRANSFORMER_SUBSYSTEMBUILDER_H diff --git a/src/utility/Stopwatch.h b/src/utility/Stopwatch.h new file mode 100644 index 000000000..0ee408808 --- /dev/null +++ b/src/utility/Stopwatch.h @@ -0,0 +1,72 @@ +#ifndef STORM_UTILITY_STOPWATCH_H_ +#define STORM_UTILITY_STOPWATCH_H_ + +#include +#include + +#include "src/utility/macros.h" + +namespace storm { + namespace utility { + // A class that provides convenience operations to display run times. + class Stopwatch { + public: + Stopwatch(double initialValueInSeconds = 0.0) : accumulatedSeconds(initialValueInSeconds), paused(false), startOfCurrentMeasurement(std::chrono::high_resolution_clock::now()) { + // Intentionally left empty + } + + ~Stopwatch() = default; + + double getAccumulatedSeconds() const { + if(paused) { + return accumulatedSeconds; + } else { + return accumulatedSeconds + std::chrono::duration(std::chrono::high_resolution_clock::now() - startOfCurrentMeasurement).count(); + } + } + + void addToAccumulatedSeconds(double value) { + accumulatedSeconds += value; + } + + void pause() { + if(paused) { + STORM_LOG_WARN("Tried to pause a stopwatch that was already paused."); + } else { + accumulatedSeconds = getAccumulatedSeconds(); + paused = true; + } + } + + void unpause() { + if(paused) { + startOfCurrentMeasurement = std::chrono::high_resolution_clock::now(); + paused = false; + } else { + STORM_LOG_WARN("Tried to unpause a stopwatch that was not paused."); + } + } + + // Note: Does NOT unpause if stopwatch is currently paused. + void reset() { + accumulatedSeconds = 0.0; + startOfCurrentMeasurement = std::chrono::high_resolution_clock::now(); + } + + friend std::ostream& operator<<(std::ostream& out, Stopwatch const& sw) { + out << (round(sw.getAccumulatedSeconds()*1000)/1000); + return out; + } + + + private: + double accumulatedSeconds; + bool paused; + std::chrono::high_resolution_clock::time_point startOfCurrentMeasurement; + + + }; + } +} + +#endif /* STORM_UTILITY_STOPWATCH_H_ */ diff --git a/src/utility/constants.cpp b/src/utility/constants.cpp index 99a4d527b..7a0ad7fe5 100644 --- a/src/utility/constants.cpp +++ b/src/utility/constants.cpp @@ -174,7 +174,22 @@ namespace storm { double convertNumber(double const& number){ return number; } - + + template<> + uint_fast64_t convertNumber(double const& number){ + return std::llround(number); + } + + template<> + double convertNumber(uint_fast64_t const& number){ + return number; + } + + template + ValueType sqrt(ValueType const& number) { + return std::sqrt(number); + } + template ValueType abs(ValueType const& number) { return std::fabs(number); @@ -294,14 +309,19 @@ namespace storm { return carl::toInt(number); } + template<> + RationalNumber convertNumber(RationalNumber const& number){ + return number; + } + template<> RationalNumber convertNumber(double const& number){ return carl::rationalize(number); } - + template<> - RationalNumber convertNumber(RationalNumber const& number){ - return number; + RationalNumber convertNumber(uint_fast64_t const& number){ + return RationalNumber(number); } template<> @@ -324,6 +344,11 @@ namespace storm { return carl::toInt(func.nominatorAsNumber()); } + template<> + RationalNumber sqrt(RationalNumber const& number) { + return carl::sqrt(number); + } + template<> RationalNumber abs(storm::RationalNumber const& number) { return carl::abs(number); @@ -372,6 +397,7 @@ namespace storm { template storm::storage::MatrixEntry& simplify(storm::storage::MatrixEntry& matrixEntry); template storm::storage::MatrixEntry&& simplify(storm::storage::MatrixEntry&& matrixEntry); + template double sqrt(double const& number); template double abs(double const& number); template bool isInteger(double const& number); @@ -473,6 +499,8 @@ namespace storm { template storm::RationalNumber convertNumber(storm::RationalNumber const& number); RationalNumber convertNumber(std::string const& number); + template storm::RationalNumber sqrt(storm::RationalNumber const& number); + template storm::RationalNumber abs(storm::RationalNumber const& number); template storm::RationalNumber pow(storm::RationalNumber const& value, uint_fast64_t exponent); diff --git a/src/utility/constants.h b/src/utility/constants.h index 75cac92e2..e66d0e0ad 100644 --- a/src/utility/constants.h +++ b/src/utility/constants.h @@ -74,6 +74,9 @@ namespace storm { template TargetType convertNumber(SourceType const& number); + template + ValueType sqrt(ValueType const& number); + template ValueType abs(ValueType const& number); diff --git a/src/utility/export.h b/src/utility/export.h index 8ae336c32..bc8e7c156 100644 --- a/src/utility/export.h +++ b/src/utility/export.h @@ -9,43 +9,74 @@ #define STORM_UTILITY_EXPORT_H_ #include +#include -#include "src/storage/parameters.h" -#include "src/settings/modules/ParametricSettings.h" -#include "src/modelchecker/reachability/CollectConstraints.h" +#include "src/utility/macros.h" +#include "src/exceptions/FileIoException.h" -namespace storm { -namespace utility { - -template -void exportParametricMcResult(const ValueType& mcresult, storm::modelchecker::reachability::CollectConstraints const& constraintCollector) { - std::string path = storm::settings::getModule().exportResultPath(); - std::ofstream filestream; - filestream.open(path); - // todo add checks. - filestream << "!Parameters: "; - std::set vars = mcresult.gatherVariables(); - std::copy(vars.begin(), vars.end(), std::ostream_iterator(filestream, ", ")); - filestream << std::endl; - filestream << "!Result: " << mcresult << std::endl; - filestream << "!Well-formed Constraints: " << std::endl; - std::copy(constraintCollector.wellformedConstraints().begin(), constraintCollector.wellformedConstraints().end(), std::ostream_iterator>(filestream, "\n")); - filestream << "!Graph-preserving Constraints: " << std::endl; - std::copy(constraintCollector.graphPreservingConstraints().begin(), constraintCollector.graphPreservingConstraints().end(), std::ostream_iterator>(filestream, "\n")); - filestream.close(); -} +//#include "src/storage/parameters.h" +//#include "src/settings/modules/ParametricSettings.h" +//#include "src/modelchecker/reachability/CollectConstraints.h" -void exportStringStreamToFile(std::string const& str, std::string filepath) { - // todo add checks. - std::ofstream filestream; - filestream.open(filepath); - filestream << str; - filestream.close(); -} +namespace storm { + namespace utility { -} + /* TODO Fix me + template + void exportParametricMcResult(const ValueType& mcresult, storm::modelchecker::reachability::CollectConstraints const& constraintCollector) { + std::string path = storm::settings::getModule().exportResultPath(); + std::ofstream filestream; + filestream.open(path); + // todo add checks. + filestream << "!Parameters: "; + std::set vars = mcresult.gatherVariables(); + std::copy(vars.begin(), vars.end(), std::ostream_iterator(filestream, ", ")); + filestream << std::endl; + filestream << "!Result: " << mcresult << std::endl; + filestream << "!Well-formed Constraints: " << std::endl; + std::copy(constraintCollector.wellformedConstraints().begin(), constraintCollector.wellformedConstraints().end(), std::ostream_iterator>(filestream, "\n")); + filestream << "!Graph-preserving Constraints: " << std::endl; + std::copy(constraintCollector.graphPreservingConstraints().begin(), constraintCollector.graphPreservingConstraints().end(), std::ostream_iterator>(filestream, "\n")); + filestream.close(); + } + */ + + inline void exportStringToFile(std::string const& str, std::string filepath) { + std::ofstream filestream; + filestream.open(filepath); + STORM_LOG_THROW(filestream.is_open(), storm::exceptions::FileIoException , "Could not open file " << filepath << "."); + filestream << str; + } + + template + inline void exportDataToCSVFile(std::string filepath, std::vector> const& data, boost::optional> const& columnHeaders) { + std::ofstream filestream; + filestream.open(filepath); + STORM_LOG_THROW(filestream.is_open(), storm::exceptions::FileIoException , "Could not open file " << filepath << "."); + + if(columnHeaders) { + for(auto columnIt = columnHeaders->begin(); columnIt != columnHeaders->end(); ++columnIt) { + if(columnIt != columnHeaders->begin()) { + filestream << ","; + } + filestream << *columnIt; + } + filestream << std::endl; + } + + for (auto const& row : data) { + for(auto columnIt = row.begin(); columnIt != row.end(); ++columnIt) { + if(columnIt != row.begin()) { + filestream << ","; + } + filestream << *columnIt; + } + filestream << std::endl; + } + } + } } -#endif \ No newline at end of file +#endif diff --git a/src/utility/policyguessing.cpp b/src/utility/policyguessing.cpp index 072662c33..15335b36a 100644 --- a/src/utility/policyguessing.cpp +++ b/src/utility/policyguessing.cpp @@ -171,14 +171,16 @@ namespace storm { storm::utility::vector::selectVectorValues(subB, probGreater0States, b); std::unique_ptr> linEqSysSolver(static_cast*>(storm::solver::GmmxxLinearEquationSolverFactory().create(eqSysA).release())); - auto& eqSettings = linEqSysSolver->getSettings(); + auto eqSettings = linEqSysSolver->getSettings(); eqSettings.setRelativeTerminationCriterion(relative); eqSettings.setMaximalNumberOfIterations(500); + linEqSysSolver->setSettings(eqSettings); std::size_t iterations = 0; std::vector copyX(subX.size()); ValueType precisionChangeFactor = storm::utility::one(); do { eqSettings.setPrecision(eqSettings.getPrecision() * precisionChangeFactor); + linEqSysSolver->setSettings(eqSettings); if(!linEqSysSolver->solveEquations(subX, subB)){ // break; //Solver did not converge.. so we have to go on with the current solution. } diff --git a/src/utility/storm.h b/src/utility/storm.h index 3ce01c028..ebef81ba0 100644 --- a/src/utility/storm.h +++ b/src/utility/storm.h @@ -343,6 +343,22 @@ namespace storm { return result; } + template + std::unique_ptr verifySparseMarkovAutomaton(std::shared_ptr> ma, storm::modelchecker::CheckTask const& task) { + std::unique_ptr result; + // Close the MA, if it is not already closed. + if (!ma->isClosed()) { + ma->close(); + } + storm::modelchecker::SparseMarkovAutomatonCslModelChecker> modelchecker(*ma); + if (modelchecker.canHandle(task)) { + result = modelchecker.check(task); + } else { + STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "The property " << task.getFormula() << " is not supported."); + } + return result; + } + template std::unique_ptr verifySparseModel(std::shared_ptr> model, std::shared_ptr const& formula, bool onlyInitialStatesRelevant = false) { storm::modelchecker::CheckTask task(*formula, onlyInitialStatesRelevant); @@ -355,18 +371,11 @@ namespace storm { } else if (model->getType() == storm::models::ModelType::Ctmc) { result = verifySparseCtmc(model->template as>(), task); } else if (model->getType() == storm::models::ModelType::MarkovAutomaton) { - std::shared_ptr> ma = model->template as>(); - // Close the MA, if it is not already closed. - if (!ma->isClosed()) { - ma->close(); - } - storm::modelchecker::SparseMarkovAutomatonCslModelChecker> modelchecker(*ma); - result = modelchecker.check(task); + result = verifySparseMarkovAutomaton(model->template as>(), task); } else { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "The model type " << model->getType() << " is not supported."); } return result; - } #ifdef STORM_HAVE_CARL diff --git a/src/utility/vector.h b/src/utility/vector.h index c7759cfa3..d3ec3e545 100644 --- a/src/utility/vector.h +++ b/src/utility/vector.h @@ -6,12 +6,13 @@ #include "tbb/tbb.h" #endif -#include "constants.h" #include #include #include +#include #include "src/storage/BitVector.h" +#include "src/utility/constants.h" #include "src/utility/macros.h" #include "src/solver/OptimizationDirection.h" @@ -83,7 +84,7 @@ namespace storm { } /*! - * Constructs a vector [min, min+1, ...., max] + * Constructs a vector [min, min+1, ...., max-1] */ inline std::vector buildVectorForRange(uint_fast64_t min, uint_fast64_t max) { STORM_LOG_ASSERT(min < max, "Invalid range."); @@ -93,9 +94,19 @@ namespace storm { iota_n(std::back_inserter(v), diff, min); return v; } - - - + + /*! + * Returns a list of indices such that the first index refers to the highest entry of the given vector, + * the second index refers to the entry with the second highest value, ... + * Example: v={3,8,4,5} yields res={1,3,2,0} + */ + template + std::vector getSortedIndices(std::vector const& v){ + std::vector res = buildVectorForRange(0, v.size()); + std::sort(res.begin(), res.end(), [&v](uint_fast64_t index1, uint_fast64_t index2) { return v[index1] > v[index2];}); + return res; + } + /*! * Selects the elements from a vector at the specified positions and writes them consecutively into another vector. * @param vector The vector into which the selected elements are to be written. @@ -109,7 +120,7 @@ namespace storm { vector[oldPosition++] = values[position]; } } - + /*! * Selects groups of elements from a vector at the specified positions and writes them consecutively into another vector. * @@ -366,16 +377,40 @@ namespace storm { } /*! - * Subtracts the two given vectors and writes the result into the first operand. + * Multiplies each element of the given vector with the given factor and writes the result into the vector. * - * @param target The first summand and target vector. - * @param summand The second summand. + * @param target The operand and target vector. + * @param factor The scaling factor */ template void scaleVectorInPlace(std::vector& target, ValueType2 const& factor) { applyPointwise(target, target, [&] (ValueType1 const& argument) -> ValueType1 { return argument * factor; }); } + /*! + * Computes x:= x + a*y, i.e., adds each element of the first vector and (the corresponding element of the second vector times the given factor) and writes the result into the first vector. + * + * @param firstOperand The first operand. + * @param secondOperand The second operand + * @param factor The factor for the elements of the second operand + */ + template + void addScaledVector(std::vector& firstOperand, std::vector const& secondOperand, InValueType3 const& factor) { + applyPointwise(firstOperand, secondOperand, firstOperand, [&] (InValueType1 const& val1, InValueType2 const& val2) -> InValueType1 { return val1 + (factor * val2); }); + } + + /*! + * Computes the dot product (aka scalar product) and returns the result + * + * @param firstOperand The first operand. + * @param secondOperand The second operand + * @return firstOperand*secondOperand + */ + template + T dotProduct(std::vector const& firstOperand, std::vector const& secondOperand) { + return std::inner_product(firstOperand.begin(), firstOperand.end(), secondOperand.begin(), storm::utility::zero()); + } + /*! * Retrieves a bit vector containing all the indices for which the value at this position makes the given * function evaluate to true. @@ -410,6 +445,17 @@ namespace storm { return filter(values, fnc); } + /*! + * Retrieves a bit vector containing all the indices for which the value at this position is equal to zero + * + * @param values The vector of values. + * @return The resulting bit vector. + */ + template + storm::storage::BitVector filterZero(std::vector const& values) { + return filter(values, storm::utility::isZero); + } + /** * Sum the entries from values that are set to one in the filter vector. * @param values @@ -711,17 +757,30 @@ namespace storm { return subVector; } + /*! + * Converts the given vector to the given ValueType + * Assumes that both, TargetType and SourceType are numeric + */ + template + std::vector convertNumericVector(std::vector const& oldVector) { + std::vector resultVector; + resultVector.reserve(oldVector.size()); + for(auto const& oldValue : oldVector){ + resultVector.push_back(storm::utility::convertNumber(oldValue)); + } + return resultVector; + } + /*! * Converts the given vector to the given ValueType */ template - std::vector toValueType(std::vector const& oldVector) { + std::vector toValueType(std::vector const& oldVector) { std::vector resultVector; - resultVector.resize(oldVector.size()); - for (size_t i = 0, size = oldVector.size(); i < size; ++i) { - resultVector.at(i) = static_cast(oldVector.at(i)); - } - + resultVector.reserve(oldVector.size()); + for(auto const& oldValue : oldVector){ + resultVector.push_back(static_cast(oldValue)); + } return resultVector; } @@ -736,6 +795,21 @@ namespace storm { return result; } + template + bool hasNegativeEntry(std::vector const& v){ + return std::any_of(v.begin(), v.end(), [](T value){return value < storm::utility::zero();}); + } + + template + bool hasPositiveEntry(std::vector const& v){ + return std::any_of(v.begin(), v.end(), [](T value){return value > storm::utility::zero();}); + } + + template + bool hasNonZeroEntry(std::vector const& v){ + return std::any_of(v.begin(), v.end(), [](T value){return !storm::utility::isZero(value);}); + } + /*! * Output vector as string. * diff --git a/storm-config.h.in b/storm-config.h.in index 18d8ee86e..c5edd1f4a 100644 --- a/storm-config.h.in +++ b/storm-config.h.in @@ -51,6 +51,9 @@ // Whether smtrat is available and to be used. #cmakedefine STORM_HAVE_SMTRAT +// Whether HyPro is available and to be used. +#cmakedefine STORM_HAVE_HYPRO + #cmakedefine STORM_LOGGING_FRAMEWORK #cmakedefine STORM_LOG_DISABLE_DEBUG diff --git a/test/functional/logic/FragmentCheckerTest.cpp b/test/functional/logic/FragmentCheckerTest.cpp index 3ae4d52e3..334c23f96 100644 --- a/test/functional/logic/FragmentCheckerTest.cpp +++ b/test/functional/logic/FragmentCheckerTest.cpp @@ -136,3 +136,51 @@ TEST(FragmentCheckerTest, Csrl) { ASSERT_NO_THROW(formula = formulaParser.parseSingleFormulaFromString("P=? [F[0,1] \"label\"]")); EXPECT_TRUE(checker.conformsToSpecification(*formula, csrl)); } + +TEST(FragmentCheckerTest, MultiObjective) { + storm::logic::FragmentChecker checker; + storm::logic::FragmentSpecification multiobjective = storm::logic::multiObjective(); + + storm::parser::FormulaParser formulaParser; + std::shared_ptr formula; + + ASSERT_NO_THROW(formula = formulaParser.parseSingleFormulaFromString("\"label\"")); + EXPECT_TRUE(checker.conformsToSpecification(*formula, multiobjective)); + + ASSERT_NO_THROW(formula = formulaParser.parseSingleFormulaFromString("P=? [F \"label\"]")); + EXPECT_TRUE(checker.conformsToSpecification(*formula, multiobjective)); + + ASSERT_NO_THROW(formula = formulaParser.parseSingleFormulaFromString("P=? [F P=? [F \"label\"]]")); + EXPECT_TRUE(checker.conformsToSpecification(*formula, multiobjective)); + + ASSERT_NO_THROW(formula = formulaParser.parseSingleFormulaFromString("R=? [F \"label\"]")); + EXPECT_TRUE(checker.conformsToSpecification(*formula, multiobjective)); + + ASSERT_NO_THROW(formula = formulaParser.parseSingleFormulaFromString("R=? [C]")); + EXPECT_TRUE(checker.conformsToSpecification(*formula, multiobjective)); + + ASSERT_NO_THROW(formula = formulaParser.parseSingleFormulaFromString("P=? [F[0.5,1] \"label\"]")); + EXPECT_FALSE(checker.conformsToSpecification(*formula, multiobjective)); + + ASSERT_NO_THROW(formula = formulaParser.parseSingleFormulaFromString("multi(R<0.3 [ C ], P<0.6 [F \"label\"] & \"label\" & R<=4[F \"label\"])")); + EXPECT_FALSE(checker.conformsToSpecification(*formula, multiobjective)); + + ASSERT_NO_THROW(formula = formulaParser.parseSingleFormulaFromString("multi(R<0.3 [ C ], P<0.6 [F \"label\"], \"label\", R<=4[F \"label\"])")); + EXPECT_FALSE(checker.conformsToSpecification(*formula, multiobjective)); + + ASSERT_NO_THROW(formula = formulaParser.parseSingleFormulaFromString("multi(R<0.3 [ C ], P<0.6 [F \"label\"], R<=4[F \"label\"])")); + EXPECT_TRUE(checker.conformsToSpecification(*formula, multiobjective)); + + ASSERT_NO_THROW(formula = formulaParser.parseSingleFormulaFromString("multi(R<0.3 [ C<=3 ], P<0.6 [F \"label\"], R<=4[F \"label\"])")); + EXPECT_FALSE(checker.conformsToSpecification(*formula, multiobjective)); + + ASSERT_NO_THROW(formula = formulaParser.parseSingleFormulaFromString("multi(R<0.3 [ C ], multi(P<0.6 [F \"label\"], R<=4[F \"label\"]))")); + EXPECT_FALSE(checker.conformsToSpecification(*formula, multiobjective)); + + ASSERT_NO_THROW(formula = formulaParser.parseSingleFormulaFromString("multi(R<0.3 [ C ], P<0.6 [F \"label\" & \"otherlabel\"], P<=4[\"label\" U<=42 \"otherlabel\"])")); + EXPECT_TRUE(checker.conformsToSpecification(*formula, multiobjective)); + + ASSERT_NO_THROW(formula = formulaParser.parseSingleFormulaFromString("multi(P=? [F P<0.5 [F \"label\"]], R<0.3 [ C ] )")); + EXPECT_FALSE(checker.conformsToSpecification(*formula, multiobjective)); + +} diff --git a/test/functional/modelchecker/SparseMaPcaaModelCheckerTest.cpp b/test/functional/modelchecker/SparseMaPcaaModelCheckerTest.cpp new file mode 100644 index 000000000..08cb27424 --- /dev/null +++ b/test/functional/modelchecker/SparseMaPcaaModelCheckerTest.cpp @@ -0,0 +1,82 @@ +#include "gtest/gtest.h" +#include "storm-config.h" + +#ifdef STORM_HAVE_HYPRO + +#include "src/modelchecker/multiobjective/pcaa.h" +#include "src/modelchecker/results/ExplicitQuantitativeCheckResult.h" +#include "src/modelchecker/results/ExplicitQualitativeCheckResult.h" +#include "src/modelchecker/results/ParetoCurveCheckResult.h" +#include "src/models/sparse/MarkovAutomaton.h" +#include "src/storage/geometry/Polytope.h" +#include "src/storage/geometry/Hyperrectangle.h" +#include "src/settings/modules/GeneralSettings.h" +#include "src/settings/SettingsManager.h" +#include "src/utility/storm.h" + + +/* Rationals for MAs not supported at this point +TEST(SparseMaPcaaModelCheckerTest, serverRationalNumbers) { + + std::string programFile = STORM_CPP_BASE_PATH "/examples/multiobjective/ma/server/server.ma"; + std::string formulasAsString = "multi(Tmax=? [ F \"error\" ], Pmax=? [ F \"processB\" ]) "; // pareto + // formulasAsString += "; \n multi(..)"; + + // programm, model, formula + storm::prism::Program program = storm::parseProgram(programFile); + program.checkValidity(); + std::vector> formulas = storm::parseFormulasForProgram(formulasAsString, program); + storm::generator::NextStateGeneratorOptions options(formulas); + std::shared_ptr> ma = storm::builder::ExplicitModelBuilder(program, options).build()->as>(); + + storm::modelchecker::SparseMarkovAutomatonCslModelChecker> checker(*ma); + + std::unique_ptr result = checker.check(storm::modelchecker::CheckTask(*formulas[0], true)); + ASSERT_TRUE(result->isParetoCurveCheckResult()); + + storm::RationalNumber p1 = storm::utility::convertNumber(11.0); p1 /= storm::utility::convertNumber(6.0); + storm::RationalNumber p2 = storm::utility::convertNumber(1.0); p2 /= storm::utility::convertNumber(2.0); + std::vector p = {p1, p2}; + storm::RationalNumber q1 = storm::utility::convertNumber(29.0); q1 /= storm::utility::convertNumber(18.0); + storm::RationalNumber q2 = storm::utility::convertNumber(2.0); q2 /= storm::utility::convertNumber(3.0); + std::vector q = {q1, q2}; + auto expectedAchievableValues = storm::storage::geometry::Polytope::createDownwardClosure(std::vector>({p,q})); + EXPECT_TRUE(expectedAchievableValues->contains(result->asParetoCurveCheckResult().getUnderApproximation())); + EXPECT_TRUE(result->asParetoCurveCheckResult().getUnderApproximation()->contains(expectedAchievableValues)); + +}*/ + + +TEST(SparseMaPcaaModelCheckerTest, server) { + + std::string programFile = STORM_CPP_BASE_PATH "/examples/multiobjective/ma/server/server.ma"; + std::string formulasAsString = "multi(Tmax=? [ F \"error\" ], Pmax=? [ F \"processB\" ]) "; // pareto + // formulasAsString += "; \n multi(..)"; + + // programm, model, formula + + storm::prism::Program program = storm::parseProgram(programFile); + program = storm::utility::prism::preprocess(program, ""); + std::vector> formulas = storm::parseFormulasForPrismProgram(formulasAsString, program); + std::shared_ptr> ma = storm::buildSparseModel(program, formulas, true)->as>(); + storm::modelchecker::SparseMarkovAutomatonCslModelChecker> checker(*ma); + + std::unique_ptr result = storm::modelchecker::multiobjective::performPcaa(*ma, formulas[0]->asMultiObjectiveFormula()); + ASSERT_TRUE(result->isParetoCurveCheckResult()); + + std::vector p = {11.0/6.0, 1.0/2.0}; + std::vector q = {29.0/18.0, 2.0/3.0}; + auto expectedAchievableValues = storm::storage::geometry::Polytope::createDownwardClosure(std::vector>({p,q})); + // due to precision issues, we enlarge one of the polytopes before checking containement + std::vector lb = {-storm::settings::getModule().getPrecision(), -storm::settings::getModule().getPrecision()}; + std::vector ub = {storm::settings::getModule().getPrecision(), storm::settings::getModule().getPrecision()}; + auto bloatingBox = storm::storage::geometry::Hyperrectangle(lb,ub).asPolytope(); + + EXPECT_TRUE(expectedAchievableValues->minkowskiSum(bloatingBox)->contains(result->asParetoCurveCheckResult().getUnderApproximation())); + EXPECT_TRUE(result->asParetoCurveCheckResult().getUnderApproximation()->minkowskiSum(bloatingBox)->contains(expectedAchievableValues)); + +} + + + +#endif /* STORM_HAVE_HYPRO */ diff --git a/test/functional/modelchecker/SparseMdpPcaaModelCheckerTest.cpp b/test/functional/modelchecker/SparseMdpPcaaModelCheckerTest.cpp new file mode 100644 index 000000000..d6d59dd47 --- /dev/null +++ b/test/functional/modelchecker/SparseMdpPcaaModelCheckerTest.cpp @@ -0,0 +1,228 @@ +#include "gtest/gtest.h" +#include "storm-config.h" + +#ifdef STORM_HAVE_HYPRO + +#include "src/modelchecker/multiobjective/pcaa.h" +#include "src/modelchecker/results/ExplicitQuantitativeCheckResult.h" +#include "src/modelchecker/results/ExplicitQualitativeCheckResult.h" +#include "src/models/sparse/Mdp.h" +#include "src/settings/modules/GeneralSettings.h" +#include "src/settings/SettingsManager.h" +#include "src/utility/storm.h" + + +TEST(SparseMdpPcaaModelCheckerTest, consensus) { + + std::string programFile = STORM_CPP_BASE_PATH "/examples/multiobjective/mdp/consensus/consensus2_3_2.nm"; + std::string formulasAsString = "multi(Pmax=? [ F \"one_proc_err\" ], P>=0.8916673903 [ G \"one_coin_ok\" ]) "; // numerical + formulasAsString += "; \n multi(P>=0.1 [ F \"one_proc_err\" ], P>=0.8916673903 [ G \"one_coin_ok\" ])"; // achievability (true) + formulasAsString += "; \n multi(P>=0.11 [ F \"one_proc_err\" ], P>=0.8916673903 [ G \"one_coin_ok\" ])"; // achievability (false) + + // programm, model, formula + storm::prism::Program program = storm::parseProgram(programFile); + program = storm::utility::prism::preprocess(program, ""); + std::vector> formulas = storm::parseFormulasForPrismProgram(formulasAsString, program); + std::shared_ptr> mdp = storm::buildSparseModel(program, formulas, true)->as>(); + uint_fast64_t const initState = *mdp->getInitialStates().begin();; + + storm::modelchecker::SparseMdpPrctlModelChecker> checker(*mdp); + + std::unique_ptr result = storm::modelchecker::multiobjective::performPcaa(*mdp, formulas[0]->asMultiObjectiveFormula()); + ASSERT_TRUE(result->isExplicitQuantitativeCheckResult()); + EXPECT_NEAR(0.10833260970000025, result->asExplicitQuantitativeCheckResult()[initState], storm::settings::getModule().getPrecision()); + + result = storm::modelchecker::multiobjective::performPcaa(*mdp, formulas[1]->asMultiObjectiveFormula()); + ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); + EXPECT_TRUE(result->asExplicitQualitativeCheckResult()[initState]); + + result = storm::modelchecker::multiobjective::performPcaa(*mdp, formulas[2]->asMultiObjectiveFormula()); + ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); + EXPECT_FALSE(result->asExplicitQualitativeCheckResult()[initState]); + +} + +TEST(SparseMdpPcaaModelCheckerTest, zeroconf) { + + std::string programFile = STORM_CPP_BASE_PATH "/examples/multiobjective/mdp/zeroconf/zeroconf4.nm"; + std::string formulasAsString = "multi(Pmax=? [ F l=4 & ip=1 ] , P>=0.993141[ G (error=0) ]) "; // numerical + formulasAsString += "; \n multi(P>=0.0003 [ F l=4 & ip=1 ] , P>=0.993141[ G (error=0) ])"; // achievability (true) + formulasAsString += "; \n multi(P>=0.00031 [ F l=4 & ip=1 ] , P>=0.993141[ G (error=0) ])"; // achievability (false) + + // programm, model, formula + storm::prism::Program program = storm::parseProgram(programFile); + program = storm::utility::prism::preprocess(program, ""); + std::vector> formulas = storm::parseFormulasForPrismProgram(formulasAsString, program); + std::shared_ptr> mdp = storm::buildSparseModel(program, formulas, true)->as>(); + uint_fast64_t const initState = *mdp->getInitialStates().begin(); + + storm::modelchecker::SparseMdpPrctlModelChecker> checker(*mdp); + + std::unique_ptr result = storm::modelchecker::multiobjective::performPcaa(*mdp, formulas[0]->asMultiObjectiveFormula()); + ASSERT_TRUE(result->isExplicitQuantitativeCheckResult()); + EXPECT_NEAR(0.0003075787401574803, result->asExplicitQuantitativeCheckResult()[initState], storm::settings::getModule().getPrecision()); + + result = storm::modelchecker::multiobjective::performPcaa(*mdp, formulas[1]->asMultiObjectiveFormula()); + ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); + EXPECT_TRUE(result->asExplicitQualitativeCheckResult()[initState]); + + result = storm::modelchecker::multiobjective::performPcaa(*mdp, formulas[2]->asMultiObjectiveFormula()); + ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); + EXPECT_FALSE(result->asExplicitQualitativeCheckResult()[initState]); + +} + +TEST(SparseMdpPcaaModelCheckerTest, zeroconfTb) { + + std::string programFile = STORM_CPP_BASE_PATH "/examples/multiobjective/mdp/zeroconf-tb/zeroconf-tb2_14.nm"; + std::string formulasAsString = " multi(Pmax=? [ F time_error=1 ] , P>=0.81[ G (error=0) ])"; // numerical + formulasAsString += "; \n multi(P>=0.00000008 [ F time_error=1 ] , P>=0.81[ G (error=0) ])"; // achievability (true) + formulasAsString += "; \n multi(P>=0.000000081 [ F time_error=1 ] , P>=0.81[ G (error=0) ])"; // achievability (false) + + // programm, model, formula + storm::prism::Program program = storm::parseProgram(programFile); + program = storm::utility::prism::preprocess(program, ""); + std::vector> formulas = storm::parseFormulasForPrismProgram(formulasAsString, program); + std::shared_ptr> mdp = storm::buildSparseModel(program, formulas, true)->as>(); + uint_fast64_t const initState = *mdp->getInitialStates().begin(); + + storm::modelchecker::SparseMdpPrctlModelChecker> checker(*mdp); + + std::unique_ptr result = storm::modelchecker::multiobjective::performPcaa(*mdp, formulas[0]->asMultiObjectiveFormula()); + ASSERT_TRUE(result->isExplicitQuantitativeCheckResult()); + EXPECT_NEAR(8.059348391417451e-8, result->asExplicitQuantitativeCheckResult()[initState], storm::settings::getModule().getPrecision()); + + result = storm::modelchecker::multiobjective::performPcaa(*mdp, formulas[1]->asMultiObjectiveFormula()); + ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); + EXPECT_TRUE(result->asExplicitQualitativeCheckResult()[initState]); + + result = storm::modelchecker::multiobjective::performPcaa(*mdp, formulas[2]->asMultiObjectiveFormula()); + ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); + EXPECT_FALSE(result->asExplicitQualitativeCheckResult()[initState]); + +} + +TEST(SparseMdpPcaaModelCheckerTest, team3with2objectives) { + + std::string programFile = STORM_CPP_BASE_PATH "/examples/multiobjective/mdp/team/team2obj_3.nm"; + std::string formulasAsString = " multi(Pmax=? [ F task1_completed ], R{\"w_1_total\"}>=2.210204082 [ C ])"; // numerical + formulasAsString += "; \n multi(P>=0.871 [ F task1_completed ], R{\"w_1_total\"}>=2.210204082 [ C ])"; // achievability (true) + formulasAsString += "; \n multi(P>=0.872 [ F task1_completed ], R{\"w_1_total\"}>=2.210204082 [ C ])"; // achievability (false) + + // programm, model, formula + storm::prism::Program program = storm::parseProgram(programFile); + program = storm::utility::prism::preprocess(program, ""); + std::vector> formulas = storm::parseFormulasForPrismProgram(formulasAsString, program); + std::shared_ptr> mdp = storm::buildSparseModel(program, formulas, true)->as>(); + uint_fast64_t const initState = *mdp->getInitialStates().begin(); + + storm::modelchecker::SparseMdpPrctlModelChecker> checker(*mdp); + + std::unique_ptr result = storm::modelchecker::multiobjective::performPcaa(*mdp, formulas[0]->asMultiObjectiveFormula()); + ASSERT_TRUE(result->isExplicitQuantitativeCheckResult()); + EXPECT_NEAR(0.8714285710612256, result->asExplicitQuantitativeCheckResult()[initState], storm::settings::getModule().getPrecision()); + + result = storm::modelchecker::multiobjective::performPcaa(*mdp, formulas[1]->asMultiObjectiveFormula()); + ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); + EXPECT_TRUE(result->asExplicitQualitativeCheckResult()[initState]); + + result = storm::modelchecker::multiobjective::performPcaa(*mdp, formulas[2]->asMultiObjectiveFormula()); + ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); + EXPECT_FALSE(result->asExplicitQualitativeCheckResult()[initState]); + +} + +TEST(SparseMdpPcaaModelCheckerTest, team3with3objectives) { + + std::string programFile = STORM_CPP_BASE_PATH "/examples/multiobjective/mdp/team/team3obj_3.nm"; + std::string formulasAsString = "multi(Pmax=? [ F task1_completed ], R{\"w_1_total\"}>=2.210204082 [ C ], P>=0.5 [ F task2_completed ])"; // numerical + formulasAsString += "; \n multi(P>=0.744 [ F task1_completed ], R{\"w_1_total\"}>=2.210204082 [ C ], P>=0.5 [ F task2_completed ])"; // achievability (true) + formulasAsString += "; \n multi(P>=0.745 [ F task1_completed ], R{\"w_1_total\"}>=2.210204082 [ C ], P>=0.5 [ F task2_completed ])"; // achievability (false) + + // programm, model, formula + storm::prism::Program program = storm::parseProgram(programFile); + program = storm::utility::prism::preprocess(program, ""); + std::vector> formulas = storm::parseFormulasForPrismProgram(formulasAsString, program); + std::shared_ptr> mdp = storm::buildSparseModel(program, formulas, true)->as>(); + uint_fast64_t const initState = *mdp->getInitialStates().begin(); + + storm::modelchecker::SparseMdpPrctlModelChecker> checker(*mdp); + + std::unique_ptr result = storm::modelchecker::multiobjective::performPcaa(*mdp, formulas[0]->asMultiObjectiveFormula()); + ASSERT_TRUE(result->isExplicitQuantitativeCheckResult()); + EXPECT_NEAR(0.7448979591841851, result->asExplicitQuantitativeCheckResult()[initState], storm::settings::getModule().getPrecision()); + + result = storm::modelchecker::multiobjective::performPcaa(*mdp, formulas[1]->asMultiObjectiveFormula()); + ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); + EXPECT_TRUE(result->asExplicitQualitativeCheckResult()[initState]); + + result = storm::modelchecker::multiobjective::performPcaa(*mdp, formulas[2]->asMultiObjectiveFormula()); + ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); + EXPECT_FALSE(result->asExplicitQualitativeCheckResult()[initState]); + +} + +TEST(SparseMdpPcaaModelCheckerTest, scheduler) { + + std::string programFile = STORM_CPP_BASE_PATH "/examples/multiobjective/mdp/scheduler/scheduler05.nm"; + std::string formulasAsString = "multi(R{\"time\"}min=?[ F \"tasks_complete\" ], R{\"energy\"}<=1.45 [ F \"tasks_complete\" ]) "; // numerical + formulasAsString += "; \n multi(R{\"time\"}<= 11.778[ F \"tasks_complete\" ], R{\"energy\"}<=1.45 [ F \"tasks_complete\" ]) "; // achievability (true) + formulasAsString += "; \n multi(R{\"time\"}<= 11.777 [ F \"tasks_complete\" ], R{\"energy\"}<=1.45 [ F \"tasks_complete\" ]) "; // achievability (false) + + // programm, model, formula + storm::prism::Program program = storm::parseProgram(programFile); + program = storm::utility::prism::preprocess(program, ""); + std::vector> formulas = storm::parseFormulasForPrismProgram(formulasAsString, program); + std::shared_ptr> mdp = storm::buildSparseModel(program, formulas, true)->as>(); + uint_fast64_t const initState = *mdp->getInitialStates().begin(); + + storm::modelchecker::SparseMdpPrctlModelChecker> checker(*mdp); + + std::unique_ptr result = storm::modelchecker::multiobjective::performPcaa(*mdp, formulas[0]->asMultiObjectiveFormula()); + ASSERT_TRUE(result->isExplicitQuantitativeCheckResult()); + EXPECT_NEAR(11.77777778, result->asExplicitQuantitativeCheckResult()[initState], storm::settings::getModule().getPrecision()); + + result = storm::modelchecker::multiobjective::performPcaa(*mdp, formulas[1]->asMultiObjectiveFormula()); + ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); + EXPECT_TRUE(result->asExplicitQualitativeCheckResult()[initState]); + + result = storm::modelchecker::multiobjective::performPcaa(*mdp, formulas[2]->asMultiObjectiveFormula()); + ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); + EXPECT_FALSE(result->asExplicitQualitativeCheckResult()[initState]); + +} + +TEST(SparseMdpPcaaModelCheckerTest, dpm) { + + std::string programFile = STORM_CPP_BASE_PATH "/examples/multiobjective/mdp/dpm/dpm100.nm"; + std::string formulasAsString = "multi(R{\"power\"}min=? [ C<=100 ], R{\"queue\"}<=70 [ C<=100 ])"; // numerical + formulasAsString += "; \n multi(R{\"power\"}<=121.613 [ C<=100 ], R{\"queue\"}<=70 [ C<=100 ])"; // achievability (true) + formulasAsString += "; \n multi(R{\"power\"}<=121.612 [ C<=100 ], R{\"queue\"}<=70 [ C<=100 ])"; // achievability (false) + + // programm, model, formula + storm::prism::Program program = storm::parseProgram(programFile); + program = storm::utility::prism::preprocess(program, ""); + std::vector> formulas = storm::parseFormulasForPrismProgram(formulasAsString, program); + std::shared_ptr> mdp = storm::buildSparseModel(program, formulas, true)->as>(); + uint_fast64_t const initState = *mdp->getInitialStates().begin(); + + storm::modelchecker::SparseMdpPrctlModelChecker> checker(*mdp); + + std::unique_ptr result = storm::modelchecker::multiobjective::performPcaa(*mdp, formulas[0]->asMultiObjectiveFormula()); + ASSERT_TRUE(result->isExplicitQuantitativeCheckResult()); + EXPECT_NEAR(121.61288420945114, result->asExplicitQuantitativeCheckResult()[initState], storm::settings::getModule().getPrecision()); + + result = storm::modelchecker::multiobjective::performPcaa(*mdp, formulas[1]->asMultiObjectiveFormula()); + ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); + EXPECT_TRUE(result->asExplicitQualitativeCheckResult()[initState]); + + result = storm::modelchecker::multiobjective::performPcaa(*mdp, formulas[2]->asMultiObjectiveFormula()); + ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); + EXPECT_FALSE(result->asExplicitQualitativeCheckResult()[initState]); + +} + + + + +#endif /* STORM_HAVE_HYPRO */ diff --git a/test/functional/modelchecker/multiobjective1.nm b/test/functional/modelchecker/multiobjective1.nm new file mode 100644 index 000000000..1db60ec50 --- /dev/null +++ b/test/functional/modelchecker/multiobjective1.nm @@ -0,0 +1,20 @@ + +mdp + +module module1 + + // local state + s : [0..2] init 0; + + [A] s=0 -> 0.6 : (s'=1) + 0.4 : (s'=2); + [B] s=0 -> 0.3 : (s'=0) + 0.7 : (s'=1); + [C] s=0 -> 0.2 : (s'=0) + 0.8 : (s'=2); + [D] s=1 -> 0.25 : (s'=0) + 0.75 : (s'=2); + [] s=2 -> 1 : (s'=s); +endmodule + +rewards "rew" + [A] true : 10; + [D] true : 4; +endrewards + diff --git a/test/functional/modelchecker/multiobjective2.nm b/test/functional/modelchecker/multiobjective2.nm new file mode 100644 index 000000000..3df5018f5 --- /dev/null +++ b/test/functional/modelchecker/multiobjective2.nm @@ -0,0 +1,20 @@ + +mdp + +module module1 + + s : [0..2] init 0; + + [A] s=0 -> 1 : (s'=1); + [B] s=0 -> 1 : (s'=2); + [C] s=1 -> 1 : true; + [D] s=1 -> 1 : (s'=2); + [E] s=2 -> 1 : true; +endmodule + +rewards "rew" + [A] true : 10; + [C] true : 3; + [E] true : 1; +endrewards + diff --git a/test/functional/parser/FormulaParserTest.cpp b/test/functional/parser/FormulaParserTest.cpp index 14077658e..0bd924bdb 100644 --- a/test/functional/parser/FormulaParserTest.cpp +++ b/test/functional/parser/FormulaParserTest.cpp @@ -149,3 +149,32 @@ TEST(FormulaParserTest, WrongFormatTest) { input = "P>0.5 [F y!=0)]"; EXPECT_THROW(formula = formulaParser.parseSingleFormulaFromString(input), storm::exceptions::WrongFormatException); } + +TEST(FormulaParserTest, MultiObjectiveFormulaTest) { + storm::parser::FormulaParser formulaParser; + + std::string input = "multi(P<0.9 [ F \"a\" ], R<42 [ F \"b\" ], Pmin=? [ F\"c\" ])"; + std::vector> formulas; + ASSERT_NO_THROW(formulas = formulaParser.parseFromString(input)); + ASSERT_EQ(1, formulas.size()); + ASSERT_TRUE(formulas[0]->isMultiObjectiveFormula()); + storm::logic::MultiObjectiveFormula mof = formulas[0]->asMultiObjectiveFormula(); + ASSERT_EQ(3, mof.getNumberOfSubformulas()); + + ASSERT_TRUE(mof.getSubformula(0).isProbabilityOperatorFormula()); + ASSERT_TRUE(mof.getSubformula(0).asProbabilityOperatorFormula().getSubformula().isEventuallyFormula()); + ASSERT_TRUE(mof.getSubformula(0).asProbabilityOperatorFormula().getSubformula().asEventuallyFormula().getSubformula().isAtomicLabelFormula()); + ASSERT_TRUE(mof.getSubformula(0).asProbabilityOperatorFormula().hasBound()); + + ASSERT_TRUE(mof.getSubformula(1).isRewardOperatorFormula()); + ASSERT_TRUE(mof.getSubformula(1).asRewardOperatorFormula().getSubformula().isEventuallyFormula()); + ASSERT_TRUE(mof.getSubformula(1).asRewardOperatorFormula().getSubformula().asEventuallyFormula().getSubformula().isAtomicLabelFormula()); + ASSERT_TRUE(mof.getSubformula(1).asRewardOperatorFormula().hasBound()); + + ASSERT_TRUE(mof.getSubformula(2).isProbabilityOperatorFormula()); + ASSERT_TRUE(mof.getSubformula(2).asProbabilityOperatorFormula().getSubformula().isEventuallyFormula()); + ASSERT_TRUE(mof.getSubformula(2).asProbabilityOperatorFormula().getSubformula().asEventuallyFormula().getSubformula().isAtomicLabelFormula()); + ASSERT_TRUE(mof.getSubformula(2).asProbabilityOperatorFormula().hasOptimalityType()); + ASSERT_TRUE(storm::solver::minimize(mof.getSubformula(2).asProbabilityOperatorFormula().getOptimalityType())); + +} diff --git a/test/functional/solver/GmmxxLinearEquationSolverTest.cpp b/test/functional/solver/GmmxxLinearEquationSolverTest.cpp index 4cf07d6ec..ccb9f8cbb 100644 --- a/test/functional/solver/GmmxxLinearEquationSolverTest.cpp +++ b/test/functional/solver/GmmxxLinearEquationSolverTest.cpp @@ -54,11 +54,13 @@ TEST(GmmxxLinearEquationSolver, gmres) { std::vector b = {16, -4, -7}; storm::solver::GmmxxLinearEquationSolver solver(A); - solver.getSettings().setSolutionMethod(storm::solver::GmmxxLinearEquationSolverSettings::SolutionMethod::Gmres); - solver.getSettings().setPrecision(1e-6); - solver.getSettings().setMaximalNumberOfIterations(10000); - solver.getSettings().setPreconditioner(storm::solver::GmmxxLinearEquationSolverSettings::Preconditioner::None); - solver.getSettings().setNumberOfIterationsUntilRestart(50); + auto settings = solver.getSettings(); + settings.setSolutionMethod(storm::solver::GmmxxLinearEquationSolverSettings::SolutionMethod::Gmres); + settings.setPrecision(1e-6); + settings.setMaximalNumberOfIterations(10000); + settings.setPreconditioner(storm::solver::GmmxxLinearEquationSolverSettings::Preconditioner::None); + settings.setNumberOfIterationsUntilRestart(50); + solver.setSettings(settings); ASSERT_NO_THROW(solver.solveEquations(x, b)); ASSERT_LT(std::abs(x[0] - 1), storm::settings::getModule().getPrecision()); @@ -86,16 +88,20 @@ TEST(GmmxxLinearEquationSolver, qmr) { std::vector b = {16, -4, -7}; storm::solver::GmmxxLinearEquationSolver solver(A); - solver.getSettings().setSolutionMethod(storm::solver::GmmxxLinearEquationSolverSettings::SolutionMethod::Qmr); - solver.getSettings().setPrecision(1e-6); - solver.getSettings().setMaximalNumberOfIterations(10000); - solver.getSettings().setPreconditioner(storm::solver::GmmxxLinearEquationSolverSettings::Preconditioner::None); + auto settings = solver.getSettings(); + settings.setSolutionMethod(storm::solver::GmmxxLinearEquationSolverSettings::SolutionMethod::Qmr); + settings.setPrecision(1e-6); + settings.setMaximalNumberOfIterations(10000); + settings.setPreconditioner(storm::solver::GmmxxLinearEquationSolverSettings::Preconditioner::None); + solver.setSettings(settings); storm::solver::GmmxxLinearEquationSolver solver2(A); - solver2.getSettings().setSolutionMethod(storm::solver::GmmxxLinearEquationSolverSettings::SolutionMethod::Qmr); - solver2.getSettings().setPrecision(1e-6); - solver2.getSettings().setMaximalNumberOfIterations(10000); - solver2.getSettings().setPreconditioner(storm::solver::GmmxxLinearEquationSolverSettings::Preconditioner::None); + auto settings2 = solver2.getSettings(); + settings2.setSolutionMethod(storm::solver::GmmxxLinearEquationSolverSettings::SolutionMethod::Qmr); + settings2.setPrecision(1e-6); + settings2.setMaximalNumberOfIterations(10000); + settings2.setPreconditioner(storm::solver::GmmxxLinearEquationSolverSettings::Preconditioner::None); + solver2.setSettings(settings2); ASSERT_NO_THROW(solver2.solveEquations(x, b)); ASSERT_LT(std::abs(x[0] - 1), storm::settings::getModule().getPrecision()); @@ -123,10 +129,12 @@ TEST(GmmxxLinearEquationSolver, bicgstab) { std::vector b = {16, -4, -7}; storm::solver::GmmxxLinearEquationSolver solver(A); - solver.getSettings().setSolutionMethod(storm::solver::GmmxxLinearEquationSolverSettings::SolutionMethod::Bicgstab); - solver.getSettings().setPrecision(1e-6); - solver.getSettings().setMaximalNumberOfIterations(10000); - solver.getSettings().setPreconditioner(storm::solver::GmmxxLinearEquationSolverSettings::Preconditioner::None); + auto settings = solver.getSettings(); + settings.setSolutionMethod(storm::solver::GmmxxLinearEquationSolverSettings::SolutionMethod::Bicgstab); + settings.setPrecision(1e-6); + settings.setMaximalNumberOfIterations(10000); + settings.setPreconditioner(storm::solver::GmmxxLinearEquationSolverSettings::Preconditioner::None); + solver.setSettings(settings); ASSERT_NO_THROW(solver.solveEquations(x, b)); ASSERT_LT(std::abs(x[0] - 1), storm::settings::getModule().getPrecision()); @@ -154,10 +162,12 @@ TEST(GmmxxLinearEquationSolver, jacobi) { std::vector b = {11, -16, 1}; storm::solver::GmmxxLinearEquationSolver solver(A); - solver.getSettings().setSolutionMethod(storm::solver::GmmxxLinearEquationSolverSettings::SolutionMethod::Jacobi); - solver.getSettings().setPrecision(1e-6); - solver.getSettings().setMaximalNumberOfIterations(10000); - solver.getSettings().setPreconditioner(storm::solver::GmmxxLinearEquationSolverSettings::Preconditioner::None); + auto settings = solver.getSettings(); + settings.setSolutionMethod(storm::solver::GmmxxLinearEquationSolverSettings::SolutionMethod::Jacobi); + settings.setPrecision(1e-6); + settings.setMaximalNumberOfIterations(10000); + settings.setPreconditioner(storm::solver::GmmxxLinearEquationSolverSettings::Preconditioner::None); + solver.setSettings(settings); ASSERT_NO_THROW(solver.solveEquations(x, b)); ASSERT_LT(std::abs(x[0] - 1), storm::settings::getModule().getPrecision()); ASSERT_LT(std::abs(x[1] - 3), storm::settings::getModule().getPrecision()); @@ -184,11 +194,13 @@ TEST(GmmxxLinearEquationSolver, gmresilu) { std::vector b = {16, -4, -7}; storm::solver::GmmxxLinearEquationSolver solver(A); - solver.getSettings().setSolutionMethod(storm::solver::GmmxxLinearEquationSolverSettings::SolutionMethod::Gmres); - solver.getSettings().setPrecision(1e-6); - solver.getSettings().setMaximalNumberOfIterations(10000); - solver.getSettings().setPreconditioner(storm::solver::GmmxxLinearEquationSolverSettings::Preconditioner::Ilu); - solver.getSettings().setNumberOfIterationsUntilRestart(50); + auto settings = solver.getSettings(); + settings.setSolutionMethod(storm::solver::GmmxxLinearEquationSolverSettings::SolutionMethod::Gmres); + settings.setPrecision(1e-6); + settings.setMaximalNumberOfIterations(10000); + settings.setPreconditioner(storm::solver::GmmxxLinearEquationSolverSettings::Preconditioner::Ilu); + settings.setNumberOfIterationsUntilRestart(50); + solver.setSettings(settings); ASSERT_NO_THROW(solver.solveEquations(x, b)); ASSERT_LT(std::abs(x[0] - 1), storm::settings::getModule().getPrecision()); ASSERT_LT(std::abs(x[1] - 3), storm::settings::getModule().getPrecision()); @@ -215,11 +227,13 @@ TEST(GmmxxLinearEquationSolver, gmresdiag) { std::vector b = {16, -4, -7}; storm::solver::GmmxxLinearEquationSolver solver(A); - solver.getSettings().setSolutionMethod(storm::solver::GmmxxLinearEquationSolverSettings::SolutionMethod::Gmres); - solver.getSettings().setPrecision(1e-6); - solver.getSettings().setMaximalNumberOfIterations(10000); - solver.getSettings().setPreconditioner(storm::solver::GmmxxLinearEquationSolverSettings::Preconditioner::Diagonal); - solver.getSettings().setNumberOfIterationsUntilRestart(50); + auto settings = solver.getSettings(); + settings.setSolutionMethod(storm::solver::GmmxxLinearEquationSolverSettings::SolutionMethod::Gmres); + settings.setPrecision(1e-6); + settings.setMaximalNumberOfIterations(10000); + settings.setPreconditioner(storm::solver::GmmxxLinearEquationSolverSettings::Preconditioner::Diagonal); + settings.setNumberOfIterationsUntilRestart(50); + solver.setSettings(settings); ASSERT_NO_THROW(solver.solveEquations(x, b)); ASSERT_LT(std::abs(x[0] - 1), storm::settings::getModule().getPrecision()); @@ -248,4 +262,4 @@ TEST(GmmxxLinearEquationSolver, MatrixVectorMultiplication) { storm::solver::GmmxxLinearEquationSolver solver(A); ASSERT_NO_THROW(solver.repeatedMultiply(x, nullptr, 4)); ASSERT_LT(std::abs(x[0] - 1), storm::settings::getModule().getPrecision()); -} \ No newline at end of file +} diff --git a/test/functional/transformer/EndComponentEliminatorTest.cpp b/test/functional/transformer/EndComponentEliminatorTest.cpp new file mode 100644 index 000000000..9a990f409 --- /dev/null +++ b/test/functional/transformer/EndComponentEliminatorTest.cpp @@ -0,0 +1,82 @@ +#include "gtest/gtest.h" + +#include "src/transformer/EndComponentEliminator.h" + + +TEST(NeutralECRemover, SimpleModelTest) { + + + storm::storage::SparseMatrixBuilder builder(12, 5, 19, true, true, 5); + ASSERT_NO_THROW(builder.newRowGroup(0)); + ASSERT_NO_THROW(builder.addNextValue(0, 0, 1.0)); + ASSERT_NO_THROW(builder.addNextValue(1, 1, 0.3)); + ASSERT_NO_THROW(builder.addNextValue(1, 2, 0.1)); + ASSERT_NO_THROW(builder.addNextValue(1, 3, 0.4)); + ASSERT_NO_THROW(builder.addNextValue(1, 4, 0.2)); + ASSERT_NO_THROW(builder.newRowGroup(2)); + ASSERT_NO_THROW(builder.addNextValue(2, 1, 0.7)); + ASSERT_NO_THROW(builder.addNextValue(2, 3, 0.3)); + ASSERT_NO_THROW(builder.addNextValue(3, 1, 0.1)); + ASSERT_NO_THROW(builder.addNextValue(3, 4, 0.9)); + ASSERT_NO_THROW(builder.addNextValue(4, 1, 0.2)); + ASSERT_NO_THROW(builder.addNextValue(4, 4, 0.8)); + ASSERT_NO_THROW(builder.newRowGroup(5)); + ASSERT_NO_THROW(builder.addNextValue(5, 2, 1.0)); + ASSERT_NO_THROW(builder.newRowGroup(6)); + ASSERT_NO_THROW(builder.addNextValue(6, 1, 1.0)); + ASSERT_NO_THROW(builder.addNextValue(7, 2, 1.0)); + ASSERT_NO_THROW(builder.addNextValue(8, 3, 1.0)); + ASSERT_NO_THROW(builder.newRowGroup(9)); + ASSERT_NO_THROW(builder.addNextValue(9, 4, 1.0)); + ASSERT_NO_THROW(builder.addNextValue(10, 1, 0.4)); + ASSERT_NO_THROW(builder.addNextValue(10, 4, 0.6)); + ASSERT_NO_THROW(builder.addNextValue(11, 3, 1)); + storm::storage::SparseMatrix matrix; + ASSERT_NO_THROW(matrix = builder.build()); + + storm::storage::BitVector subsystem(5, true); + subsystem.set(2, false); + + storm::storage::BitVector possibleEcRows(12, true); + possibleEcRows.set(3, false); + possibleEcRows.set(6, false); + possibleEcRows.set(7, false); + possibleEcRows.set(8, false); + possibleEcRows.set(11, false); + + storm::storage::BitVector allowEmptyRows(5, true); + allowEmptyRows.set(1, false); + allowEmptyRows.set(4, false); + + + auto res = storm::transformer::EndComponentEliminator::transform(matrix, subsystem, possibleEcRows, allowEmptyRows); + + // Expected data + storm::storage::SparseMatrixBuilder expectedBuilder(8, 3, 8, true, true, 3); + ASSERT_NO_THROW(expectedBuilder.newRowGroup(0)); + ASSERT_NO_THROW(expectedBuilder.addNextValue(0, 2, 1.0)); + ASSERT_NO_THROW(expectedBuilder.addNextValue(2, 0, 1.0)); + ASSERT_NO_THROW(expectedBuilder.newRowGroup(3)); + ASSERT_NO_THROW(expectedBuilder.addNextValue(3, 0, 0.4)); + ASSERT_NO_THROW(expectedBuilder.addNextValue(3, 2, 0.5)); + ASSERT_NO_THROW(expectedBuilder.newRowGroup(5)); + ASSERT_NO_THROW(expectedBuilder.addNextValue(5, 0, 1.0)); + ASSERT_NO_THROW(expectedBuilder.addNextValue(6, 0, 0.3)); + ASSERT_NO_THROW(expectedBuilder.addNextValue(6, 2, 0.7)); + ASSERT_NO_THROW(expectedBuilder.addNextValue(7, 2, 1.0)); + storm::storage::SparseMatrix expectedMatrix; + ASSERT_NO_THROW(expectedMatrix = expectedBuilder.build()); + + std::vector expectedNewToOldRowMapping = {6,7,8,1,0,11,2,3}; + + std::vector expectedOldToNewStateMapping = {1,2,std::numeric_limits::max(), 0, 2}; + + // Note that there are other possible solutions that yield equivalent matrices / vectors. + // In particular, the ordering within the row groups depends on the MEC decomposition implementation. + // However, this is not checked here... + EXPECT_EQ(expectedMatrix, res.matrix); + EXPECT_EQ(expectedNewToOldRowMapping, res.newToOldRowMapping); + EXPECT_EQ(expectedOldToNewStateMapping, res.oldToNewStateMapping); + //std::cout << "Original matrix:" << std::endl << matrix << std::endl << std::endl << "Computation Result: " << std::endl << res.matrix << std::endl<< std::endl << "expected Matrix" << std::endl<< expectedMatrix << std::endl; + +} diff --git a/test/functional/transformer/StateDuplicatorTest.cpp b/test/functional/transformer/StateDuplicatorTest.cpp new file mode 100644 index 000000000..79e465ce1 --- /dev/null +++ b/test/functional/transformer/StateDuplicatorTest.cpp @@ -0,0 +1,100 @@ +#include "gtest/gtest.h" + +#include "src/transformer/StateDuplicator.h" + + +TEST(StateDuplicator, SimpleModelTest) { + + storm::storage::SparseMatrix matrix; + storm::storage::SparseMatrixBuilder builder(6, 4, 7, true, true, 4); + ASSERT_NO_THROW(builder.newRowGroup(0)); + ASSERT_NO_THROW(builder.addNextValue(0, 0, 0.3)); + ASSERT_NO_THROW(builder.addNextValue(0, 1, 0.7)); + ASSERT_NO_THROW(builder.addNextValue(1, 3, 1.0)); + ASSERT_NO_THROW(builder.newRowGroup(2)); + ASSERT_NO_THROW(builder.addNextValue(2, 1, 1.0)); + ASSERT_NO_THROW(builder.newRowGroup(3)); + ASSERT_NO_THROW(builder.addNextValue(3, 0, 1.0)); + ASSERT_NO_THROW(builder.newRowGroup(4)); + ASSERT_NO_THROW(builder.addNextValue(4, 0, 1.0)); + ASSERT_NO_THROW(builder.addNextValue(5, 3, 1.0)); + ASSERT_NO_THROW(matrix = builder.build()); + + storm::models::sparse::StateLabeling labeling(4); + storm::storage::BitVector initStates(4); + initStates.set(0); + labeling.addLabel("init", initStates); + storm::storage::BitVector gateStates(4); + gateStates.set(3); + labeling.addLabel("gate", gateStates); + storm::storage::BitVector aStates(4); + aStates.set(0); + aStates.set(2); + labeling.addLabel("a", aStates); + storm::storage::BitVector bStates(4); + bStates.set(1); + bStates.set(3); + labeling.addLabel("b", bStates); + + std::unordered_map> rewardModels; + std::vector stateReward = {1.0, 2.0, 3.0, 4.0}; + std::vector stateActionReward = {1.1, 1.2, 2.1, 3.1, 4.1, 4.2}; + rewardModels.insert(std::make_pair("rewards", storm::models::sparse::StandardRewardModel(stateReward, stateActionReward))); + + storm::models::sparse::Mdp model(matrix, labeling, rewardModels); + + auto res = storm::transformer::StateDuplicator>::transform(model, gateStates); + + storm::storage::SparseMatrixBuilder expectedBuilder(8, 5, 10, true, true, 5); + ASSERT_NO_THROW(expectedBuilder.newRowGroup(0)); + ASSERT_NO_THROW(expectedBuilder.addNextValue(0, 0, 0.3)); + ASSERT_NO_THROW(expectedBuilder.addNextValue(0, 1, 0.7)); + ASSERT_NO_THROW(expectedBuilder.addNextValue(1, 2, 1.0)); + ASSERT_NO_THROW(expectedBuilder.newRowGroup(2)); + ASSERT_NO_THROW(expectedBuilder.addNextValue(2, 1, 1.0)); + ASSERT_NO_THROW(expectedBuilder.newRowGroup(3)); + ASSERT_NO_THROW(expectedBuilder.addNextValue(3, 3, 1.0)); + ASSERT_NO_THROW(expectedBuilder.addNextValue(4, 2, 1.0)); + ASSERT_NO_THROW(expectedBuilder.newRowGroup(5)); + ASSERT_NO_THROW(expectedBuilder.addNextValue(5, 3, 0.3)); + ASSERT_NO_THROW(expectedBuilder.addNextValue(5, 4, 0.7)); + ASSERT_NO_THROW(expectedBuilder.addNextValue(6, 2, 1.0)); + ASSERT_NO_THROW(expectedBuilder.newRowGroup(7)); + ASSERT_NO_THROW(expectedBuilder.addNextValue(7, 4, 1.0)); + ASSERT_NO_THROW(matrix = expectedBuilder.build()); + EXPECT_EQ(matrix, res.model->getTransitionMatrix()); + + initStates.resize(5); + EXPECT_EQ(initStates, res.model->getInitialStates()); + gateStates=storm::storage::BitVector(5); + gateStates.set(2); + EXPECT_EQ(gateStates, res.model->getStates("gate")); + aStates = initStates; + aStates.set(3); + EXPECT_EQ(aStates, res.model->getStates("a")); + bStates = ~aStates; + EXPECT_EQ(bStates, res.model->getStates("b")); + + EXPECT_TRUE(res.model->hasRewardModel("rewards")); + EXPECT_TRUE(res.model->getRewardModel("rewards").hasStateRewards()); + stateReward = {1.0, 2.0, 4.0, 1.0, 2.0}; + EXPECT_EQ(stateReward, res.model->getRewardModel("rewards").getStateRewardVector()); + EXPECT_TRUE(res.model->getRewardModel("rewards").hasStateActionRewards()); + stateActionReward = {1.1, 1.2, 2.1, 4.1, 4.2, 1.1, 1.2, 2.1}; + EXPECT_EQ(stateActionReward, res.model->getRewardModel("rewards").getStateActionRewardVector()); + + storm::storage::BitVector firstCopy(5); + firstCopy.set(0); + firstCopy.set(1); + EXPECT_EQ(firstCopy, res.firstCopy); + EXPECT_EQ(~firstCopy, res.secondCopy); + + std::vector mapping = {0,1,3,0,1}; + EXPECT_EQ(mapping, res.newToOldStateIndexMapping); + uint_fast64_t max = std::numeric_limits::max(); + mapping = {0, 1, max, max}; + EXPECT_EQ(mapping, res.firstCopyOldToNewStateIndexMapping); + mapping = {3, 4, max, 2}; + EXPECT_EQ(mapping, res.secondCopyOldToNewStateIndexMapping); + +}