diff --git a/.gitignore b/.gitignore index f825daea1..aa15c7b9a 100644 --- a/.gitignore +++ b/.gitignore @@ -33,9 +33,13 @@ resources/3rdparty/cudd-2.5.0/ ipch/ obj/ CMakeFiles/ +CPackConfig.cmake # The build Dir build/ build//CMakeLists.txt /*.vcxproj /*.filters /*.sln +#Temp texteditor files +*.orig +*.*~ diff --git a/CMakeLists.txt b/CMakeLists.txt index fa3485ad3..072e50b68 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,6 +123,9 @@ elseif(MSVC) # Windows.h breaks GMM in gmm_except.h because of its macro definition for min and max add_definitions(/DNOMINMAX) + # since nobody cares at the moment + add_definitions(/wd4250) + if(ENABLE_Z3) set(Z3_LIB_NAME "libz3") endif() @@ -224,6 +227,13 @@ configure_file ( "${PROJECT_SOURCE_DIR}/storm-config.h.in" "${PROJECT_BINARY_DIR}/include/storm-config.h" ) + +# Configure a header file to pass the storm version to the source code +configure_file ( + "${PROJECT_SOURCE_DIR}/storm-version.h.in" + "${PROJECT_BINARY_DIR}/include/storm-version.h" +) + # Add the binary dir include directory for storm-config.h include_directories("${PROJECT_BINARY_DIR}/include") @@ -250,11 +260,11 @@ file(GLOB STORM_PARSER_FILES ${PROJECT_SOURCE_DIR}/src/parser/*.h ${PROJECT_SOUR file(GLOB_RECURSE STORM_PARSER_PRISMPARSER_FILES ${PROJECT_SOURCE_DIR}/src/parser/prismparser/*.h ${PROJECT_SOURCE_DIR}/src/parser/prismparser/*.cpp) file(GLOB_RECURSE STORM_SETTINGS_FILES ${PROJECT_SOURCE_DIR}/src/settings/*.h ${PROJECT_SOURCE_DIR}/src/settings/*.cpp) file(GLOB_RECURSE STORM_SOLVER_FILES ${PROJECT_SOURCE_DIR}/src/solver/*.h ${PROJECT_SOURCE_DIR}/src/solver/*.cpp) -file(GLOB_RECURSE STORM_STORAGE_FILES ${PROJECT_SOURCE_DIR}/src/storage/*.h ${PROJECT_SOURCE_DIR}/src/storage/*.cpp) +file(GLOB STORM_STORAGE_FILES ${PROJECT_SOURCE_DIR}/src/storage/*.h ${PROJECT_SOURCE_DIR}/src/storage/*.cpp) +file(GLOB_RECURSE STORM_STORAGE_DD_FILES ${PROJECT_SOURCE_DIR}/src/storage/dd/*.h ${PROJECT_SOURCE_DIR}/src/storage/dd/*.cpp) +file(GLOB_RECURSE STORM_STORAGE_EXPRESSIONS_FILES ${PROJECT_SOURCE_DIR}/src/storage/expressions/*.h ${PROJECT_SOURCE_DIR}/src/storage/expressions/*.cpp) +file(GLOB_RECURSE STORM_STORAGE_PRISM_FILES ${PROJECT_SOURCE_DIR}/src/storage/prism/*.h ${PROJECT_SOURCE_DIR}/src/storage/prism/*.cpp) file(GLOB_RECURSE STORM_UTILITY_FILES ${PROJECT_SOURCE_DIR}/src/utility/*.h ${PROJECT_SOURCE_DIR}/src/utility/*.cpp) -file(GLOB STORM_IR_FILES ${PROJECT_SOURCE_DIR}/src/ir/*.h ${PROJECT_SOURCE_DIR}/src/ir/*.cpp) -file(GLOB_RECURSE STORM_IR_EXPRESSIONS_FILES ${PROJECT_SOURCE_DIR}/src/ir/expressions/*.h ${PROJECT_SOURCE_DIR}/src/ir/expressions/*.cpp) - # Test Sources # Note that the tests also need the source files, except for the main file file(GLOB_RECURSE STORM_FUNCTIONAL_TEST_FILES ${STORM_CPP_TESTS_BASE_PATH}/functional/*.h ${STORM_CPP_TESTS_BASE_PATH}/functional/*.cpp) @@ -273,8 +283,6 @@ source_group(formula\\csl FILES ${STORM_FORMULA_CSL_FILES}) source_group(formula\\ltl FILES ${STORM_FORMULA_LTL_FILES}) source_group(formula\\prctl FILES ${STORM_FORMULA_PRCTL_FILES}) source_group(generated FILES ${STORM_BUILD_HEADERS}) -source_group(ir FILES ${STORM_IR_FILES}) -source_group(ir\\expressions FILES ${STORM_IR_EXPRESSIONS_FILES}) source_group(modelchecker FILES ${STORM_MODELCHECKER_FILES}) source_group(counterexamples FILES ${STORM_COUNTEREXAMPLES_FILES}) source_group(models FILES ${STORM_MODELS_FILES}) @@ -283,6 +291,9 @@ source_group(parser\\prismparser FILES ${STORM_PARSER_PRISMPARSER_FILES}) source_group(settings FILES ${STORM_SETTINGS_FILES}) source_group(solver FILES ${STORM_SOLVER_FILES}) source_group(storage FILES ${STORM_STORAGE_FILES}) +source_group(storage\\dd FILES ${STORM_STORAGE_DD_FILES}) +source_group(storage\\expressions FILES ${STORM_STORAGE_EXPRESSIONS_FILES}) +source_group(storage\\prism FILES ${STORM_STORAGE_PRISM_FILES}) source_group(utility FILES ${STORM_UTILITY_FILES}) source_group(functional-test FILES ${STORM_FUNCTIONAL_TEST_FILES}) source_group(performance-test FILES ${STORM_PERFORMANCE_TEST_FILES}) @@ -542,4 +553,4 @@ INSTALL(TARGETS storm storm-functional-tests storm-performance-tests LIBRARY DESTINATION lib ARCHIVE DESTINATION lib ) -include(CPackConfig.cmake) +include(StormCPackConfig.cmake) \ No newline at end of file diff --git a/CPackConfig.cmake b/StormCPackConfig.cmake similarity index 100% rename from CPackConfig.cmake rename to StormCPackConfig.cmake diff --git a/examples/dtmc/synchronous_leader/leader4_8.pm b/examples/dtmc/synchronous_leader/leader4_8.pm index fc8f167a0..188039cf3 100644 --- a/examples/dtmc/synchronous_leader/leader4_8.pm +++ b/examples/dtmc/synchronous_leader/leader4_8.pm @@ -4,8 +4,8 @@ dtmc // CONSTANTS -const N = 4; // number of processes -const K = 8; // range of probabilistic choice +const int N = 4; // number of processes +const int K = 8; // range of probabilistic choice // counter module used to count the number of processes that have been read // and to know when a process has decided diff --git a/examples/dtmc/synchronous_leader/leader5_8.pm b/examples/dtmc/synchronous_leader/leader5_8.pm index 72139fda9..4723ed5b0 100644 --- a/examples/dtmc/synchronous_leader/leader5_8.pm +++ b/examples/dtmc/synchronous_leader/leader5_8.pm @@ -4,8 +4,8 @@ dtmc // CONSTANTS -const N = 5; // number of processes -const K = 8; // range of probabilistic choice +const int N = 5; // number of processes +const int K = 8; // range of probabilistic choice // counter module used to count the number of processes that have been read // and to know when a process has decided diff --git a/examples/dtmc/synchronous_leader/leader6_8.pm b/examples/dtmc/synchronous_leader/leader6_8.pm index 283042ad5..a31773b1d 100644 --- a/examples/dtmc/synchronous_leader/leader6_8.pm +++ b/examples/dtmc/synchronous_leader/leader6_8.pm @@ -4,8 +4,8 @@ dtmc // CONSTANTS -const N = 6; // number of processes -const K = 8; // range of probabilistic choice +const int N = 6; // number of processes +const int K = 8; // range of probabilistic choice // counter module used to count the number of processes that have been read // and to know when a process has decided diff --git a/resources/3rdparty/cudd-2.5.0/CMakeLists.txt b/resources/3rdparty/cudd-2.5.0/CMakeLists.txt index c7cff2aca..4d08513de 100644 --- a/resources/3rdparty/cudd-2.5.0/CMakeLists.txt +++ b/resources/3rdparty/cudd-2.5.0/CMakeLists.txt @@ -25,5 +25,14 @@ if(MSVC) add_definitions(/D_SCL_SECURE_NO_DEPRECATE /D_CRT_SECURE_NO_WARNINGS) endif() +# Since we do not target Alphas, this symbol is always set +add_definitions(-DHAVE_IEEE_754) + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + message(STATUS "CUDD: Targeting 64bit architecture") + add_definitions(-DSIZEOF_VOID_P=8) + add_definitions(-DSIZEOF_LONG=8) +endif() + # Add the library add_library(cudd ${CUDD_SOURCES} ${CUDD_HEADERS} ${CUDD_HEADERS_CXX} ${CUDD_SOURCES_CXX}) \ No newline at end of file diff --git a/resources/3rdparty/cudd-2.5.0/src/cudd/cudd.h b/resources/3rdparty/cudd-2.5.0/src/cudd/cudd.h index 8e3686170..51d509b35 100644 --- a/resources/3rdparty/cudd-2.5.0/src/cudd/cudd.h +++ b/resources/3rdparty/cudd-2.5.0/src/cudd/cudd.h @@ -767,6 +767,8 @@ extern int Cudd_bddVarIsBound (DdManager *dd, int index); extern DdNode * Cudd_addExistAbstract (DdManager *manager, DdNode *f, DdNode *cube); extern DdNode * Cudd_addUnivAbstract (DdManager *manager, DdNode *f, DdNode *cube); extern DdNode * Cudd_addOrAbstract (DdManager *manager, DdNode *f, DdNode *cube); +extern DdNode * Cudd_addMinAbstract(DdManager * manager, DdNode * f, DdNode * cube); +extern DdNode * Cudd_addMaxAbstract(DdManager * manager, DdNode * f, DdNode * cube); extern DdNode * Cudd_addApply (DdManager *dd, DdNode * (*)(DdManager *, DdNode **, DdNode **), DdNode *f, DdNode *g); extern DdNode * Cudd_addPlus (DdManager *dd, DdNode **f, DdNode **g); extern DdNode * Cudd_addTimes (DdManager *dd, DdNode **f, DdNode **g); @@ -962,6 +964,7 @@ extern DdNode * Cudd_Increasing (DdManager *dd, DdNode *f, int i); extern int Cudd_EquivDC (DdManager *dd, DdNode *F, DdNode *G, DdNode *D); extern int Cudd_bddLeqUnless (DdManager *dd, DdNode *f, DdNode *g, DdNode *D); extern int Cudd_EqualSupNorm (DdManager *dd, DdNode *f, DdNode *g, CUDD_VALUE_TYPE tolerance, int pr); +extern int Cudd_EqualSupNormRel (DdManager *dd, DdNode *f, DdNode *g, CUDD_VALUE_TYPE tolerance, int pr); extern DdNode * Cudd_bddMakePrime (DdManager *dd, DdNode *cube, DdNode *f); extern DdNode * Cudd_bddMaximallyExpand(DdManager *dd, DdNode *lb, DdNode *ub, DdNode *f); extern DdNode * Cudd_bddLargestPrimeUnate(DdManager *dd , DdNode *f, DdNode *phaseBdd); diff --git a/resources/3rdparty/cudd-2.5.0/src/cudd/cuddAddAbs.c b/resources/3rdparty/cudd-2.5.0/src/cudd/cuddAddAbs.c index 5e809c134..7d774586c 100644 --- a/resources/3rdparty/cudd-2.5.0/src/cudd/cuddAddAbs.c +++ b/resources/3rdparty/cudd-2.5.0/src/cudd/cuddAddAbs.c @@ -11,12 +11,16 @@ <li> Cudd_addExistAbstract() <li> Cudd_addUnivAbstract() <li> Cudd_addOrAbstract() + <li> Cudd_addMinAbstract() + <li> Cudd_addMaxAbstract() </ul> Internal procedures included in this module: <ul> <li> cuddAddExistAbstractRecur() <li> cuddAddUnivAbstractRecur() <li> cuddAddOrAbstractRecur() + <li> cuddAddMinAbstractRecur() + <li> cuddAddMaxAbstractRecur() </ul> Static procedures included in this module: <ul> @@ -229,6 +233,82 @@ Cudd_addOrAbstract( } /* end of Cudd_addOrAbstract */ +/**Function******************************************************************** + + Synopsis [Abstracts all the variables in cube from the + ADD f by taking the minimum.] + + Description [Abstracts all the variables in cube from the ADD f + by taking the minimum over all possible values taken by the + variables. Returns the abstracted ADD if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addUnivAbstract Cudd_addExistAbstract] + + Note: Added by Dave Parker 14/6/99 + + ******************************************************************************/ +DdNode * +Cudd_addMinAbstract( + DdManager * manager, + DdNode * f, + DdNode * cube) +{ + DdNode *res; + + if (addCheckPositiveCube(manager, cube) == 0) { + (void) fprintf(manager->err,"Error: Can only abstract cubes"); + return(NULL); + } + + do { + manager->reordered = 0; + res = cuddAddMinAbstractRecur(manager, f, cube); + } while (manager->reordered == 1); + return(res); + +} /* end of Cudd_addMinAbstract */ + + +/**Function******************************************************************** + + Synopsis [Abstracts all the variables in cube from the + ADD f by taking the maximum.] + + Description [Abstracts all the variables in cube from the ADD f + by taking the maximum over all possible values taken by the + variables. Returns the abstracted ADD if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addUnivAbstract Cudd_addExistAbstract] + + Note: Added by Dave Parker 14/6/99 + + ******************************************************************************/ +DdNode * +Cudd_addMaxAbstract( + DdManager * manager, + DdNode * f, + DdNode * cube) +{ + DdNode *res; + + if (addCheckPositiveCube(manager, cube) == 0) { + (void) fprintf(manager->err,"Error: Can only abstract cubes"); + return(NULL); + } + + do { + manager->reordered = 0; + res = cuddAddMaxAbstractRecur(manager, f, cube); + } while (manager->reordered == 1); + return(res); + +} /* end of Cudd_addMaxAbstract */ /*---------------------------------------------------------------------------*/ /* Definition of internal functions */ @@ -542,7 +622,188 @@ cuddAddOrAbstractRecur( } /* end of cuddAddOrAbstractRecur */ +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_addMinAbstract.] + + Description [Performs the recursive step of Cudd_addMinAbstract. + Returns the ADD obtained by abstracting the variables of cube from f, + if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + + ******************************************************************************/ +DdNode * +cuddAddMinAbstractRecur( + DdManager * manager, + DdNode * f, + DdNode * cube) +{ + DdNode *T, *E, *res, *res1, *res2, *zero; + + zero = DD_ZERO(manager); + + /* Cube is guaranteed to be a cube at this point. */ + if (f == zero || cuddIsConstant(cube)) { + return(f); + } + + /* Abstract a variable that does not appear in f. */ + if (cuddI(manager,f->index) > cuddI(manager,cube->index)) { + res = cuddAddMinAbstractRecur(manager, f, cuddT(cube)); + return(res); + } + + if ((res = cuddCacheLookup2(manager, Cudd_addMinAbstract, f, cube)) != NULL) { + return(res); + } + + + T = cuddT(f); + E = cuddE(f); + + /* If the two indices are the same, so are their levels. */ + if (f->index == cube->index) { + res1 = cuddAddMinAbstractRecur(manager, T, cuddT(cube)); + if (res1 == NULL) return(NULL); + cuddRef(res1); + res2 = cuddAddMinAbstractRecur(manager, E, cuddT(cube)); + if (res2 == NULL) { + Cudd_RecursiveDeref(manager,res1); + return(NULL); + } + cuddRef(res2); + res = cuddAddApplyRecur(manager, Cudd_addMinimum, res1, res2); + if (res == NULL) { + Cudd_RecursiveDeref(manager,res1); + Cudd_RecursiveDeref(manager,res2); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(manager,res1); + Cudd_RecursiveDeref(manager,res2); + cuddCacheInsert2(manager, Cudd_addMinAbstract, f, cube, res); + cuddDeref(res); + return(res); + } + else { /* if (cuddI(manager,f->index) < cuddI(manager,cube->index)) */ + res1 = cuddAddMinAbstractRecur(manager, T, cube); + if (res1 == NULL) return(NULL); + cuddRef(res1); + res2 = cuddAddMinAbstractRecur(manager, E, cube); + if (res2 == NULL) { + Cudd_RecursiveDeref(manager,res1); + return(NULL); + } + cuddRef(res2); + res = (res1 == res2) ? res1 : + cuddUniqueInter(manager, (int) f->index, res1, res2); + if (res == NULL) { + Cudd_RecursiveDeref(manager,res1); + Cudd_RecursiveDeref(manager,res2); + return(NULL); + } + cuddDeref(res1); + cuddDeref(res2); + cuddCacheInsert2(manager, Cudd_addMinAbstract, f, cube, res); + return(res); + } + +} /* end of cuddAddMinAbstractRecur */ + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_addMaxAbstract.] + + Description [Performs the recursive step of Cudd_addMaxAbstract. + Returns the ADD obtained by abstracting the variables of cube from f, + if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + + ******************************************************************************/ +DdNode * +cuddAddMaxAbstractRecur( + DdManager * manager, + DdNode * f, + DdNode * cube) +{ + DdNode *T, *E, *res, *res1, *res2, *zero; + + zero = DD_ZERO(manager); + + /* Cube is guaranteed to be a cube at this point. */ + if (f == zero || cuddIsConstant(cube)) { + return(f); + } + + /* Abstract a variable that does not appear in f. */ + if (cuddI(manager,f->index) > cuddI(manager,cube->index)) { + res = cuddAddMaxAbstractRecur(manager, f, cuddT(cube)); + return(res); + } + + if ((res = cuddCacheLookup2(manager, Cudd_addMaxAbstract, f, cube)) != NULL) { + return(res); + } + + + T = cuddT(f); + E = cuddE(f); + + /* If the two indices are the same, so are their levels. */ + if (f->index == cube->index) { + res1 = cuddAddMaxAbstractRecur(manager, T, cuddT(cube)); + if (res1 == NULL) return(NULL); + cuddRef(res1); + res2 = cuddAddMaxAbstractRecur(manager, E, cuddT(cube)); + if (res2 == NULL) { + Cudd_RecursiveDeref(manager,res1); + return(NULL); + } + cuddRef(res2); + res = cuddAddApplyRecur(manager, Cudd_addMaximum, res1, res2); + if (res == NULL) { + Cudd_RecursiveDeref(manager,res1); + Cudd_RecursiveDeref(manager,res2); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(manager,res1); + Cudd_RecursiveDeref(manager,res2); + cuddCacheInsert2(manager, Cudd_addMaxAbstract, f, cube, res); + cuddDeref(res); + return(res); + } + else { /* if (cuddI(manager,f->index) < cuddI(manager,cube->index)) */ + res1 = cuddAddMaxAbstractRecur(manager, T, cube); + if (res1 == NULL) return(NULL); + cuddRef(res1); + res2 = cuddAddMaxAbstractRecur(manager, E, cube); + if (res2 == NULL) { + Cudd_RecursiveDeref(manager,res1); + return(NULL); + } + cuddRef(res2); + res = (res1 == res2) ? res1 : + cuddUniqueInter(manager, (int) f->index, res1, res2); + if (res == NULL) { + Cudd_RecursiveDeref(manager,res1); + Cudd_RecursiveDeref(manager,res2); + return(NULL); + } + cuddDeref(res1); + cuddDeref(res2); + cuddCacheInsert2(manager, Cudd_addMaxAbstract, f, cube, res); + return(res); + } + +} /* end of cuddAddMaxAbstractRecur */ /*---------------------------------------------------------------------------*/ /* Definition of static functions */ diff --git a/resources/3rdparty/cudd-2.5.0/src/cudd/cuddExport.c b/resources/3rdparty/cudd-2.5.0/src/cudd/cuddExport.c index 3d8da77ac..2392da36a 100644 --- a/resources/3rdparty/cudd-2.5.0/src/cudd/cuddExport.c +++ b/resources/3rdparty/cudd-2.5.0/src/cudd/cuddExport.c @@ -502,9 +502,11 @@ Cudd_DumpDot( scan = nodelist[j]; while (scan != NULL) { if (st_is_member(visited,(char *) scan)) { - retval = fprintf(fp,"\"%p\";\n", - (void *) ((mask & (ptrint) scan) / sizeof(DdNode))); - if (retval == EOF) goto failure; + if (scan != Cudd_ReadZero(dd)) { + retval = fprintf(fp,"\"%p\";\n", + (void *) ((mask & (ptrint) scan) / sizeof(DdNode))); + if (retval == EOF) goto failure; + } } scan = scan->next; } @@ -541,6 +543,12 @@ Cudd_DumpDot( scan = nodelist[j]; while (scan != NULL) { if (st_is_member(visited,(char *) scan)) { + retval = fprintf(fp, + "\"%p\" [label = \"%s\"];\n", + (void *) ((mask & (ptrint) scan) / + sizeof(DdNode)), inames[dd->invperm[i]]); + if (retval == EOF) goto failure; + if (cuddT(scan) != Cudd_ReadZero(dd)) { retval = fprintf(fp, "\"%p\" -> \"%p\";\n", (void *) ((mask & (ptrint) scan) / @@ -548,6 +556,8 @@ Cudd_DumpDot( (void *) ((mask & (ptrint) cuddT(scan)) / sizeof(DdNode))); if (retval == EOF) goto failure; + } + if (cuddE(scan) != Cudd_ReadZero(dd)) { if (Cudd_IsComplement(cuddE(scan))) { retval = fprintf(fp, "\"%p\" -> \"%p\" [style = dotted];\n", @@ -565,6 +575,7 @@ Cudd_DumpDot( } if (retval == EOF) goto failure; } + } scan = scan->next; } } @@ -578,11 +589,13 @@ Cudd_DumpDot( scan = nodelist[j]; while (scan != NULL) { if (st_is_member(visited,(char *) scan)) { + if (scan != Cudd_ReadZero(dd)) { retval = fprintf(fp,"\"%p\" [label = \"%g\"];\n", (void *) ((mask & (ptrint) scan) / sizeof(DdNode)), cuddV(scan)); if (retval == EOF) goto failure; } + } scan = scan->next; } } diff --git a/resources/3rdparty/cudd-2.5.0/src/cudd/cuddInt.h b/resources/3rdparty/cudd-2.5.0/src/cudd/cuddInt.h index 398b057f7..313a56586 100644 --- a/resources/3rdparty/cudd-2.5.0/src/cudd/cuddInt.h +++ b/resources/3rdparty/cudd-2.5.0/src/cudd/cuddInt.h @@ -1000,6 +1000,8 @@ dd->nextSample += 250000;} extern DdNode * cuddAddExistAbstractRecur (DdManager *manager, DdNode *f, DdNode *cube); extern DdNode * cuddAddUnivAbstractRecur (DdManager *manager, DdNode *f, DdNode *cube); extern DdNode * cuddAddOrAbstractRecur (DdManager *manager, DdNode *f, DdNode *cube); +extern DdNode * cuddAddMinAbstractRecur (DdManager *manager, DdNode *f, DdNode *cube); +extern DdNode * cuddAddMaxAbstractRecur (DdManager *manager, DdNode *f, DdNode *cube); extern DdNode * cuddAddApplyRecur (DdManager *dd, DdNode * (*)(DdManager *, DdNode **, DdNode **), DdNode *f, DdNode *g); extern DdNode * cuddAddMonadicApplyRecur (DdManager * dd, DdNode * (*op)(DdManager *, DdNode *), DdNode * f); extern DdNode * cuddAddScalarInverseRecur (DdManager *dd, DdNode *f, DdNode *epsilon); diff --git a/resources/3rdparty/cudd-2.5.0/src/cudd/cuddSat.c b/resources/3rdparty/cudd-2.5.0/src/cudd/cuddSat.c index 19dcafff0..869733f32 100644 --- a/resources/3rdparty/cudd-2.5.0/src/cudd/cuddSat.c +++ b/resources/3rdparty/cudd-2.5.0/src/cudd/cuddSat.c @@ -853,6 +853,71 @@ Cudd_EqualSupNorm( } /* end of Cudd_EqualSupNorm */ +/**Function******************************************************************** + + Synopsis [Compares two ADDs for equality within tolerance.] + + Description [Same as Cudd_EqualSupNorm but tests for max _relative_ difference + i.e. (f-g/f)<e instead of (f-g)<e ] + + SideEffects [None] + + SeeAlso [] + + ******************************************************************************/ +int +Cudd_EqualSupNormRel( + DdManager * dd /* manager */, + DdNode * f /* first ADD */, + DdNode * g /* second ADD */, + CUDD_VALUE_TYPE tolerance /* maximum allowed difference */, + int pr /* verbosity level */) +{ + DdNode *fv, *fvn, *gv, *gvn, *r; + unsigned int topf, topg; + + statLine(dd); + /* Check terminal cases. */ + if (f == g) return(1); + if (Cudd_IsConstant(f) && Cudd_IsConstant(g)) { + if (ddAbs((cuddV(f) - cuddV(g))/cuddV(f)) < tolerance) { + return(1); + } else { + if (pr>0) { + (void) fprintf(dd->out,"Offending nodes:\n"); + (void) fprintf(dd->out, + "f: address = %p\t value = %40.30f\n", + (void *) f, cuddV(f)); + (void) fprintf(dd->out, + "g: address = %p\t value = %40.30f\n", + (void *) g, cuddV(g)); + } + return(0); + } + } + + /* We only insert the result in the cache if the comparison is + ** successful. Therefore, if we hit we return 1. */ + r = cuddCacheLookup2(dd,(DD_CTFP)Cudd_EqualSupNormRel,f,g); + if (r != NULL) { + return(1); + } + + /* Compute the cofactors and solve the recursive subproblems. */ + topf = cuddI(dd,f->index); + topg = cuddI(dd,g->index); + + if (topf <= topg) {fv = cuddT(f); fvn = cuddE(f);} else {fv = fvn = f;} + if (topg <= topf) {gv = cuddT(g); gvn = cuddE(g);} else {gv = gvn = g;} + + if (!Cudd_EqualSupNormRel(dd,fv,gv,tolerance,pr)) return(0); + if (!Cudd_EqualSupNormRel(dd,fvn,gvn,tolerance,pr)) return(0); + + cuddCacheInsert2(dd,(DD_CTFP)Cudd_EqualSupNormRel,f,g,DD_ONE(dd)); + + return(1); + +} /* end of Cudd_EqualSupNormRel */ /**Function******************************************************************** diff --git a/resources/3rdparty/cudd-2.5.0/src/obj/cuddObj.cc b/resources/3rdparty/cudd-2.5.0/src/obj/cuddObj.cc index ffbaaf826..fcec76782 100644 --- a/resources/3rdparty/cudd-2.5.0/src/obj/cuddObj.cc +++ b/resources/3rdparty/cudd-2.5.0/src/obj/cuddObj.cc @@ -2405,6 +2405,23 @@ ADD::OrAbstract( } // ADD::OrAbstract +ADD +ADD::MinAbstract(const ADD& cube) const +{ + DdManager *mgr = checkSameManager(cube); + DdNode *result = Cudd_addMinAbstract(mgr, node, cube.node); + checkReturnValue(result); + return ADD(p, result); +} // ADD::MinAbstract + +ADD +ADD::MaxAbstract(const ADD& cube) const +{ + DdManager *mgr = checkSameManager(cube); + DdNode *result = Cudd_addMaxAbstract(mgr, node, cube.node); + checkReturnValue(result); + return ADD(p, result); +} // ADD::MaxAbstract ADD ADD::Plus( @@ -4730,6 +4747,16 @@ ADD::EqualSupNorm( } // ADD::EqualSupNorm +bool +ADD::EqualSupNormRel( + const ADD& g, + CUDD_VALUE_TYPE tolerance, + int pr) const +{ + DdManager *mgr = checkSameManager(g); + return Cudd_EqualSupNormRel(mgr, node, g.node, tolerance, pr) != 0; + +} // ADD::EqualSupNormRel BDD BDD::MakePrime( @@ -5250,14 +5277,14 @@ ABDD::FirstCube( int -NextCube( +ABDD::NextCube( DdGen * gen, int ** cube, - CUDD_VALUE_TYPE * value) + CUDD_VALUE_TYPE * value) { return Cudd_NextCube(gen, cube, value); -} // NextCube +} // ABDD::NextCube BDD diff --git a/resources/3rdparty/cudd-2.5.0/src/obj/cuddObj.hh b/resources/3rdparty/cudd-2.5.0/src/obj/cuddObj.hh index 0dce65989..1162a968c 100644 --- a/resources/3rdparty/cudd-2.5.0/src/obj/cuddObj.hh +++ b/resources/3rdparty/cudd-2.5.0/src/obj/cuddObj.hh @@ -192,6 +192,7 @@ public: const; int CountLeaves() const; DdGen * FirstCube(int ** cube, CUDD_VALUE_TYPE * value) const; + static int NextCube(DdGen * gen, int ** cube, CUDD_VALUE_TYPE * value); double Density(int nvars) const; }; // ABDD @@ -373,6 +374,8 @@ public: ADD ExistAbstract(const ADD& cube) const; ADD UnivAbstract(const ADD& cube) const; ADD OrAbstract(const ADD& cube) const; + ADD MinAbstract(const ADD& cube) const; + ADD MaxAbstract(const ADD& cube) const; ADD Plus(const ADD& g) const; ADD Times(const ADD& g) const; ADD Threshold(const ADD& g) const; @@ -424,8 +427,8 @@ public: ADD TimesPlus(const ADD& B, std::vector<ADD> z) const; ADD Triangle(const ADD& g, std::vector<ADD> z) const; ADD Eval(int * inputs) const; - bool EqualSupNorm(const ADD& g, CUDD_VALUE_TYPE tolerance, int pr) const; - + bool EqualSupNorm(const ADD& g, CUDD_VALUE_TYPE tolerance, int pr = 0) const; + bool EqualSupNormRel(const ADD& g, CUDD_VALUE_TYPE tolerance, int pr = 0) const; }; // ADD diff --git a/resources/3rdparty/ltl2dstar-0.5.1/src/parsers/nba-parser-lbtt.lex.cpp b/resources/3rdparty/ltl2dstar-0.5.1/src/parsers/nba-parser-lbtt.lex.cpp index f20e1eedb..55e55fbd2 100644 --- a/resources/3rdparty/ltl2dstar-0.5.1/src/parsers/nba-parser-lbtt.lex.cpp +++ b/resources/3rdparty/ltl2dstar-0.5.1/src/parsers/nba-parser-lbtt.lex.cpp @@ -31,7 +31,7 @@ /* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */ -#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L +#if (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) || defined _WIN32 #include <inttypes.h> typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; diff --git a/resources/3rdparty/ltl2dstar-0.5.1/src/parsers/nba-parser-promela.lex.cpp b/resources/3rdparty/ltl2dstar-0.5.1/src/parsers/nba-parser-promela.lex.cpp index 82812c039..9be4a847a 100644 --- a/resources/3rdparty/ltl2dstar-0.5.1/src/parsers/nba-parser-promela.lex.cpp +++ b/resources/3rdparty/ltl2dstar-0.5.1/src/parsers/nba-parser-promela.lex.cpp @@ -31,7 +31,7 @@ /* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */ -#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L +#if (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) || defined _WIN32 #include <inttypes.h> typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; diff --git a/src/adapters/ExplicitModelAdapter.h b/src/adapters/ExplicitModelAdapter.h index a2932b9c9..e120f4a59 100644 --- a/src/adapters/ExplicitModelAdapter.h +++ b/src/adapters/ExplicitModelAdapter.h @@ -15,12 +15,11 @@ #include <queue> #include <boost/functional/hash.hpp> #include <boost/container/flat_set.hpp> +#include <boost/algorithm/string.hpp> -#include "src/ir/Program.h" -#include "src/ir/RewardModel.h" -#include "src/ir/StateReward.h" -#include "src/ir/TransitionReward.h" -#include "src/utility/IRUtility.h" +#include "src/storage/prism/Program.h" +#include "src/storage/expressions/SimpleValuation.h" +#include "src/utility/PrismUtility.h" #include "src/models/AbstractModel.h" #include "src/models/Dtmc.h" #include "src/models/Ctmc.h" @@ -29,20 +28,22 @@ #include "src/models/AtomicPropositionsLabeling.h" #include "src/storage/SparseMatrix.h" #include "src/settings/Settings.h" +#include "src/exceptions/ExceptionMacros.h" #include "src/exceptions/WrongFormatException.h" -#include "log4cplus/logger.h" -#include "log4cplus/loggingmacros.h" -extern log4cplus::Logger logger; - -using namespace storm::utility::ir; - namespace storm { namespace adapters { + + using namespace storm::utility::prism; template<typename ValueType> class ExplicitModelAdapter { public: + typedef storm::expressions::SimpleValuation StateType; + typedef storm::expressions::SimpleValuationPointerHash StateHash; + typedef storm::expressions::SimpleValuationPointerCompare StateCompare; + typedef storm::expressions::SimpleValuationPointerLess StateLess; + // A structure holding information about the reachable state space. struct StateInformation { StateInformation() : reachableStates(), stateToIndexMap() { @@ -52,10 +53,19 @@ namespace storm { // A list of reachable states. std::vector<StateType*> reachableStates; + // A list of initial states. + std::vector<uint_fast64_t> initialStateIndices; + // A mapping from states to indices in the list of reachable states. std::unordered_map<StateType*, uint_fast64_t, StateHash, StateCompare> stateToIndexMap; }; + // A structure storing information about the used variables of the program. + struct VariableInformation { + // A mapping of (integer) variable to their lower/upper bounds. + std::map<std::string, std::pair<int_fast64_t, int_fast64_t>> variableToBoundsMap; + }; + // A structure holding the individual components of a model. struct ModelComponents { ModelComponents() : transitionMatrix(), stateLabeling(), stateRewards(), transitionRewardMatrix(), choiceLabeling() { @@ -78,6 +88,62 @@ namespace storm { std::vector<boost::container::flat_set<uint_fast64_t>> choiceLabeling; }; + static std::map<std::string, storm::expressions::Expression> parseConstantDefinitionString(storm::prism::Program const& program, std::string const& constantDefinitionString) { + std::map<std::string, storm::expressions::Expression> constantDefinitions; + std::set<std::string> definedConstants; + + if (!constantDefinitionString.empty()) { + // Parse the string that defines the undefined constants of the model and make sure that it contains exactly + // one value for each undefined constant of the model. + std::vector<std::string> definitions; + boost::split(definitions, constantDefinitionString, boost::is_any_of(",")); + for (auto& definition : definitions) { + boost::trim(definition); + + // Check whether the token could be a legal constant definition. + uint_fast64_t positionOfAssignmentOperator = definition.find('='); + if (positionOfAssignmentOperator == std::string::npos) { + throw storm::exceptions::InvalidArgumentException() << "Illegal constant definition string: syntax error."; + } + + // Now extract the variable name and the value from the string. + std::string constantName = definition.substr(0, positionOfAssignmentOperator); + boost::trim(constantName); + std::string value = definition.substr(positionOfAssignmentOperator + 1); + boost::trim(value); + + // Check whether the constant is a legal undefined constant of the program and if so, of what type it is. + if (program.hasConstant(constantName)) { + // Get the actual constant and check whether it's in fact undefined. + auto const& constant = program.getConstant(constantName); + LOG_THROW(!constant.isDefined(), storm::exceptions::InvalidArgumentException, "Illegally trying to define already defined constant '" << constantName <<"'."); + LOG_THROW(definedConstants.find(constantName) == definedConstants.end(), storm::exceptions::InvalidArgumentException, "Illegally trying to define constant '" << constantName <<"' twice."); + definedConstants.insert(constantName); + + if (constant.getType() == storm::expressions::ExpressionReturnType::Bool) { + if (value == "true") { + constantDefinitions[constantName] = storm::expressions::Expression::createTrue(); + } else if (value == "false") { + constantDefinitions[constantName] = storm::expressions::Expression::createFalse(); + } else { + throw storm::exceptions::InvalidArgumentException() << "Illegal value for boolean constant: " << value << "."; + } + } else if (constant.getType() == storm::expressions::ExpressionReturnType::Int) { + int_fast64_t integerValue = std::stoi(value); + constantDefinitions[constantName] = storm::expressions::Expression::createIntegerLiteral(integerValue); + } else if (constant.getType() == storm::expressions::ExpressionReturnType::Double) { + double doubleValue = std::stod(value); + constantDefinitions[constantName] = storm::expressions::Expression::createDoubleLiteral(doubleValue); + } + } else { + throw storm::exceptions::InvalidArgumentException() << "Illegal constant definition string: unknown undefined constant " << constantName << "."; + } + } + } + + return constantDefinitions; + } + /*! * Convert the program given at construction time to an abstract model. The type of the model is the one * specified in the program. The given reward model name selects the rewards that the model will contain. @@ -90,24 +156,33 @@ namespace storm { * rewards. * @return The explicit model that was given by the probabilistic program. */ - static std::unique_ptr<storm::models::AbstractModel<ValueType>> translateProgram(storm::ir::Program program, std::string const& constantDefinitionString = "", std::string const& rewardModelName = "") { + static std::unique_ptr<storm::models::AbstractModel<ValueType>> translateProgram(storm::prism::Program program, std::string const& constantDefinitionString = "", std::string const& rewardModelName = "") { // Start by defining the undefined constants in the model. - defineUndefinedConstants(program, constantDefinitionString); + // First, we need to parse the constant definition string. + std::map<std::string, storm::expressions::Expression> constantDefinitions = parseConstantDefinitionString(program, constantDefinitionString); + + storm::prism::Program preparedProgram = program.defineUndefinedConstants(constantDefinitions); + LOG_THROW(!preparedProgram.hasUndefinedConstants(), storm::exceptions::InvalidArgumentException, "Program still contains undefined constants."); - ModelComponents modelComponents = buildModelComponents(program, rewardModelName); + // Now that we have defined all the constants in the program, we need to substitute their appearances in + // all expressions in the program so we can then evaluate them without having to store the values of the + // constants in the state (i.e., valuation). + preparedProgram = preparedProgram.substituteConstants(); + + ModelComponents modelComponents = buildModelComponents(preparedProgram, rewardModelName); std::unique_ptr<storm::models::AbstractModel<ValueType>> result; switch (program.getModelType()) { - case storm::ir::Program::DTMC: + case storm::prism::Program::ModelType::DTMC: result = std::unique_ptr<storm::models::AbstractModel<ValueType>>(new storm::models::Dtmc<ValueType>(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling), rewardModelName != "" ? std::move(modelComponents.stateRewards) : boost::optional<std::vector<ValueType>>(), rewardModelName != "" ? std::move(modelComponents.transitionRewardMatrix) : boost::optional<storm::storage::SparseMatrix<ValueType>>(), std::move(modelComponents.choiceLabeling))); break; - case storm::ir::Program::CTMC: + case storm::prism::Program::ModelType::CTMC: result = std::unique_ptr<storm::models::AbstractModel<ValueType>>(new storm::models::Ctmc<ValueType>(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling), rewardModelName != "" ? std::move(modelComponents.stateRewards) : boost::optional<std::vector<ValueType>>(), rewardModelName != "" ? std::move(modelComponents.transitionRewardMatrix) : boost::optional<storm::storage::SparseMatrix<ValueType>>(), std::move(modelComponents.choiceLabeling))); break; - case storm::ir::Program::MDP: + case storm::prism::Program::ModelType::MDP: result = std::unique_ptr<storm::models::AbstractModel<ValueType>>(new storm::models::Mdp<ValueType>(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling), rewardModelName != "" ? std::move(modelComponents.stateRewards) : boost::optional<std::vector<ValueType>>(), rewardModelName != "" ? std::move(modelComponents.transitionRewardMatrix) : boost::optional<storm::storage::SparseMatrix<ValueType>>(), std::move(modelComponents.choiceLabeling))); break; - case storm::ir::Program::CTMDP: + case storm::prism::Program::ModelType::CTMDP: result = std::unique_ptr<storm::models::AbstractModel<ValueType>>(new storm::models::Ctmdp<ValueType>(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling), rewardModelName != "" ? std::move(modelComponents.stateRewards) : boost::optional<std::vector<ValueType>>(), rewardModelName != "" ? std::move(modelComponents.transitionRewardMatrix) : boost::optional<storm::storage::SparseMatrix<ValueType>>(), std::move(modelComponents.choiceLabeling))); break; default: @@ -116,36 +191,19 @@ namespace storm { break; } - // Undefine the constants so that the program can be used again somewhere else. - undefineUndefinedConstants(program); - return result; } private: - /*! - * Transforms a state into a somewhat readable string. - * - * @param state The state to transform into a string. - * @return A string representation of the state. - */ - static std::string toString(StateType const* state) { - std::stringstream ss; - for (unsigned int i = 0; i < state->first.size(); i++) ss << state->first[i] << "\t"; - for (unsigned int i = 0; i < state->second.size(); i++) ss << state->second[i] << "\t"; - return ss.str(); - } - /*! * Applies an update to the given state and returns the resulting new state object. This methods does not * modify the given state but returns a new one. * - * @param variableInformation A structure with information about the variables in the program. * @params state The state to which to apply the update. * @params update The update to apply. * @return The resulting state. */ - static StateType* applyUpdate(VariableInformation const& variableInformation, StateType const* state, storm::ir::Update const& update) { + static StateType* applyUpdate(VariableInformation const& variableInformation, StateType const* state, storm::prism::Update const& update) { return applyUpdate(variableInformation, state, state, update); } @@ -154,26 +212,28 @@ namespace storm { * over the variable values of the given base state. This methods does not modify the given state but * returns a new one. * - * @param variableInformation A structure with information about the variables in the program. * @param state The state to which to apply the update. * @param baseState The state used for evaluating the update. * @param update The update to apply. * @return The resulting state. */ - static StateType* applyUpdate(VariableInformation const& variableInformation, StateType const* state, StateType const* baseState, storm::ir::Update const& update) { + static StateType* applyUpdate(VariableInformation const& variableInformation, StateType const* state, StateType const* baseState, storm::prism::Update const& update) { StateType* newState = new StateType(*state); - for (auto variableAssignmentPair : update.getBooleanAssignments()) { - setValue(newState, variableInformation.booleanVariableToIndexMap.at(variableAssignmentPair.first), variableAssignmentPair.second.getExpression()->getValueAsBool(baseState)); - } - for (auto variableAssignmentPair : update.getIntegerAssignments()) { - int_fast64_t newVariableValue = variableAssignmentPair.second.getExpression()->getValueAsInt(baseState); - bool isLegalValueForVariable = newVariableValue >= variableInformation.lowerBounds.at(variableInformation.integerVariableToIndexMap.at(variableAssignmentPair.first)) && newVariableValue <= variableInformation.upperBounds.at(variableInformation.integerVariableToIndexMap.at(variableAssignmentPair.first)); - - if (!isLegalValueForVariable) { - throw storm::exceptions::InvalidStateException() << "Invalid value '" << newVariableValue << "' for variable \"" << variableInformation.integerVariables.at(variableInformation.integerVariableToIndexMap.at(variableAssignmentPair.first)).getName() << "\". Please strengthen the guards if necessary or enlarge the domains of the variables."; + + // This variable needs to be declared prior to the switch, because of C++ rules. + int_fast64_t newValue = 0; + for (auto const& assignment : update.getAssignments()) { + switch (assignment.getExpression().getReturnType()) { + case storm::expressions::ExpressionReturnType::Bool: newState->setBooleanValue(assignment.getVariableName(), assignment.getExpression().evaluateAsBool(baseState)); break; + case storm::expressions::ExpressionReturnType::Int: + { + newValue = assignment.getExpression().evaluateAsInt(baseState); + auto const& boundsPair = variableInformation.variableToBoundsMap.find(assignment.getVariableName()); + LOG_THROW(boundsPair->second.first <= newValue && newValue <= boundsPair->second.second, storm::exceptions::InvalidArgumentException, "Invalid value " << newValue << " for variable '" << assignment.getVariableName() << "'."); + newState->setIntegerValue(assignment.getVariableName(), newValue); break; + } + default: LOG_ASSERT(false, "Invalid type of assignment."); break; } - - setValue(newState, variableInformation.integerVariableToIndexMap.at(variableAssignmentPair.first), newVariableValue); } return newState; } @@ -220,25 +280,32 @@ namespace storm { * @param action The action label to select. * @return A list of lists of active commands or nothing. */ - static boost::optional<std::vector<std::list<storm::ir::Command>>> getActiveCommandsByAction(storm::ir::Program const& program, StateType const* state, std::string const& action) { - boost::optional<std::vector<std::list<storm::ir::Command>>> result((std::vector<std::list<storm::ir::Command>>())); + static boost::optional<std::vector<std::list<storm::prism::Command>>> getActiveCommandsByAction(storm::prism::Program const& program, StateType const* state, std::string const& action) { + boost::optional<std::vector<std::list<storm::prism::Command>>> result((std::vector<std::list<storm::prism::Command>>())); // Iterate over all modules. for (uint_fast64_t i = 0; i < program.getNumberOfModules(); ++i) { - storm::ir::Module const& module = program.getModule(i); + storm::prism::Module const& module = program.getModule(i); // If the module has no command labeled with the given action, we can skip this module. if (!module.hasAction(action)) { continue; } - std::set<uint_fast64_t> const& commandIndices = module.getCommandsByAction(action); - std::list<storm::ir::Command> commands; + std::set<uint_fast64_t> const& commandIndices = module.getCommandIndicesByAction(action); + + // If the module contains the action, but there is no command in the module that is labeled with + // this action, we don't have any feasible command combinations. + if (commandIndices.empty()) { + return boost::optional<std::vector<std::list<storm::prism::Command>>>(); + } + + std::list<storm::prism::Command> commands; // Look up commands by their indices and add them if the guard evaluates to true in the given state. for (uint_fast64_t commandIndex : commandIndices) { - storm::ir::Command const& command = module.getCommand(commandIndex); - if (command.getGuard()->getValueAsBool(state)) { + storm::prism::Command const& command = module.getCommand(commandIndex); + if (command.getGuardExpression().evaluateAsBool(state)) { commands.push_back(command); } } @@ -246,7 +313,7 @@ namespace storm { // If there was no enabled command although the module has some command with the required action label, // we must not return anything. if (commands.size() == 0) { - return boost::optional<std::vector<std::list<storm::ir::Command>>>(); + return boost::optional<std::vector<std::list<storm::prism::Command>>>(); } result.get().push_back(std::move(commands)); @@ -254,23 +321,26 @@ namespace storm { return result; } - static std::list<Choice<ValueType>> getUnlabeledTransitions(storm::ir::Program const& program, StateInformation& stateInformation, VariableInformation const& variableInformation, uint_fast64_t stateIndex, std::queue<uint_fast64_t>& stateQueue) { + static std::list<Choice<ValueType>> getUnlabeledTransitions(storm::prism::Program const& program, StateInformation& stateInformation, VariableInformation const& variableInformation, uint_fast64_t stateIndex, std::queue<uint_fast64_t>& stateQueue) { std::list<Choice<ValueType>> result; StateType const* currentState = stateInformation.reachableStates[stateIndex]; // Iterate over all modules. for (uint_fast64_t i = 0; i < program.getNumberOfModules(); ++i) { - storm::ir::Module const& module = program.getModule(i); + storm::prism::Module const& module = program.getModule(i); // Iterate over all commands. for (uint_fast64_t j = 0; j < module.getNumberOfCommands(); ++j) { - storm::ir::Command const& command = module.getCommand(j); + storm::prism::Command const& command = module.getCommand(j); // Only consider unlabeled commands. if (command.getActionName() != "") continue; + // Skip the command, if it is not enabled. - if (!command.getGuard()->getValueAsBool(currentState)) continue; + if (!command.getGuardExpression().evaluateAsBool(currentState)) { + continue; + } result.push_back(Choice<ValueType>("")); Choice<ValueType>& choice = result.back(); @@ -279,7 +349,7 @@ namespace storm { double probabilitySum = 0; // Iterate over all updates of the current command. for (uint_fast64_t k = 0; k < command.getNumberOfUpdates(); ++k) { - storm::ir::Update const& update = command.getUpdate(k); + storm::prism::Update const& update = command.getUpdate(k); // Obtain target state index. std::pair<bool, uint_fast64_t> flagTargetStateIndexPair = getOrAddStateIndex(applyUpdate(variableInformation, currentState, update), stateInformation); @@ -290,7 +360,7 @@ namespace storm { } // Update the choice by adding the probability/target state to it. - double probabilityToAdd = update.getLikelihoodExpression()->getValueAsDouble(currentState); + double probabilityToAdd = update.getLikelihoodExpression().evaluateAsDouble(currentState); probabilitySum += probabilityToAdd; boost::container::flat_set<uint_fast64_t> labels; labels.insert(update.getGlobalIndex()); @@ -298,27 +368,24 @@ namespace storm { } // Check that the resulting distribution is in fact a distribution. - if (std::abs(1 - probabilitySum) > storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()) { - LOG4CPLUS_ERROR(logger, "Sum of update probabilities should be one for command:\n\t" << command.toString()); - throw storm::exceptions::WrongFormatException() << "Sum of update probabilities should be one for command:\n\t" << command.toString(); - } + LOG_THROW(std::abs(1 - probabilitySum) < storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble(), storm::exceptions::WrongFormatException, "Probabilities do not sum to one for command '" << command << "'."); } } return result; } - static std::list<Choice<ValueType>> getLabeledTransitions(storm::ir::Program const& program, StateInformation& stateInformation, VariableInformation const& variableInformation, uint_fast64_t stateIndex, std::queue<uint_fast64_t>& stateQueue) { + static std::list<Choice<ValueType>> getLabeledTransitions(storm::prism::Program const& program, StateInformation& stateInformation, VariableInformation const& variableInformation, uint_fast64_t stateIndex, std::queue<uint_fast64_t>& stateQueue) { std::list<Choice<ValueType>> result; for (std::string const& action : program.getActions()) { StateType const* currentState = stateInformation.reachableStates[stateIndex]; - boost::optional<std::vector<std::list<storm::ir::Command>>> optionalActiveCommandLists = getActiveCommandsByAction(program, currentState, action); + boost::optional<std::vector<std::list<storm::prism::Command>>> optionalActiveCommandLists = getActiveCommandsByAction(program, currentState, action); // Only process this action label, if there is at least one feasible solution. if (optionalActiveCommandLists) { - std::vector<std::list<storm::ir::Command>> const& activeCommandList = optionalActiveCommandLists.get(); - std::vector<std::list<storm::ir::Command>::const_iterator> iteratorList(activeCommandList.size()); + std::vector<std::list<storm::prism::Command>> const& activeCommandList = optionalActiveCommandLists.get(); + std::vector<std::list<storm::prism::Command>::const_iterator> iteratorList(activeCommandList.size()); // Initialize the list of iterators. for (size_t i = 0; i < activeCommandList.size(); ++i) { @@ -335,17 +402,17 @@ namespace storm { // FIXME: This does not check whether a global variable is written multiple times. While the // behaviour for this is undefined anyway, a warning should be issued in that case. for (uint_fast64_t i = 0; i < iteratorList.size(); ++i) { - storm::ir::Command const& command = *iteratorList[i]; + storm::prism::Command const& command = *iteratorList[i]; for (uint_fast64_t j = 0; j < command.getNumberOfUpdates(); ++j) { - storm::ir::Update const& update = command.getUpdate(j); + storm::prism::Update const& update = command.getUpdate(j); for (auto const& stateProbabilityPair : *currentTargetStates) { StateType* newTargetState = applyUpdate(variableInformation, stateProbabilityPair.first, currentState, update); storm::storage::LabeledValues<double> newProbability; - double updateProbability = update.getLikelihoodExpression()->getValueAsDouble(currentState); + double updateProbability = update.getLikelihoodExpression().evaluateAsDouble(currentState); for (auto const& valueLabelSetPair : stateProbabilityPair.second) { // Copy the label set, so we can modify it. boost::container::flat_set<uint_fast64_t> newLabelSet = valueLabelSetPair.second; @@ -452,13 +519,29 @@ namespace storm { * @return A tuple containing a vector with all rows at which the nondeterministic choices of each state begin * and a vector containing the labels associated with each choice. */ - static std::vector<boost::container::flat_set<uint_fast64_t>> buildMatrices(storm::ir::Program const& program, VariableInformation const& variableInformation, std::vector<storm::ir::TransitionReward> const& transitionRewards, StateInformation& stateInformation, bool deterministicModel, storm::storage::SparseMatrixBuilder<ValueType>& transitionMatrixBuilder, storm::storage::SparseMatrixBuilder<ValueType>& transitionRewardMatrixBuilder) { + static std::vector<boost::container::flat_set<uint_fast64_t>> buildMatrices(storm::prism::Program const& program, VariableInformation const& variableInformation, std::vector<storm::prism::TransitionReward> const& transitionRewards, StateInformation& stateInformation, bool deterministicModel, storm::storage::SparseMatrixBuilder<ValueType>& transitionMatrixBuilder, storm::storage::SparseMatrixBuilder<ValueType>& transitionRewardMatrixBuilder) { std::vector<boost::container::flat_set<uint_fast64_t>> choiceLabels; // Initialize a queue and insert the initial state. std::queue<uint_fast64_t> stateQueue; - StateType* initialState = getInitialState(program, variableInformation); - getOrAddStateIndex(initialState, stateInformation); + StateType* initialState = new StateType; + for (auto const& booleanVariable : program.getGlobalBooleanVariables()) { + initialState->addBooleanIdentifier(booleanVariable.getName(), booleanVariable.getInitialValueExpression().evaluateAsBool()); + } + for (auto const& integerVariable : program.getGlobalIntegerVariables()) { + initialState->addIntegerIdentifier(integerVariable.getName(), integerVariable.getInitialValueExpression().evaluateAsInt()); + } + for (auto const& module : program.getModules()) { + for (auto const& booleanVariable : module.getBooleanVariables()) { + initialState->addBooleanIdentifier(booleanVariable.getName(), booleanVariable.getInitialValueExpression().evaluateAsBool()); + } + for (auto const& integerVariable : module.getIntegerVariables()) { + initialState->addIntegerIdentifier(integerVariable.getName(), integerVariable.getInitialValueExpression().evaluateAsInt()); + } + } + + std::pair<bool, uint_fast64_t> addIndexPair = getOrAddStateIndex(initialState, stateInformation); + stateInformation.initialStateIndices.push_back(addIndexPair.second); stateQueue.push(stateInformation.stateToIndexMap[initialState]); // Now explore the current state until there is no more reachable state. @@ -476,6 +559,8 @@ namespace storm { // requested and issue an error otherwise. if (totalNumberOfChoices == 0) { if (storm::settings::Settings::getInstance()->isSet("fixDeadlocks")) { + // Insert empty choice labeling for added self-loop transitions. + choiceLabels.push_back(boost::container::flat_set<uint_fast64_t>()); transitionMatrixBuilder.addNextValue(currentRow, currentState, storm::utility::constantOne<ValueType>()); ++currentRow; } else { @@ -498,8 +583,8 @@ namespace storm { // Now add all rewards that match this choice. for (auto const& transitionReward : transitionRewards) { - if (transitionReward.getActionName() == "" && transitionReward.getStatePredicate()->getValueAsBool(stateInformation.reachableStates.at(currentState))) { - stateToRewardMap[stateProbabilityPair.first] += ValueType(transitionReward.getRewardValue()->getValueAsDouble(stateInformation.reachableStates.at(currentState))); + if (transitionReward.getActionName() == "" && transitionReward.getStatePredicateExpression().evaluateAsBool(stateInformation.reachableStates.at(currentState))) { + stateToRewardMap[stateProbabilityPair.first] += ValueType(transitionReward.getRewardValueExpression().evaluateAsDouble(stateInformation.reachableStates.at(currentState))); } } } @@ -511,8 +596,8 @@ namespace storm { // Now add all rewards that match this choice. for (auto const& transitionReward : transitionRewards) { - if (transitionReward.getActionName() == choice.getActionLabel() && transitionReward.getStatePredicate()->getValueAsBool(stateInformation.reachableStates.at(currentState))) { - stateToRewardMap[stateProbabilityPair.first] += ValueType(transitionReward.getRewardValue()->getValueAsDouble(stateInformation.reachableStates.at(currentState))); + if (transitionReward.getActionName() == choice.getActionLabel() && transitionReward.getStatePredicateExpression().evaluateAsBool(stateInformation.reachableStates.at(currentState))) { + stateToRewardMap[stateProbabilityPair.first] += ValueType(transitionReward.getRewardValueExpression().evaluateAsDouble(stateInformation.reachableStates.at(currentState))); } } } @@ -549,8 +634,8 @@ namespace storm { // Now add all rewards that match this choice. for (auto const& transitionReward : transitionRewards) { - if (transitionReward.getActionName() == "" && transitionReward.getStatePredicate()->getValueAsBool(stateInformation.reachableStates.at(currentState))) { - stateToRewardMap[stateProbabilityPair.first] += ValueType(transitionReward.getRewardValue()->getValueAsDouble(stateInformation.reachableStates.at(currentState))); + if (transitionReward.getActionName() == "" && transitionReward.getStatePredicateExpression().evaluateAsBool(stateInformation.reachableStates.at(currentState))) { + stateToRewardMap[stateProbabilityPair.first] += ValueType(transitionReward.getRewardValueExpression().evaluateAsDouble(stateInformation.reachableStates.at(currentState))); } } @@ -576,8 +661,8 @@ namespace storm { // Now add all rewards that match this choice. for (auto const& transitionReward : transitionRewards) { - if (transitionReward.getActionName() == choice.getActionLabel() && transitionReward.getStatePredicate()->getValueAsBool(stateInformation.reachableStates.at(currentState))) { - stateToRewardMap[stateProbabilityPair.first] += ValueType(transitionReward.getRewardValue()->getValueAsDouble(stateInformation.reachableStates.at(currentState))); + if (transitionReward.getActionName() == choice.getActionLabel() && transitionReward.getStatePredicateExpression().evaluateAsBool(stateInformation.reachableStates.at(currentState))) { + stateToRewardMap[stateProbabilityPair.first] += ValueType(transitionReward.getRewardValueExpression().evaluateAsDouble(stateInformation.reachableStates.at(currentState))); } } @@ -609,20 +694,28 @@ namespace storm { * is considered. * @return A structure containing the components of the resulting model. */ - static ModelComponents buildModelComponents(storm::ir::Program const& program, std::string const& rewardModelName) { + static ModelComponents buildModelComponents(storm::prism::Program const& program, std::string const& rewardModelName) { ModelComponents modelComponents; - VariableInformation variableInformation = createVariableInformation(program); + VariableInformation variableInformation; + for (auto const& integerVariable : program.getGlobalIntegerVariables()) { + variableInformation.variableToBoundsMap[integerVariable.getName()] = std::make_pair(integerVariable.getLowerBoundExpression().evaluateAsInt(), integerVariable.getUpperBoundExpression().evaluateAsInt()); + } + for (auto const& module : program.getModules()) { + for (auto const& integerVariable : module.getIntegerVariables()) { + variableInformation.variableToBoundsMap[integerVariable.getName()] = std::make_pair(integerVariable.getLowerBoundExpression().evaluateAsInt(), integerVariable.getUpperBoundExpression().evaluateAsInt()); + } + } // Create the structure for storing the reachable state space. StateInformation stateInformation; // Get the selected reward model or create an empty one if none is selected. - storm::ir::RewardModel const& rewardModel = rewardModelName != "" ? program.getRewardModel(rewardModelName) : storm::ir::RewardModel(); + storm::prism::RewardModel const& rewardModel = rewardModelName != "" ? program.getRewardModel(rewardModelName) : storm::prism::RewardModel(); // Determine whether we have to combine different choices to one or whether this model can have more than // one choice per state. - bool deterministicModel = program.getModelType() == storm::ir::Program::DTMC || program.getModelType() == storm::ir::Program::CTMC; + bool deterministicModel = program.getModelType() == storm::prism::Program::ModelType::DTMC || program.getModelType() == storm::prism::Program::ModelType::CTMC; // Build the transition and reward matrices. storm::storage::SparseMatrixBuilder<ValueType> transitionMatrixBuilder(0, 0, 0, !deterministicModel, 0); @@ -655,30 +748,29 @@ namespace storm { * @param stateInformation Information about the state space of the program. * @return The state labeling of the given program. */ - static storm::models::AtomicPropositionsLabeling buildStateLabeling(storm::ir::Program const& program, VariableInformation const& variableInformation, StateInformation const& stateInformation) { - std::map<std::string, std::unique_ptr<storm::ir::expressions::BaseExpression>> const& labels = program.getLabels(); + static storm::models::AtomicPropositionsLabeling buildStateLabeling(storm::prism::Program const& program, VariableInformation const& variableInformation, StateInformation const& stateInformation) { + std::vector<storm::prism::Label> const& labels = program.getLabels(); storm::models::AtomicPropositionsLabeling result(stateInformation.reachableStates.size(), labels.size() + 1); // Initialize labeling. - for (auto const& labelExpressionPair : labels) { - result.addAtomicProposition(labelExpressionPair.first); + for (auto const& label : labels) { + result.addAtomicProposition(label.getName()); } for (uint_fast64_t index = 0; index < stateInformation.reachableStates.size(); index++) { - for (auto const& labelExpressionPair: labels) { + for (auto const& label : labels) { // Add label to state, if the corresponding expression is true. - if (labelExpressionPair.second->getValueAsBool(stateInformation.reachableStates[index])) { - result.addAtomicPropositionToState(labelExpressionPair.first, index); + if (label.getStatePredicateExpression().evaluateAsBool(stateInformation.reachableStates[index])) { + result.addAtomicPropositionToState(label.getName(), index); } } } // Also label the initial state with the special label "init". result.addAtomicProposition("init"); - StateType* initialState = getInitialState(program, variableInformation); - uint_fast64_t initialIndex = stateInformation.stateToIndexMap.at(initialState); - result.addAtomicPropositionToState("init", initialIndex); - delete initialState; + for (auto const& index : stateInformation.initialStateIndices) { + result.addAtomicPropositionToState("init", index); + } return result; } @@ -690,14 +782,14 @@ namespace storm { * @param stateInformation Information about the state space. * @return A vector containing the state rewards for the state space. */ - static std::vector<ValueType> buildStateRewards(std::vector<storm::ir::StateReward> const& rewards, StateInformation const& stateInformation) { + static std::vector<ValueType> buildStateRewards(std::vector<storm::prism::StateReward> const& rewards, StateInformation const& stateInformation) { std::vector<ValueType> result(stateInformation.reachableStates.size()); for (uint_fast64_t index = 0; index < stateInformation.reachableStates.size(); index++) { result[index] = ValueType(0); for (auto const& reward : rewards) { // Add this reward to the state if the state is included in the state reward. - if (reward.getStatePredicate()->getValueAsBool(stateInformation.reachableStates[index])) { - result[index] += ValueType(reward.getRewardValue()->getValueAsDouble(stateInformation.reachableStates[index])); + if (reward.getStatePredicateExpression().evaluateAsBool(stateInformation.reachableStates[index])) { + result[index] += ValueType(reward.getRewardValueExpression().evaluateAsDouble(stateInformation.reachableStates[index])); } } } diff --git a/src/adapters/GmmxxAdapter.h b/src/adapters/GmmxxAdapter.h index ff35800b2..b1d2206a5 100644 --- a/src/adapters/GmmxxAdapter.h +++ b/src/adapters/GmmxxAdapter.h @@ -51,8 +51,8 @@ public: columns.reserve(matrix.getEntryCount()); for (auto const& entry : matrix) { - columns.emplace_back(entry.first); - values.emplace_back(entry.second); + columns.emplace_back(entry.getColumn()); + values.emplace_back(entry.getValue()); } std::swap(result->ir, columns); diff --git a/src/adapters/SymbolicModelAdapter.h b/src/adapters/SymbolicModelAdapter.h index be0729729..29ab6c613 100644 --- a/src/adapters/SymbolicModelAdapter.h +++ b/src/adapters/SymbolicModelAdapter.h @@ -157,7 +157,6 @@ private: for (uint_fast64_t j = 0; j < module.getNumberOfBooleanVariables(); ++j) { storm::ir::BooleanVariable const& booleanVariable = module.getBooleanVariable(j); - bool initialValue = booleanVariable.getInitialValue()->getValueAsBool(nullptr); *initialStates *= *cuddUtility->getConstantEncoding(1, variableToRowDecisionDiagramVariableMap[booleanVariable.getName()]); } for (uint_fast64_t j = 0; j < module.getNumberOfIntegerVariables(); ++j) { @@ -187,7 +186,6 @@ private: } bool changed; - int iter = 0; do { changed = false; *newReachableStates = *reachableStates * *systemAdd01; diff --git a/src/counterexamples/MILPMinimalLabelSetGenerator.h b/src/counterexamples/MILPMinimalLabelSetGenerator.h index 3ec8256ed..ad581e616 100644 --- a/src/counterexamples/MILPMinimalLabelSetGenerator.h +++ b/src/counterexamples/MILPMinimalLabelSetGenerator.h @@ -11,7 +11,7 @@ #include <chrono> #include "src/models/Mdp.h" -#include "src/ir/Program.h" +#include "src/storage/prism/Program.h" #include "src/exceptions/NotImplementedException.h" #include "src/exceptions/InvalidArgumentException.h" #include "src/exceptions/InvalidStateException.h" @@ -65,13 +65,13 @@ namespace storm { * A helper struct capturing information about the variables of the MILP formulation. */ struct VariableInformation { - std::unordered_map<uint_fast64_t, uint_fast64_t> labelToVariableIndexMap; - std::unordered_map<uint_fast64_t, std::list<uint_fast64_t>> stateToChoiceVariablesIndexMap; - std::unordered_map<uint_fast64_t, uint_fast64_t> initialStateToChoiceVariableIndexMap; - std::unordered_map<uint_fast64_t, uint_fast64_t> stateToProbabilityVariableIndexMap; - uint_fast64_t virtualInitialStateVariableIndex; - std::unordered_map<uint_fast64_t, uint_fast64_t> problematicStateToVariableIndexMap; - std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, uint_fast64_t, PairHash> problematicTransitionToVariableIndexMap; + std::unordered_map<uint_fast64_t, std::string> labelToVariableMap; + std::unordered_map<uint_fast64_t, std::list<std::string>> stateToChoiceVariablesMap; + std::unordered_map<uint_fast64_t, std::string> initialStateToChoiceVariableMap; + std::unordered_map<uint_fast64_t, std::string> stateToProbabilityVariableMap; + std::string virtualInitialStateVariable; + std::unordered_map<uint_fast64_t, std::string> problematicStateToVariableMap; + std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, std::string, PairHash> problematicTransitionToVariableMap; uint_fast64_t numberOfVariables; VariableInformation() : numberOfVariables(0) {} @@ -92,8 +92,7 @@ namespace storm { StateInformation result; result.relevantStates = storm::utility::graph::performProbGreater0E(labeledMdp.getTransitionMatrix(), labeledMdp.getNondeterministicChoiceIndices(), labeledMdp.getBackwardTransitions(), phiStates, psiStates); result.relevantStates &= ~psiStates; - result.problematicStates = storm::utility::graph::performProbGreater0E(labeledMdp.getTransitionMatrix(), labeledMdp.getNondeterministicChoiceIndices(), labeledMdp.getBackwardTransitions(), phiStates, psiStates); - result.problematicStates.complement(); + result.problematicStates = storm::utility::graph::performProb0E(labeledMdp.getTransitionMatrix(), labeledMdp.getNondeterministicChoiceIndices(), labeledMdp.getBackwardTransitions(), phiStates, psiStates); result.problematicStates &= result.relevantStates; LOG4CPLUS_DEBUG(logger, "Found " << phiStates.getNumberOfSetBits() << " filter states."); LOG4CPLUS_DEBUG(logger, "Found " << psiStates.getNumberOfSetBits() << " target states."); @@ -131,7 +130,7 @@ namespace storm { bool allSuccessorsProblematic = true; for (auto const& successorEntry : transitionMatrix.getRow(row)) { // If there is a relevant successor, we need to add the labels of the current choice. - if (stateInformation.relevantStates.get(successorEntry.first) || psiStates.get(successorEntry.first)) { + if (stateInformation.relevantStates.get(successorEntry.getColumn()) || psiStates.get(successorEntry.getColumn())) { for (auto const& label : choiceLabeling[row]) { result.allRelevantLabels.insert(label); } @@ -140,7 +139,7 @@ namespace storm { result.relevantChoicesForRelevantStates[state].push_back(row); } } - if (!stateInformation.problematicStates.get(successorEntry.first)) { + if (!stateInformation.problematicStates.get(successorEntry.getColumn())) { allSuccessorsProblematic = false; } } @@ -168,15 +167,15 @@ namespace storm { * @param relevantLabels The set of relevant labels of the model. * @return A mapping from labels to variable indices. */ - static std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> createLabelVariables(storm::solver::LpSolver& solver, boost::container::flat_set<uint_fast64_t> const& relevantLabels) { - int error = 0; + static std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> createLabelVariables(storm::solver::LpSolver& solver, boost::container::flat_set<uint_fast64_t> const& relevantLabels) { std::stringstream variableNameBuffer; - std::unordered_map<uint_fast64_t, uint_fast64_t> resultingMap; + std::unordered_map<uint_fast64_t, std::string> resultingMap; for (auto const& label : relevantLabels) { variableNameBuffer.str(""); variableNameBuffer.clear(); variableNameBuffer << "label" << label; - resultingMap[label] = solver.createBinaryVariable(variableNameBuffer.str(), 1); + resultingMap[label] = variableNameBuffer.str(); + solver.addBinaryVariable(resultingMap[label], 1); } return std::make_pair(resultingMap, relevantLabels.size()); } @@ -189,20 +188,20 @@ namespace storm { * @param choiceInformation The information about the choices of the model. * @return A mapping from states to a list of choice variable indices. */ - static std::pair<std::unordered_map<uint_fast64_t, std::list<uint_fast64_t>>, uint_fast64_t> createSchedulerVariables(storm::solver::LpSolver& solver, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation) { - int error = 0; + static std::pair<std::unordered_map<uint_fast64_t, std::list<std::string>>, uint_fast64_t> createSchedulerVariables(storm::solver::LpSolver& solver, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation) { std::stringstream variableNameBuffer; uint_fast64_t numberOfVariablesCreated = 0; - std::unordered_map<uint_fast64_t, std::list<uint_fast64_t>> resultingMap; + std::unordered_map<uint_fast64_t, std::list<std::string>> resultingMap; for (auto state : stateInformation.relevantStates) { - resultingMap.emplace(state, std::list<uint_fast64_t>()); + resultingMap.emplace(state, std::list<std::string>()); std::list<uint_fast64_t> const& relevantChoicesForState = choiceInformation.relevantChoicesForRelevantStates.at(state); for (uint_fast64_t row : relevantChoicesForState) { variableNameBuffer.str(""); variableNameBuffer.clear(); variableNameBuffer << "choice" << row << "in" << state; - resultingMap[state].push_back(solver.createBinaryVariable(variableNameBuffer.str(), 0)); + resultingMap[state].push_back(variableNameBuffer.str()); + solver.addBinaryVariable(resultingMap[state].back()); ++numberOfVariablesCreated; } } @@ -218,11 +217,10 @@ namespace storm { * @param stateInformation The information about the states of the model. * @return A mapping from initial states to choice variable indices. */ - static std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> createInitialChoiceVariables(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, StateInformation const& stateInformation) { - int error = 0; + static std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> createInitialChoiceVariables(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, StateInformation const& stateInformation) { std::stringstream variableNameBuffer; uint_fast64_t numberOfVariablesCreated = 0; - std::unordered_map<uint_fast64_t, uint_fast64_t> resultingMap; + std::unordered_map<uint_fast64_t, std::string> resultingMap; for (auto initialState : labeledMdp.getLabeledStates("init")) { // Only consider this initial state if it is relevant. @@ -230,7 +228,8 @@ namespace storm { variableNameBuffer.str(""); variableNameBuffer.clear(); variableNameBuffer << "init" << initialState; - resultingMap[initialState] = solver.createBinaryVariable(variableNameBuffer.str(), 0); + resultingMap[initialState] = variableNameBuffer.str(); + solver.addBinaryVariable(resultingMap[initialState]); ++numberOfVariablesCreated; } } @@ -244,36 +243,36 @@ namespace storm { * @param stateInformation The information about the states in the model. * @return A mapping from states to the index of the corresponding probability variables. */ - static std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> createProbabilityVariables(storm::solver::LpSolver& solver, StateInformation const& stateInformation) { - int error = 0; + static std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> createProbabilityVariables(storm::solver::LpSolver& solver, StateInformation const& stateInformation) { std::stringstream variableNameBuffer; uint_fast64_t numberOfVariablesCreated = 0; - std::unordered_map<uint_fast64_t, uint_fast64_t> resultingMap; + std::unordered_map<uint_fast64_t, std::string> resultingMap; for (auto state : stateInformation.relevantStates) { variableNameBuffer.str(""); variableNameBuffer.clear(); variableNameBuffer << "p" << state; - resultingMap[state] = solver.createContinuousVariable(variableNameBuffer.str(), storm::solver::LpSolver::BOUNDED, 0, 1, 0); + resultingMap[state] = variableNameBuffer.str(); + solver.addBoundedContinuousVariable(resultingMap[state], 0, 1); ++numberOfVariablesCreated; } return std::make_pair(resultingMap, numberOfVariablesCreated); } /*! - * Creates the variables for the probabilities in the model. + * Creates the variable for the probability of the virtual initial state. * * @param solver The MILP solver. * @param maximizeProbability If set to true, the objective function is constructed in a way that a * label-minimal subsystem of maximal probability is computed. * @return The index of the variable for the probability of the virtual initial state. */ - static std::pair<uint_fast64_t, uint_fast64_t> createVirtualInitialStateVariable(storm::solver::LpSolver& solver, bool maximizeProbability = false) { - int error = 0; + static std::pair<std::string, uint_fast64_t> createVirtualInitialStateVariable(storm::solver::LpSolver& solver, bool maximizeProbability = false) { std::stringstream variableNameBuffer; variableNameBuffer << "pinit"; - - return std::make_pair(solver.createContinuousVariable(variableNameBuffer.str(), storm::solver::LpSolver::BOUNDED, 0, 1, 0), 1); + std::string variableName = variableNameBuffer.str(); + solver.addBoundedContinuousVariable(variableName, 0, 1); + return std::make_pair(variableName, 1); } /*! @@ -284,11 +283,10 @@ namespace storm { * @param stateInformation The information about the states in the model. * @return A mapping from problematic states to the index of the corresponding variables. */ - static std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> createProblematicStateVariables(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation) { - int error = 0; + static std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> createProblematicStateVariables(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation) { std::stringstream variableNameBuffer; uint_fast64_t numberOfVariablesCreated = 0; - std::unordered_map<uint_fast64_t, uint_fast64_t> resultingMap; + std::unordered_map<uint_fast64_t, std::string> resultingMap; for (auto state : stateInformation.problematicStates) { // First check whether there is not already a variable for this state and advance to the next state @@ -297,19 +295,21 @@ namespace storm { variableNameBuffer.str(""); variableNameBuffer.clear(); variableNameBuffer << "r" << state; - resultingMap[state] = solver.createContinuousVariable(variableNameBuffer.str(), storm::solver::LpSolver::BOUNDED, 0, 1, 0); + resultingMap[state] = variableNameBuffer.str(); + solver.addBoundedContinuousVariable(resultingMap[state], 0, 1); ++numberOfVariablesCreated; } std::list<uint_fast64_t> const& relevantChoicesForState = choiceInformation.relevantChoicesForRelevantStates.at(state); for (uint_fast64_t row : relevantChoicesForState) { for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(row)) { - if (stateInformation.relevantStates.get(successorEntry.first)) { - if (resultingMap.find(successorEntry.first) == resultingMap.end()) { + if (stateInformation.relevantStates.get(successorEntry.getColumn())) { + if (resultingMap.find(successorEntry.getColumn()) == resultingMap.end()) { variableNameBuffer.str(""); variableNameBuffer.clear(); - variableNameBuffer << "r" << successorEntry.first; - resultingMap[state] = solver.createContinuousVariable(variableNameBuffer.str(), storm::solver::LpSolver::BOUNDED, 0, 1, 0); + variableNameBuffer << "r" << successorEntry.getColumn(); + resultingMap[successorEntry.getColumn()] = variableNameBuffer.str(); + solver.addBoundedContinuousVariable(resultingMap[successorEntry.getColumn()], 0, 1); ++numberOfVariablesCreated; } } @@ -328,21 +328,21 @@ namespace storm { * @param choiceInformation The information about the choices in the model. * @return A mapping from problematic choices to the index of the corresponding variables. */ - static std::pair<std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, uint_fast64_t, PairHash>, uint_fast64_t> createProblematicChoiceVariables(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation) { - int error = 0; + static std::pair<std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, std::string, PairHash>, uint_fast64_t> createProblematicChoiceVariables(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation) { std::stringstream variableNameBuffer; uint_fast64_t numberOfVariablesCreated = 0; - std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, uint_fast64_t, PairHash> resultingMap; + std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, std::string, PairHash> resultingMap; for (auto state : stateInformation.problematicStates) { std::list<uint_fast64_t> const& relevantChoicesForState = choiceInformation.relevantChoicesForRelevantStates.at(state); for (uint_fast64_t row : relevantChoicesForState) { for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(row)) { - if (stateInformation.relevantStates.get(successorEntry.first)) { + if (stateInformation.relevantStates.get(successorEntry.getColumn())) { variableNameBuffer.str(""); variableNameBuffer.clear(); - variableNameBuffer << "t" << state << "to" << successorEntry.first; - resultingMap[std::make_pair(state, successorEntry.first)] = solver.createBinaryVariable(variableNameBuffer.str(), 0); + variableNameBuffer << "t" << state << "to" << successorEntry.getColumn(); + resultingMap[std::make_pair(state, successorEntry.getColumn())] = variableNameBuffer.str(); + solver.addBinaryVariable(resultingMap[std::make_pair(state, successorEntry.getColumn())]); ++numberOfVariablesCreated; } } @@ -366,44 +366,44 @@ namespace storm { VariableInformation result; // Create variables for involved labels. - std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> labelVariableResult = createLabelVariables(solver, choiceInformation.allRelevantLabels); - result.labelToVariableIndexMap = std::move(labelVariableResult.first); + std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> labelVariableResult = createLabelVariables(solver, choiceInformation.allRelevantLabels); + result.labelToVariableMap = std::move(labelVariableResult.first); result.numberOfVariables += labelVariableResult.second; LOG4CPLUS_DEBUG(logger, "Created variables for labels."); // Create scheduler variables for relevant states and their actions. - std::pair<std::unordered_map<uint_fast64_t, std::list<uint_fast64_t>>, uint_fast64_t> schedulerVariableResult = createSchedulerVariables(solver, stateInformation, choiceInformation); - result.stateToChoiceVariablesIndexMap = std::move(schedulerVariableResult.first); + std::pair<std::unordered_map<uint_fast64_t, std::list<std::string>>, uint_fast64_t> schedulerVariableResult = createSchedulerVariables(solver, stateInformation, choiceInformation); + result.stateToChoiceVariablesMap = std::move(schedulerVariableResult.first); result.numberOfVariables += schedulerVariableResult.second; LOG4CPLUS_DEBUG(logger, "Created variables for nondeterministic choices."); // Create scheduler variables for nondeterministically choosing an initial state. - std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> initialChoiceVariableResult = createInitialChoiceVariables(solver, labeledMdp, stateInformation); - result.initialStateToChoiceVariableIndexMap = std::move(initialChoiceVariableResult.first); + std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> initialChoiceVariableResult = createInitialChoiceVariables(solver, labeledMdp, stateInformation); + result.initialStateToChoiceVariableMap = std::move(initialChoiceVariableResult.first); result.numberOfVariables += initialChoiceVariableResult.second; LOG4CPLUS_DEBUG(logger, "Created variables for the nondeterministic choice of the initial state."); // Create variables for probabilities for all relevant states. - std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> probabilityVariableResult = createProbabilityVariables(solver, stateInformation); - result.stateToProbabilityVariableIndexMap = std::move(probabilityVariableResult.first); + std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> probabilityVariableResult = createProbabilityVariables(solver, stateInformation); + result.stateToProbabilityVariableMap = std::move(probabilityVariableResult.first); result.numberOfVariables += probabilityVariableResult.second; LOG4CPLUS_DEBUG(logger, "Created variables for the reachability probabilities."); // Create a probability variable for a virtual initial state that nondeterministically chooses one of the system's real initial states as its target state. - std::pair<uint_fast64_t, uint_fast64_t> virtualInitialStateVariableResult = createVirtualInitialStateVariable(solver); - result.virtualInitialStateVariableIndex = virtualInitialStateVariableResult.first; + std::pair<std::string, uint_fast64_t> virtualInitialStateVariableResult = createVirtualInitialStateVariable(solver); + result.virtualInitialStateVariable = virtualInitialStateVariableResult.first; result.numberOfVariables += virtualInitialStateVariableResult.second; LOG4CPLUS_DEBUG(logger, "Created variables for the virtual initial state."); // Create variables for problematic states. - std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> problematicStateVariableResult = createProblematicStateVariables(solver, labeledMdp, stateInformation, choiceInformation); - result.problematicStateToVariableIndexMap = std::move(problematicStateVariableResult.first); + std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> problematicStateVariableResult = createProblematicStateVariables(solver, labeledMdp, stateInformation, choiceInformation); + result.problematicStateToVariableMap = std::move(problematicStateVariableResult.first); result.numberOfVariables += problematicStateVariableResult.second; LOG4CPLUS_DEBUG(logger, "Created variables for the problematic states."); // Create variables for problematic choices. - std::pair<std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, uint_fast64_t, PairHash>, uint_fast64_t> problematicTransitionVariableResult = createProblematicChoiceVariables(solver, labeledMdp, stateInformation, choiceInformation); - result.problematicTransitionToVariableIndexMap = problematicTransitionVariableResult.first; + std::pair<std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, std::string, PairHash>, uint_fast64_t> problematicTransitionVariableResult = createProblematicChoiceVariables(solver, labeledMdp, stateInformation, choiceInformation); + result.problematicTransitionToVariableMap = problematicTransitionVariableResult.first; result.numberOfVariables += problematicTransitionVariableResult.second; LOG4CPLUS_DEBUG(logger, "Created variables for the problematic choices."); @@ -427,7 +427,13 @@ namespace storm { * @return The total number of constraints that were created. */ static uint_fast64_t assertProbabilityGreaterThanThreshold(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, VariableInformation const& variableInformation, double probabilityThreshold, bool strictBound) { - solver.addConstraint("ProbGreaterThreshold", {variableInformation.virtualInitialStateVariableIndex}, {1}, strictBound ? storm::solver::LpSolver::GREATER : storm::solver::LpSolver::GREATER_EQUAL, probabilityThreshold); + storm::expressions::Expression constraint; + if (strictBound) { + constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.virtualInitialStateVariable) > storm::expressions::Expression::createDoubleLiteral(probabilityThreshold); + } else { + constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.virtualInitialStateVariable) >= storm::expressions::Expression::createDoubleLiteral(probabilityThreshold); + } + solver.addConstraint("ProbGreaterThreshold", constraint); return 1; } @@ -443,28 +449,28 @@ namespace storm { // Assert that the policy chooses at most one action in each state of the system. uint_fast64_t numberOfConstraintsCreated = 0; for (auto state : stateInformation.relevantStates) { - std::list<uint_fast64_t> const& choiceVariableIndices = variableInformation.stateToChoiceVariablesIndexMap.at(state); - std::vector<uint_fast64_t> variables; - std::vector<double> coefficients(choiceVariableIndices.size(), 1); - variables.reserve(choiceVariableIndices.size()); - for (auto choiceVariableIndex : choiceVariableIndices) { - variables.push_back(choiceVariableIndex); + std::list<std::string> const& choiceVariableIndices = variableInformation.stateToChoiceVariablesMap.at(state); + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleLiteral(0); + + for (auto const& choiceVariable : choiceVariableIndices) { + constraint = constraint + storm::expressions::Expression::createIntegerVariable(choiceVariable); } - solver.addConstraint("ValidPolicy" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS_EQUAL, 1); + constraint = constraint <= storm::expressions::Expression::createDoubleLiteral(1); + + solver.addConstraint("ValidPolicy" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } // Now assert that the virtual initial state picks exactly one initial state from the system as its // successor state. - std::vector<uint_fast64_t> variables; - variables.reserve(variableInformation.initialStateToChoiceVariableIndexMap.size()); - std::vector<double> coefficients(variableInformation.initialStateToChoiceVariableIndexMap.size(), 1); - for (auto initialStateVariableIndexPair : variableInformation.initialStateToChoiceVariableIndexMap) { - variables.push_back(initialStateVariableIndexPair.second); + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleLiteral(0); + for (auto const& initialStateVariablePair : variableInformation.initialStateToChoiceVariableMap) { + constraint = constraint + storm::expressions::Expression::createIntegerVariable(initialStateVariablePair.second); } + constraint = constraint == storm::expressions::Expression::createDoubleLiteral(1); - solver.addConstraint("VirtualInitialStateChoosesOneInitialState", variables, coefficients, storm::solver::LpSolver::EQUAL, 1); + solver.addConstraint("VirtualInitialStateChoosesOneInitialState", constraint); ++numberOfConstraintsCreated; return numberOfConstraintsCreated; @@ -486,13 +492,14 @@ namespace storm { std::vector<boost::container::flat_set<uint_fast64_t>> const& choiceLabeling = labeledMdp.getChoiceLabeling(); for (auto state : stateInformation.relevantStates) { - std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(state).begin(); + std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesMap.at(state).begin(); for (auto choice : choiceInformation.relevantChoicesForRelevantStates.at(state)) { for (auto label : choiceLabeling[choice]) { - solver.addConstraint("ChoicesImplyLabels" + std::to_string(numberOfConstraintsCreated), {variableInformation.labelToVariableIndexMap.at(label), *choiceVariableIndicesIterator}, {1, -1}, storm::solver::LpSolver::GREATER_EQUAL, 0); + storm::expressions::Expression constraint = storm::expressions::Expression::createIntegerVariable(variableInformation.labelToVariableMap.at(label)) - storm::expressions::Expression::createIntegerVariable(*choiceVariableIterator) >= storm::expressions::Expression::createDoubleLiteral(0); + solver.addConstraint("ChoicesImplyLabels" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } - ++choiceVariableIndicesIterator; + ++choiceVariableIterator; } } return numberOfConstraintsCreated; @@ -511,16 +518,12 @@ namespace storm { static uint_fast64_t assertZeroProbabilityWithoutChoice(storm::solver::LpSolver& solver, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation, VariableInformation const& variableInformation) { uint_fast64_t numberOfConstraintsCreated = 0; for (auto state : stateInformation.relevantStates) { - std::list<uint_fast64_t> const& choiceVariableIndices = variableInformation.stateToChoiceVariablesIndexMap.at(state); - - std::vector<double> coefficients(choiceVariableIndices.size() + 1, -1); - coefficients[0] = 1; - std::vector<uint_fast64_t> variables; - variables.reserve(variableInformation.stateToChoiceVariablesIndexMap.at(state).size() + 1); - variables.push_back(variableInformation.stateToProbabilityVariableIndexMap.at(state)); - variables.insert(variables.end(), choiceVariableIndices.begin(), choiceVariableIndices.end()); - - solver.addConstraint("ProbabilityIsZeroIfNoAction" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS_EQUAL, 0); + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.stateToProbabilityVariableMap.at(state)); + for (auto const& choiceVariable : variableInformation.stateToChoiceVariablesMap.at(state)) { + constraint = constraint - storm::expressions::Expression::createIntegerVariable(choiceVariable); + } + constraint = constraint <= storm::expressions::Expression::createDoubleLiteral(0); + solver.addConstraint("ProbabilityIsZeroIfNoAction" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } return numberOfConstraintsCreated; @@ -539,42 +542,33 @@ namespace storm { */ static uint_fast64_t assertReachabilityProbabilities(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, storm::storage::BitVector const& psiStates, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation, VariableInformation const& variableInformation) { uint_fast64_t numberOfConstraintsCreated = 0; - int error = 0; for (auto state : stateInformation.relevantStates) { - std::vector<double> coefficients; - std::vector<uint_fast64_t> variables; - - std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(state).begin(); + std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesMap.at(state).begin(); for (auto choice : choiceInformation.relevantChoicesForRelevantStates.at(state)) { - variables.clear(); - coefficients.clear(); - variables.push_back(variableInformation.stateToProbabilityVariableIndexMap.at(state)); - coefficients.push_back(1.0); + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.stateToProbabilityVariableMap.at(state)); double rightHandSide = 1; for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(choice)) { - if (stateInformation.relevantStates.get(successorEntry.first)) { - variables.push_back(static_cast<int>(variableInformation.stateToProbabilityVariableIndexMap.at(successorEntry.first))); - coefficients.push_back(-successorEntry.second); - } else if (psiStates.get(successorEntry.first)) { - rightHandSide += successorEntry.second; + if (stateInformation.relevantStates.get(successorEntry.getColumn())) { + constraint = constraint - storm::expressions::Expression::createDoubleLiteral(successorEntry.getValue()) * storm::expressions::Expression::createDoubleVariable(variableInformation.stateToProbabilityVariableMap.at(successorEntry.getColumn())); + } else if (psiStates.get(successorEntry.getColumn())) { + rightHandSide += successorEntry.getValue(); } } - coefficients.push_back(1); - variables.push_back(*choiceVariableIndicesIterator); - - solver.addConstraint("ReachabilityProbabilities" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS_EQUAL, rightHandSide); + constraint = constraint + storm::expressions::Expression::createIntegerVariable(*choiceVariableIterator) <= storm::expressions::Expression::createDoubleLiteral(rightHandSide); + solver.addConstraint("ReachabilityProbabilities" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; - ++choiceVariableIndicesIterator; + ++choiceVariableIterator; } } // Make sure that the virtual initial state is being assigned the probability from the initial state // that it selected as a successor state. - for (auto initialStateVariableIndexPair : variableInformation.initialStateToChoiceVariableIndexMap) { - solver.addConstraint("VirtualInitialStateHasCorrectProbability" + std::to_string(numberOfConstraintsCreated), {variableInformation.virtualInitialStateVariableIndex, variableInformation.stateToProbabilityVariableIndexMap.at(initialStateVariableIndexPair.first), initialStateVariableIndexPair.second}, {1, -1, 1}, storm::solver::LpSolver::LESS_EQUAL, 1); + for (auto const& initialStateVariablePair : variableInformation.initialStateToChoiceVariableMap) { + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.virtualInitialStateVariable) - storm::expressions::Expression::createDoubleVariable(variableInformation.stateToProbabilityVariableMap.at(initialStateVariablePair.first)) + storm::expressions::Expression::createDoubleVariable(initialStateVariablePair.second) <= storm::expressions::Expression::createDoubleLiteral(1); + solver.addConstraint("VirtualInitialStateHasCorrectProbability" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } @@ -596,44 +590,34 @@ namespace storm { for (auto stateListPair : choiceInformation.problematicChoicesForProblematicStates) { for (auto problematicChoice : stateListPair.second) { - std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(stateListPair.first).begin(); + std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesMap.at(stateListPair.first).begin(); for (auto relevantChoice : choiceInformation.relevantChoicesForRelevantStates.at(stateListPair.first)) { if (relevantChoice == problematicChoice) { break; } - ++choiceVariableIndicesIterator; + ++choiceVariableIterator; } - std::vector<uint_fast64_t> variables; - std::vector<double> coefficients; - - variables.push_back(*choiceVariableIndicesIterator); - coefficients.push_back(1); - + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(*choiceVariableIterator); for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(problematicChoice)) { - variables.push_back(variableInformation.problematicTransitionToVariableIndexMap.at(std::make_pair(stateListPair.first, successorEntry.first))); - coefficients.push_back(-1); + constraint = constraint - storm::expressions::Expression::createDoubleVariable(variableInformation.problematicTransitionToVariableMap.at(std::make_pair(stateListPair.first, successorEntry.getColumn()))); } + constraint = constraint <= storm::expressions::Expression::createDoubleLiteral(0); - solver.addConstraint("UnproblematicStateReachable" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS_EQUAL, 0); + solver.addConstraint("UnproblematicStateReachable" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } } for (auto state : stateInformation.problematicStates) { for (auto problematicChoice : choiceInformation.problematicChoicesForProblematicStates.at(state)) { - for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(state)) { - std::vector<uint_fast64_t> variables; - std::vector<double> coefficients; - - variables.push_back(variableInformation.problematicStateToVariableIndexMap.at(state)); - coefficients.push_back(1); - variables.push_back(variableInformation.problematicStateToVariableIndexMap.at(successorEntry.first)); - coefficients.push_back(-1); - variables.push_back(variableInformation.problematicTransitionToVariableIndexMap.at(std::make_pair(state, successorEntry.first))); - coefficients.push_back(1); + for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(problematicChoice)) { + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.problematicStateToVariableMap.at(state)); + constraint = constraint - storm::expressions::Expression::createDoubleVariable(variableInformation.problematicStateToVariableMap.at(successorEntry.getColumn())); + constraint = constraint + storm::expressions::Expression::createDoubleVariable(variableInformation.problematicTransitionToVariableMap.at(std::make_pair(state, successorEntry.getColumn()))); + constraint = constraint < storm::expressions::Expression::createDoubleLiteral(1); - solver.addConstraint("UnproblematicStateReachable" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS, 1); + solver.addConstraint("UnproblematicStateReachable" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } } @@ -655,7 +639,8 @@ namespace storm { uint_fast64_t numberOfConstraintsCreated = 0; for (auto label : choiceInformation.knownLabels) { - solver.addConstraint("KnownLabels" + std::to_string(numberOfConstraintsCreated), {variableInformation.labelToVariableIndexMap.at(label)}, {1}, storm::solver::LpSolver::EQUAL, 1); + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.labelToVariableMap.at(label)) == storm::expressions::Expression::createDoubleLiteral(1); + solver.addConstraint("KnownLabels" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } @@ -676,61 +661,52 @@ namespace storm { static uint_fast64_t assertSchedulerCuts(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, storm::storage::BitVector const& psiStates, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation, VariableInformation const& variableInformation) { storm::storage::SparseMatrix<T> backwardTransitions = labeledMdp.getBackwardTransitions(); uint_fast64_t numberOfConstraintsCreated = 0; - std::vector<uint_fast64_t> variables; - std::vector<double> coefficients; for (auto state : stateInformation.relevantStates) { // Assert that all states, that select an action, this action either has a non-zero probability to // go to a psi state directly, or in the successor states, at least one action is selected as well. - std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(state).begin(); + std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesMap.at(state).begin(); for (auto choice : choiceInformation.relevantChoicesForRelevantStates.at(state)) { bool psiStateReachableInOneStep = false; for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(choice)) { - if (psiStates.get(successorEntry.first)) { + if (psiStates.get(successorEntry.getColumn())) { psiStateReachableInOneStep = true; } } if (!psiStateReachableInOneStep) { - variables.clear(); - coefficients.clear(); - - variables.push_back(static_cast<int>(*choiceVariableIndicesIterator)); - coefficients.push_back(1); - + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(*choiceVariableIterator); for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(choice)) { - if (state != successorEntry.first && stateInformation.relevantStates.get(successorEntry.first)) { - std::list<uint_fast64_t> const& successorChoiceVariableIndices = variableInformation.stateToChoiceVariablesIndexMap.at(successorEntry.first); + if (state != successorEntry.getColumn() && stateInformation.relevantStates.get(successorEntry.getColumn())) { + std::list<std::string> const& successorChoiceVariableIndices = variableInformation.stateToChoiceVariablesMap.at(successorEntry.getColumn()); - for (auto choiceVariableIndex : successorChoiceVariableIndices) { - variables.push_back(choiceVariableIndex); - coefficients.push_back(-1); + for (auto const& choiceVariable : successorChoiceVariableIndices) { + constraint = constraint - storm::expressions::Expression::createDoubleVariable(choiceVariable); } } } + constraint = constraint <= storm::expressions::Expression::createDoubleLiteral(1); - solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS_EQUAL, 1); + solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } - ++choiceVariableIndicesIterator; + ++choiceVariableIterator; } // For all states assert that there is either a selected incoming transition in the subsystem or the // state is the chosen initial state if there is one selected action in the current state. - variables.clear(); - coefficients.clear(); - - for (auto choiceVariableIndex : variableInformation.stateToChoiceVariablesIndexMap.at(state)) { - variables.push_back(choiceVariableIndex); - coefficients.push_back(1); + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleLiteral(0); + + for (auto const& choiceVariable : variableInformation.stateToChoiceVariablesMap.at(state)) { + constraint = constraint + storm::expressions::Expression::createDoubleVariable(choiceVariable); } // Compute the set of predecessors. std::unordered_set<uint_fast64_t> predecessors; for (auto const& predecessorEntry : backwardTransitions.getRow(state)) { - if (state != predecessorEntry.first) { - predecessors.insert(predecessorEntry.first); + if (state != predecessorEntry.getColumn()) { + predecessors.insert(predecessorEntry.getColumn()); } } @@ -740,13 +716,13 @@ namespace storm { continue; } - std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(predecessor).begin(); + std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesMap.at(predecessor).begin(); for (auto relevantChoice : choiceInformation.relevantChoicesForRelevantStates.at(predecessor)) { bool choiceTargetsCurrentState = false; // Check if the current choice targets the current state. for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(relevantChoice)) { - if (state == successorEntry.first) { + if (state == successorEntry.getColumn()) { choiceTargetsCurrentState = true; break; } @@ -754,45 +730,42 @@ namespace storm { // If it does, we can add the choice to the sum. if (choiceTargetsCurrentState) { - variables.push_back(static_cast<int>(*choiceVariableIndicesIterator)); - coefficients.push_back(-1); + constraint = constraint - storm::expressions::Expression::createDoubleVariable(*choiceVariableIterator); } - ++choiceVariableIndicesIterator; + ++choiceVariableIterator; } } // If the current state is an initial state and is selected as a successor state by the virtual // initial state, then this also justifies making a choice in the current state. if (labeledMdp.getLabeledStates("init").get(state)) { - variables.push_back(variableInformation.initialStateToChoiceVariableIndexMap.at(state)); - coefficients.push_back(-1); + constraint = constraint - storm::expressions::Expression::createDoubleVariable(variableInformation.initialStateToChoiceVariableMap.at(state)); } + constraint = constraint <= storm::expressions::Expression::createDoubleLiteral(0); - solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS_EQUAL, 0); + solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } // Assert that at least one initial state selects at least one action. - variables.clear(); - coefficients.clear(); + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleLiteral(0); for (auto initialState : labeledMdp.getLabeledStates("init")) { - for (auto choiceVariableIndex : variableInformation.stateToChoiceVariablesIndexMap.at(initialState)) { - variables.push_back(choiceVariableIndex); - coefficients.push_back(1); + for (auto const& choiceVariable : variableInformation.stateToChoiceVariablesMap.at(initialState)) { + constraint = constraint + storm::expressions::Expression::createDoubleVariable(choiceVariable); } } - solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::GREATER_EQUAL, 1); + constraint = constraint >= storm::expressions::Expression::createDoubleLiteral(1); + solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; // Add constraints that ensure at least one choice is selected that targets a psi state. - variables.clear(); - coefficients.clear(); + constraint = storm::expressions::Expression::createDoubleLiteral(0); std::unordered_set<uint_fast64_t> predecessors; for (auto psiState : psiStates) { // Compute the set of predecessors. for (auto const& predecessorEntry : backwardTransitions.getRow(psiState)) { - if (psiState != predecessorEntry.first) { - predecessors.insert(predecessorEntry.first); + if (psiState != predecessorEntry.getColumn()) { + predecessors.insert(predecessorEntry.getColumn()); } } } @@ -803,13 +776,13 @@ namespace storm { continue; } - std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(predecessor).begin(); + std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesMap.at(predecessor).begin(); for (auto relevantChoice : choiceInformation.relevantChoicesForRelevantStates.at(predecessor)) { bool choiceTargetsPsiState = false; // Check if the current choice targets the current state. for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(relevantChoice)) { - if (psiStates.get(successorEntry.first)) { + if (psiStates.get(successorEntry.getColumn())) { choiceTargetsPsiState = true; break; } @@ -817,14 +790,14 @@ namespace storm { // If it does, we can add the choice to the sum. if (choiceTargetsPsiState) { - variables.push_back(*choiceVariableIndicesIterator); - coefficients.push_back(1); + constraint = constraint + storm::expressions::Expression::createDoubleVariable(*choiceVariableIterator); } - ++choiceVariableIndicesIterator; + ++choiceVariableIterator; } } + constraint = constraint >= storm::expressions::Expression::createDoubleLiteral(1); - solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::GREATER_EQUAL, 1); + solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; return numberOfConstraintsCreated; @@ -881,6 +854,9 @@ namespace storm { LOG4CPLUS_DEBUG(logger, "Asserted scheduler cuts."); } + // Finally, we can tell the solver to incorporate the latest changes. + solver.update(); + LOG4CPLUS_INFO(logger, "Successfully created " << numberOfConstraints << " MILP constraints."); } @@ -893,7 +869,7 @@ namespace storm { static boost::container::flat_set<uint_fast64_t> getUsedLabelsInSolution(storm::solver::LpSolver const& solver, VariableInformation const& variableInformation) { boost::container::flat_set<uint_fast64_t> result; - for (auto labelVariablePair : variableInformation.labelToVariableIndexMap) { + for (auto const& labelVariablePair : variableInformation.labelToVariableMap) { bool labelTaken = solver.getBinaryValue(labelVariablePair.second); if (labelTaken) { @@ -917,10 +893,10 @@ namespace storm { std::map<uint_fast64_t, uint_fast64_t> result; for (auto state : stateInformation.relevantStates) { - std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(state).begin(); + std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesIndexMap.at(state).begin(); for (auto choice : choiceInformation.relevantChoicesForRelevantStates.at(state)) { - bool choiceTaken = solver.getBinaryValue(*choiceVariableIndicesIterator); - ++choiceVariableIndicesIterator; + bool choiceTaken = solver.getBinaryValue(*choiceVariableIterator); + ++choiceVariableIterator; if (choiceTaken) { result.emplace_hint(result.end(), state, choice); } @@ -939,20 +915,20 @@ namespace storm { */ static std::pair<uint_fast64_t, double> getReachabilityProbability(storm::solver::LpSolver const& solver, storm::models::Mdp<T> const& labeledMdp, VariableInformation const& variableInformation) { uint_fast64_t selectedInitialState = 0; - for (auto initialStateVariableIndexPair : variableInformation.initialStateToChoiceVariableIndexMap) { - bool initialStateChosen = solver.getBinaryValue(initialStateVariableIndexPair.second); + for (auto const& initialStateVariablePair : variableInformation.initialStateToChoiceVariableMap) { + bool initialStateChosen = solver.getBinaryValue(initialStateVariablePair.second); if (initialStateChosen) { - selectedInitialState = initialStateVariableIndexPair.first; + selectedInitialState = initialStateVariablePair.first; break; } } - double reachabilityProbability = solver.getContinuousValue(variableInformation.virtualInitialStateVariableIndex); + double reachabilityProbability = solver.getContinuousValue(variableInformation.virtualInitialStateVariable); return std::make_pair(selectedInitialState, reachabilityProbability); } public: - + static boost::container::flat_set<uint_fast64_t> getMinimalLabelSet(storm::models::Mdp<T> const& labeledMdp, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, double probabilityThreshold, bool strictBound, bool checkThresholdFeasible = false, bool includeSchedulerCuts = false) { // (0) Check whether the MDP is indeed labeled. if (!labeledMdp.hasChoiceLabeling()) { @@ -1009,7 +985,7 @@ namespace storm { * @param formulaPtr A pointer to a safety formula. The outermost operator must be a probabilistic bound operator with a strict upper bound. The nested * formula can be either an unbounded until formula or an eventually formula. */ - static void computeCounterexample(storm::ir::Program const& program, storm::models::Mdp<T> const& labeledMdp, storm::property::prctl::AbstractPrctlFormula<double> const* formulaPtr) { + static void computeCounterexample(storm::prism::Program const& program, storm::models::Mdp<T> const& labeledMdp, storm::property::prctl::AbstractPrctlFormula<double> const* formulaPtr) { std::cout << std::endl << "Generating minimal label counterexample for formula " << formulaPtr->toString() << std::endl; // First, we need to check whether the current formula is an Until-Formula. storm::property::prctl::ProbabilisticBoundOperator<double> const* probBoundFormula = dynamic_cast<storm::property::prctl::ProbabilisticBoundOperator<double> const*>(formulaPtr); @@ -1021,7 +997,7 @@ namespace storm { LOG4CPLUS_ERROR(logger, "Illegal comparison operator in formula " << probBoundFormula->toString() << ". Only upper bounds are supported for counterexample generation."); throw storm::exceptions::InvalidPropertyException() << "Illegal comparison operator in formula " << probBoundFormula->toString() << ". Only upper bounds are supported for counterexample generation."; } - bool strictBound = !probBoundFormula->getComparisonOperator() == storm::property::ComparisonType::LESS; + bool strictBound = !(probBoundFormula->getComparisonOperator() == storm::property::ComparisonType::LESS); // Now derive the probability threshold we need to exceed as well as the phi and psi states. Simultaneously, check whether the formula is of a valid shape. double bound = probBoundFormula->getBound(); @@ -1054,9 +1030,8 @@ namespace storm { std::cout << std::endl << "Computed minimal label set of size " << usedLabelSet.size() << " in " << std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count() << "ms." << std::endl; std::cout << "Resulting program:" << std::endl; - storm::ir::Program restrictedProgram(program); - restrictedProgram.restrictCommands(usedLabelSet); - std::cout << restrictedProgram.toString() << std::endl; + storm::prism::Program restrictedProgram = program.restrictCommands(usedLabelSet); + std::cout << restrictedProgram << std::endl; std::cout << std::endl << "-------------------------------------------" << std::endl; // FIXME: Return the DTMC that results from applying the max scheduler in the MDP restricted to the computed label set. diff --git a/src/counterexamples/PathBasedSubsystemGenerator.h b/src/counterexamples/PathBasedSubsystemGenerator.h index b7a9424fa..e72f1e96f 100644 --- a/src/counterexamples/PathBasedSubsystemGenerator.h +++ b/src/counterexamples/PathBasedSubsystemGenerator.h @@ -69,31 +69,31 @@ public: for(auto const& trans : transMat.getRow(init)) { //save transition only if it's no 'virtual transition of prob 0 and it doesn't go from init state to init state. - if(trans.second != (T) 0 && !subSysStates.get(trans.first)) { + if(trans.getValue() != (T) 0 && !subSysStates.get(trans.getColumn())) { //new state? - if(distances[trans.first].second == (T) -1) { - distances[trans.first].first = init; - distances[trans.first].second = trans.second; + if(distances[trans.getColumn()].second == (T) -1) { + distances[trans.getColumn()].first = init; + distances[trans.getColumn()].second = trans.getValue(); - activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, distances[trans.first].second)); + activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), distances[trans.getColumn()].second)); } - else if(distances[trans.first].second < trans.second){ + else if(distances[trans.getColumn()].second < trans.getValue()){ //This state has already been discovered //And the distance can be improved by using this transition. //find state in set, remove it, reenter it with new and correct values. - auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.first, distances[trans.first].second)); + auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.getColumn(), distances[trans.getColumn()].second)); for(;range.first != range.second; range.first++) { - if(trans.first == range.first->first) { + if(trans.getColumn() == range.first->first) { activeSet.erase(range.first); break; } } - distances[trans.first].first = init; - distances[trans.first].second = trans.second; + distances[trans.getColumn()].first = init; + distances[trans.getColumn()].second = trans.getValue(); - activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, trans.second)); + activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), trans.getValue())); } } } @@ -115,36 +115,36 @@ public: // Look at all neighbors for(auto const& trans : transMat.getRow(activeState.first)) { // Only consider the transition if it's not virtual - if(trans.second != (T) 0) { + if(trans.getValue() != (T) 0) { - T distance = activeState.second * trans.second; + T distance = activeState.second * trans.getValue(); //not discovered or initial terminal state - if(distances[trans.first].second == (T)-1) { + if(distances[trans.getColumn()].second == (T)-1) { //New state discovered -> save it - distances[trans.first].first = activeState.first; - distances[trans.first].second = distance; + distances[trans.getColumn()].first = activeState.first; + distances[trans.getColumn()].second = distance; // push newly discovered state into activeSet - activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, distance)); + activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), distance)); } - else if(distances[trans.first].second < distance ){ + else if(distances[trans.getColumn()].second < distance) { //This state has already been discovered //And the distance can be improved by using this transition. //find state in set, remove it, reenter it with new and correct values. - auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.first, distances[trans.first].second)); + auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.getColumn(), distances[trans.getColumn()].second)); for(;range.first != range.second; range.first++) { - if(trans.first == range.first->first) { + if(trans.getColumn() == range.first->first) { activeSet.erase(range.first); break; } } - distances[trans.first].first = activeState.first; - distances[trans.first].second = distance; - activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, distance)); + distances[trans.getColumn()].first = activeState.first; + distances[trans.getColumn()].second = distance; + activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), distance)); } } } @@ -182,33 +182,33 @@ public: for(auto const& trans : transMat.getRow(init)) { //save transition only if it's no 'virtual transition of prob 0 and it doesn't go from init state to init state. - if(trans.second != (T) 0 && !subSysStates.get(trans.first)) { + if(trans.getValue() != (T) 0 && !subSysStates.get(trans.getColumn())) { //new state? - if(distances[trans.first].second == (T) -1) { + if(distances[trans.getColumn()].second == (T) -1) { //for initialization of subsys -> subsys search use prob (init -> subsys state -> found state) instead of prob(subsys state -> found state) - distances[trans.first].first = init; - distances[trans.first].second = trans.second * (itDistances[init].second == -1 ? 1 : itDistances[init].second); + distances[trans.getColumn()].first = init; + distances[trans.getColumn()].second = trans.getValue() * (itDistances[init].second == -1 ? 1 : itDistances[init].second); - activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, distances[trans.first].second)); + activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), distances[trans.getColumn()].second)); } - else if(distances[trans.first].second < trans.second * itDistances[init].second){ + else if(distances[trans.getColumn()].second < trans.getValue() * itDistances[init].second){ //This state has already been discovered //And the distance can be improved by using this transition. //find state in set, remove it, reenter it with new and correct values. - auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.first, distances[trans.first].second)); + auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.getColumn(), distances[trans.getColumn()].second)); for(;range.first != range.second; range.first++) { - if(trans.first == range.first->first) { + if(trans.getColumn() == range.first->first) { activeSet.erase(range.first); break; } } //for initialization of subsys -> subsys search use prob (init -> subsys state -> found state) instead of prob(subsys state -> found state) - distances[trans.first].first = init; - distances[trans.first].second = trans.second * (itDistances[init].second == -1 ? 1 : itDistances[init].second); + distances[trans.getColumn()].first = init; + distances[trans.getColumn()].second = trans.getValue() * (itDistances[init].second == -1 ? 1 : itDistances[init].second); - activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, trans.second)); + activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), trans.getValue())); } } } @@ -225,7 +225,7 @@ public: activeSet.erase(--activeSet.end()); // Always stop at first target/terminal state - //if(terminalStates.get(activeState.first) || subSysStates.get(activeState.first)) break; + //if(terminalStates.get(activeState.getColumn()) || subSysStates.get(activeState.getColumn())) break; // If this is an initial state, do not consider its outgoing transitions, since all relevant ones have already been considered // Same goes for forbidden states since they may not be used on a path, except as last node. @@ -233,36 +233,36 @@ public: // Look at all neighbors for(auto const& trans : transMat.getRow(activeState.first)) { // Only consider the transition if it's not virtual - if(trans.second != (T) 0) { + if(trans.getValue() != (T) 0) { - T distance = activeState.second * trans.second; + T distance = activeState.second * trans.getValue(); //not discovered or initial terminal state - if(distances[trans.first].second == (T)-1) { + if(distances[trans.getColumn()].second == (T)-1) { //New state discovered -> save it - distances[trans.first].first = activeState.first; - distances[trans.first].second = distance; + distances[trans.getColumn()].first = activeState.first; + distances[trans.getColumn()].second = distance; // push newly discovered state into activeSet - activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, distance)); + activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), distance)); } - else if(distances[trans.first].second < distance ){ + else if(distances[trans.getColumn()].second < distance) { //This state has already been discovered //And the distance can be improved by using this transition. //find state in set, remove it, reenter it with new and correct values. - auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.first, distances[trans.first].second)); + auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.getColumn(), distances[trans.getColumn()].second)); for(;range.first != range.second; range.first++) { - if(trans.first == range.first->first) { + if(trans.getColumn() == range.first->first) { activeSet.erase(range.first); break; } } - distances[trans.first].first = activeState.first; - distances[trans.first].second = distance; - activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, distance)); + distances[trans.getColumn()].first = activeState.first; + distances[trans.getColumn()].second = distance; + activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), distance)); } } } @@ -292,8 +292,8 @@ public: // if there is a terminal state that is an initial state then prob == 1 and return if(initStates.get(*target)){ - distances[*target].first = *target; - distances[*target].second = (T) 1; + distances[*target].getColumn() = *target; + distances[*target].getValue() = (T) 1; return; } @@ -302,19 +302,19 @@ public: //only use if allowed and not in subsys and not terminal if(allowedStates.get(*iter) && !subSysStates.get(*iter) && !terminalStates.get(*iter)) { //new state? - if(distances[*iter].second == (T) -1) { + if(distances[*iter].getValue() == (T) -1) { // save as discovered and push into active set - distances[*iter].first = *target; //successor - distances[*iter].second = transMat.getValue(*iter, *target); //prob of shortest path + distances[*iter].getColumn() = *target; //successor + distances[*iter].getValue() = transMat.getValue(*iter, *target); //prob of shortest path activeSet.insert(std::pair<uint_fast64_t, T>(*iter, probabilities[*iter])); //prob of reaching some terminal state from pred. } else { // state was already discovered // is this the better transition? - if(distances[*iter].second > transMat.getValue(*iter, *target)) { - distances[*iter].first = *target; - distances[*iter].second = transMat.getValue(*iter, *target); + if(distances[*iter].getValue() > transMat.getValue(*iter, *target)) { + distances[*iter].getColumn() = *target; + distances[*iter].getValue() = transMat.getValue(*iter, *target); } } } @@ -328,19 +328,19 @@ public: //only use if allowed and not in subsys and not terminal if(allowedStates.get(*iter) && !subSysStates.get(*iter) && !terminalStates.get(*iter)) { //new state? - if(distances[*iter].second == (T) -1) { + if(distances[*iter].getValue() == (T) -1) { // save as discovered and push into active set - distances[*iter].first = *sysState; //successor - distances[*iter].second = transMat.getValue(*iter, *sysState); //prob of shortest path + distances[*iter].getColumn() = *sysState; //successor + distances[*iter].getValue() = transMat.getValue(*iter, *sysState); //prob of shortest path activeSet.insert(std::pair<uint_fast64_t, T>(*iter, probabilities[*iter])); //prob of reaching some terminal state from pred. } else { // state was already discovered // is this the better transition? - if(distances[*iter].second > transMat.getValue(*iter, *sysState)) { - distances[*iter].first = *sysState; - distances[*iter].second = transMat.getValue(*iter, *sysState); + if(distances[*iter].getValue() > transMat.getValue(*iter, *sysState)) { + distances[*iter].getColumn() = *sysState; + distances[*iter].getValue() = transMat.getValue(*iter, *sysState); } } } @@ -355,7 +355,7 @@ public: while(!activeSet.empty()) { // copy here since using a reference leads to segfault state = *(--activeSet.end()); - activeState = state.first; + activeState = state.getColumn(); activeSet.erase(--activeSet.end()); //stop on the first subsys/init state @@ -368,19 +368,19 @@ public: //only if transition is not "virtual" and no selfloop if(*iter != activeState && transMat.getValue(*iter, activeState) != (T) 0) { //new state? - if(distances[*iter].second == (T) -1) { + if(distances[*iter].getValue() == (T) -1) { // save as discovered and push into active set - distances[*iter].first = activeState; - distances[*iter].second = transMat.getValue(*iter, activeState) * distances[activeState].second; + distances[*iter].getColumn() = activeState; + distances[*iter].getValue() = transMat.getValue(*iter, activeState) * distances[activeState].getValue(); activeSet.insert(std::pair<uint_fast64_t, T>(*iter, probabilities[*iter])); } else { // state was already discovered // is this the better transition? - if(distances[*iter].second < transMat.getValue(*iter, activeState) * distances[activeState].second) { - distances[*iter].first = activeState; - distances[*iter].second = transMat.getValue(*iter, activeState) * distances[activeState].second; + if(distances[*iter].getValue() < transMat.getValue(*iter, activeState) * distances[activeState].getValue()) { + distances[*iter].getColumn() = activeState; + distances[*iter].getValue() = transMat.getValue(*iter, activeState) * distances[activeState].getValue(); } } } @@ -389,15 +389,15 @@ public: } //get path probability - probability = distances[activeState].second; + probability = distances[activeState].getValue(); if(probability == (T) -1) probability = 1; // iterate over the successors until reaching the end of the finite path shortestPath.push_back(activeState); - activeState = distances[activeState].first; + activeState = distances[activeState].getColumn(); while(!terminalStates.get(activeState) && !subSysStates.get(activeState)) { shortestPath.push_back(activeState); - activeState = distances[activeState].first; + activeState = distances[activeState].getColumn(); } shortestPath.push_back(activeState); } @@ -482,7 +482,7 @@ public: shortestPath.push_back(bestIndex); //At last compensate for the distance between init and source state - probability = itSearch ? probability : probability / itDistances[bestIndex].second; + probability = itSearch ? probability : probability / itDistances[bestIndex].first; } private: @@ -507,7 +507,7 @@ public: /*! * */ - static storm::models::Dtmc<T> computeCriticalSubsystem(storm::models::Dtmc<T>& model, storm::property::prctl::AbstractStateFormula<T> const& stateFormula) { + static storm::models::Dtmc<T> computeCriticalSubsystem(storm::models::Dtmc<T> & model, storm::property::prctl::AbstractStateFormula<T> const& stateFormula) { //------------------------------------------------------------- // 1. Strip and handle formulas diff --git a/src/counterexamples/SMTMinimalCommandSetGenerator.h b/src/counterexamples/SMTMinimalCommandSetGenerator.h index 6ccee589d..2abb27ed2 100644 --- a/src/counterexamples/SMTMinimalCommandSetGenerator.h +++ b/src/counterexamples/SMTMinimalCommandSetGenerator.h @@ -25,7 +25,7 @@ #include "src/solver/GmmxxNondeterministicLinearEquationSolver.h" #include "src/utility/counterexamples.h" -#include "src/utility/IRUtility.h" +#include "src/utility/PrismUtility.h" namespace storm { namespace counterexamples { diff --git a/src/exceptions/ExceptionMacros.h b/src/exceptions/ExceptionMacros.h new file mode 100644 index 000000000..09e7058ec --- /dev/null +++ b/src/exceptions/ExceptionMacros.h @@ -0,0 +1,31 @@ +#ifndef STORM_EXCEPTIONS_EXCEPTIONMACROS_H_ +#define STORM_EXCEPTIONS_EXCEPTIONMACROS_H_ + +#include <cassert> + +#include "log4cplus/logger.h" +#include "log4cplus/loggingmacros.h" + +extern log4cplus::Logger logger; + +#ifndef NDEBUG +#define LOG_ASSERT(cond, message) \ +{ \ + if (!(cond)) { \ + LOG4CPLUS_ERROR(logger, message); \ + assert(cond); \ + } \ +} while (false) +#else +#define LOG_ASSERT(cond, message) /* empty */ +#endif + +#define LOG_THROW(cond, exception, message) \ +{ \ +if (!(cond)) { \ +LOG4CPLUS_ERROR(logger, message); \ +throw exception() << message; \ +} \ +} while (false) + +#endif /* STORM_EXCEPTIONS_EXCEPTIONMACROS_H_ */ \ No newline at end of file diff --git a/src/exceptions/InvalidOperationException.h b/src/exceptions/InvalidOperationException.h new file mode 100644 index 000000000..728f7ff1c --- /dev/null +++ b/src/exceptions/InvalidOperationException.h @@ -0,0 +1,17 @@ +#ifndef STORM_EXCEPTIONS_INVALIDOPERATIONEXCEPTION_H_ +#define STORM_EXCEPTIONS_INVALIDOPERATIONEXCEPTION_H_ + +#include "src/exceptions/BaseException.h" + +namespace storm { + + namespace exceptions { + + /*! + * @brief This exception is thrown when an operation is invalid in this context + */ + STORM_EXCEPTION_DEFINE_NEW(InvalidOperationException) + + } // namespace exceptions +} // namespace storm +#endif // STORM_EXCEPTIONS_INVALIDOPERATIONEXCEPTION_H_ diff --git a/src/exceptions/InvalidTypeException.h b/src/exceptions/InvalidTypeException.h new file mode 100644 index 000000000..de2874218 --- /dev/null +++ b/src/exceptions/InvalidTypeException.h @@ -0,0 +1,18 @@ +#ifndef STORM_EXCEPTIONS_INVALIDTYPEEXCEPTION_H_ +#define STORM_EXCEPTIONS_INVALIDTYPEEXCEPTION_H_ + +#include "src/exceptions/BaseException.h" + +namespace storm { + + namespace exceptions { + + /*! + * @brief This exception is thrown when a type is invalid in this context + */ + STORM_EXCEPTION_DEFINE_NEW(InvalidTypeException) + + } // namespace exceptions + +} // namespace storm +#endif // STORM_EXCEPTIONS_INVALIDTYPEEXCEPTION_H_ diff --git a/src/ir/Assignment.cpp b/src/ir/Assignment.cpp deleted file mode 100644 index a50847afb..000000000 --- a/src/ir/Assignment.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Assignment.cpp - * - * Created on: 12.01.2013 - * Author: Christian Dehnert - */ - -#include <sstream> - -#include "Assignment.h" -#include "src/parser/prismparser/VariableState.h" - -namespace storm { - namespace ir { - - Assignment::Assignment() : variableName(), expression() { - // Nothing to do here. - } - - Assignment::Assignment(std::string const& variableName, std::unique_ptr<storm::ir::expressions::BaseExpression>&& expression) - : variableName(variableName), expression(std::move(expression)) { - // Nothing to do here. - } - - Assignment::Assignment(Assignment const& oldAssignment, std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) - : variableName(oldAssignment.variableName), expression(oldAssignment.expression->clone(renaming, variableState)) { - auto renamingPair = renaming.find(oldAssignment.variableName); - if (renamingPair != renaming.end()) { - this->variableName = renamingPair->second; - } - } - - Assignment::Assignment(Assignment const& otherAssignment) : variableName(otherAssignment.variableName), expression() { - if (otherAssignment.expression != nullptr) { - expression = otherAssignment.expression->clone(); - } - } - - Assignment& Assignment::operator=(Assignment const& otherAssignment) { - if (this != &otherAssignment) { - this->variableName = otherAssignment.variableName; - this->expression = otherAssignment.expression->clone(); - } - - return *this; - } - - std::string const& Assignment::getVariableName() const { - return variableName; - } - - std::unique_ptr<storm::ir::expressions::BaseExpression> const& Assignment::getExpression() const { - return expression; - } - - std::string Assignment::toString() const { - std::stringstream result; - result << "(" << variableName << "' = " << expression->toString() << ")"; - return result.str(); - } - - } // namespace ir -} // namespace storm diff --git a/src/ir/Assignment.h b/src/ir/Assignment.h deleted file mode 100644 index 12d96a413..000000000 --- a/src/ir/Assignment.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Assignment.h - * - * Created on: 06.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_ASSIGNMENT_H_ -#define STORM_IR_ASSIGNMENT_H_ - -#include <memory> - -#include "expressions/BaseExpression.h" - -namespace storm { - - namespace parser { - namespace prism { - class VariableState; - } // namespace prismparser - } // namespace parser - - namespace ir { - - /*! - * A class representing the assignment of an expression to a variable. - */ - class Assignment { - public: - /*! - * Default constructor. Creates an empty assignment. - */ - Assignment(); - - /*! - * Constructs an assignment using the given variable name and expression. - * - * @param variableName The variable that this assignment targets. - * @param expression The expression to assign to the variable. - */ - Assignment(std::string const& variableName, std::unique_ptr<storm::ir::expressions::BaseExpression>&& expression); - - /*! - * Creates a copy of the given assignment and performs the provided renaming. - * - * @param oldAssignment The assignment to copy. - * @param renaming A mapping from names that are to be renamed to the names they are to be - * replaced with. - * @param variableState An object knowing about the variables in the system. - */ - Assignment(Assignment const& oldAssignment, std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState); - - /*! - * Performs a deep-copy of the given assignment. - * - * @param otherAssignment The assignment to copy. - */ - Assignment(Assignment const& otherAssignment); - - /*! - * Performs a deep-copy of the given assignment and assigns it to the current one. - * - * @param otherAssignment The assignment to assign. - */ - Assignment& operator=(Assignment const& otherAssignment); - - /*! - * Retrieves the name of the variable that this assignment targets. - * - * @return The name of the variable that this assignment targets. - */ - std::string const& getVariableName() const; - - /*! - * Retrieves the expression that is assigned to the variable. - * - * @return The expression that is assigned to the variable. - */ - std::unique_ptr<storm::ir::expressions::BaseExpression> const& getExpression() const; - - /*! - * Retrieves a string representation of this assignment. - * @returns a string representation of this assignment. - */ - std::string toString() const; - - private: - // The name of the variable that this assignment targets. - std::string variableName; - - // The expression that is assigned to the variable. - std::unique_ptr<storm::ir::expressions::BaseExpression> expression; - }; - - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_ASSIGNMENT_H_ */ diff --git a/src/ir/BooleanVariable.cpp b/src/ir/BooleanVariable.cpp deleted file mode 100644 index 843655be9..000000000 --- a/src/ir/BooleanVariable.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * BooleanVariable.cpp - * - * Created on: 12.01.2013 - * Author: Christian Dehnert - */ - -#include <sstream> - -#include "BooleanVariable.h" -#include "src/parser/prismparser/VariableState.h" - -namespace storm { - namespace ir { - - BooleanVariable::BooleanVariable() : Variable() { - // Nothing to do here. - } - - BooleanVariable::BooleanVariable(uint_fast64_t localIndex, uint_fast64_t globalIndex, std::string const& variableName, std::unique_ptr<storm::ir::expressions::BaseExpression>&& initialValue) - : Variable(localIndex, globalIndex, variableName, std::move(initialValue)) { - // Nothing to do here. - } - - BooleanVariable::BooleanVariable(BooleanVariable const& oldVariable, std::string const& newName, uint_fast64_t newGlobalIndex, std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) - : Variable(oldVariable, newName, newGlobalIndex, renaming, variableState) { - // Nothing to do here. - } - - BooleanVariable& BooleanVariable::operator=(BooleanVariable const& otherVariable) { - if (this != &otherVariable) { - Variable::operator=(otherVariable); - } - - return *this; - } - - std::string BooleanVariable::toString() const { - std::stringstream result; - result << this->getName() << ": bool"; - if (this->getInitialValue() != nullptr) { - result << " init " << this->getInitialValue()->toString(); - } - result << ";"; - return result.str(); - } - - } // namespace ir -} // namespace storm diff --git a/src/ir/BooleanVariable.h b/src/ir/BooleanVariable.h deleted file mode 100644 index b968d7890..000000000 --- a/src/ir/BooleanVariable.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * BooleanVariable.h - * - * Created on: 08.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_BOOLEANVARIABLE_H_ -#define STORM_IR_BOOLEANVARIABLE_H_ - -#include <memory> -#include <map> - -#include "src/ir/Variable.h" - -namespace storm { - - namespace parser { - namespace prism { - class VariableState; - } // namespace prismparser - } // namespace parser - - namespace ir { - - /*! - * A class representing a boolean variable. - */ - class BooleanVariable : public Variable { - public: - /*! - * Default constructor. Creates a boolean variable without a name. - */ - BooleanVariable(); - - /*! - * Creates a boolean variable with the given name and the given initial value. - * - * @param localIndex A module-local unique index for the variable. - * @param globalIndex A globally unique index for the variable. - * @param variableName The name of the variable. - * @param initialValue The expression that defines the initial value of the variable. - */ - BooleanVariable(uint_fast64_t localIndex, uint_fast64_t globalIndex, std::string const& variableName, std::unique_ptr<storm::ir::expressions::BaseExpression>&& initialValue = nullptr); - - /*! - * Creates a copy of the given boolean variable and performs the provided renaming. - * - * @param oldVariable The variable to copy. - * @param newName New name of this variable. - * @param newGlobalIndex The new global index of the variable. - * @param renaming A mapping from names that are to be renamed to the names they are to be - * replaced with. - * @param variableState An object knowing about the variables in the system. - */ - BooleanVariable(BooleanVariable const& oldVariable, std::string const& newName, uint_fast64_t newGlobalIndex, std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState); - - BooleanVariable& operator=(BooleanVariable const& otherVariable); - - /*! - * Retrieves a string representation of this variable. - * @returns a string representation of this variable. - */ - std::string toString() const; - }; - - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_BOOLEANVARIABLE_H_ */ diff --git a/src/ir/Command.cpp b/src/ir/Command.cpp deleted file mode 100644 index dbb1eef64..000000000 --- a/src/ir/Command.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Command.cpp - * - * Created on: 12.01.2013 - * Author: Christian Dehnert - */ - -#include <sstream> -#include <iostream> - -#include "Command.h" -#include "src/parser/prismparser/VariableState.h" - -namespace storm { - namespace ir { - - Command::Command() : actionName(), guardExpression(), updates(), globalIndex() { - // Nothing to do here. - } - - Command::Command(uint_fast64_t globalIndex, std::string const& actionName, std::unique_ptr<storm::ir::expressions::BaseExpression>&& guardExpression, std::vector<storm::ir::Update> const& updates) - : actionName(actionName), guardExpression(std::move(guardExpression)), updates(updates), globalIndex(globalIndex) { - // Nothing to do here. - } - - Command::Command(Command const& oldCommand, uint_fast64_t newGlobalIndex, std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState& variableState) - : actionName(oldCommand.getActionName()), guardExpression(oldCommand.guardExpression->clone(renaming, variableState)), globalIndex(newGlobalIndex) { - auto renamingPair = renaming.find(this->actionName); - if (renamingPair != renaming.end()) { - this->actionName = renamingPair->second; - } - this->updates.reserve(oldCommand.getNumberOfUpdates()); - for (Update const& update : oldCommand.updates) { - this->updates.emplace_back(update, variableState.getNextGlobalUpdateIndex(), renaming, variableState); - variableState.nextGlobalUpdateIndex++; - } - } - - Command::Command(Command const& otherCommand) : actionName(otherCommand.actionName), guardExpression(), updates(otherCommand.updates), globalIndex(otherCommand.globalIndex) { - if (otherCommand.guardExpression != nullptr) { - guardExpression = otherCommand.guardExpression->clone(); - } - } - - Command& Command::operator=(Command const& otherCommand) { - if (this != &otherCommand) { - this->actionName = otherCommand.actionName; - this->guardExpression = otherCommand.guardExpression->clone(); - this->updates = otherCommand.updates; - this->globalIndex = otherCommand.globalIndex; - } - - return *this; - } - - std::string const& Command::getActionName() const { - return this->actionName; - } - - std::unique_ptr<storm::ir::expressions::BaseExpression> const& Command::getGuard() const { - return guardExpression; - } - - uint_fast64_t Command::getNumberOfUpdates() const { - return this->updates.size(); - } - - storm::ir::Update const& Command::getUpdate(uint_fast64_t index) const { - return this->updates[index]; - } - - std::vector<storm::ir::Update> const& Command::getUpdates() const { - return this->updates; - } - - uint_fast64_t Command::getGlobalIndex() const { - return this->globalIndex; - } - - std::string Command::toString() const { - std::stringstream result; - result << "[" << actionName << "] " << guardExpression->toString() << " -> "; - for (uint_fast64_t i = 0; i < updates.size(); ++i) { - result << updates[i].toString(); - if (i < updates.size() - 1) { - result << " + "; - } - } - result << ";"; - return result.str(); - } - - } // namespace ir -} // namespace storm diff --git a/src/ir/Command.h b/src/ir/Command.h deleted file mode 100644 index 04efe8b86..000000000 --- a/src/ir/Command.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Command.h - * - * Created on: 06.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_COMMAND_H_ -#define STORM_IR_COMMAND_H_ - -#include <vector> -#include <string> -#include <map> - -#include "expressions/BaseExpression.h" -#include "Update.h" - -namespace storm { - - namespace parser { - namespace prism { - class VariableState; - } // namespace prismparser - } // namespace parser - - namespace ir { - - /*! - * A class representing a command. - */ - class Command { - public: - /*! - * Default constructor. Creates a a command without name, guard and updates. - */ - Command(); - - /*! - * Creates a command with the given name, guard and updates. - * - * @param globalIndex The global index of the command. - * @param actionName The action name of the command. - * @param guardExpression the expression that defines the guard of the command. - * @param updates A list of updates that is associated with this command. - */ - Command(uint_fast64_t globalIndex, std::string const& actionName, std::unique_ptr<storm::ir::expressions::BaseExpression>&& guardExpression, std::vector<storm::ir::Update> const& updates); - - /*! - * Creates a copy of the given command and performs the provided renaming. - * - * @param oldCommand The command to copy. - * @param newGlobalIndex The global index of the copy of the command. - * @param renaming A mapping from names that are to be renamed to the names they are to be - * replaced with. - * @param variableState An object knowing about the variables in the system. - */ - Command(Command const& oldCommand, uint_fast64_t newGlobalIndex, std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState& variableState); - - /*! - * Performs a deep-copy of the given command. - * - * @param otherCommand The command to copy. - */ - Command(Command const& otherCommand); - - Command& operator=(Command const& otherCommand); - - /*! - * Retrieves the action name of this command. - * - * @return The action name of this command. - */ - std::string const& getActionName() const; - - /*! - * Retrieves a reference to the guard of the command. - * - * @return A reference to the guard of the command. - */ - std::unique_ptr<storm::ir::expressions::BaseExpression> const& getGuard() const; - - /*! - * Retrieves the number of updates associated with this command. - * - * @return The number of updates associated with this command. - */ - uint_fast64_t getNumberOfUpdates() const; - - /*! - * Retrieves a reference to the update with the given index. - * - * @return A reference to the update with the given index. - */ - storm::ir::Update const& getUpdate(uint_fast64_t index) const; - - /*! - * Retrieves a vector of all updates associated with this command. - * - * @return A vector of updates associated with this command. - */ - std::vector<storm::ir::Update> const& getUpdates() const; - - /*! - * Retrieves the global index of the command, that is, a unique index over all modules. - * - * @return The global index of the command. - */ - uint_fast64_t getGlobalIndex() const; - - /*! - * Retrieves a string representation of this command. - * - * @return A string representation of this command. - */ - std::string toString() const; - - private: - // The name of the command. - std::string actionName; - - // The expression that defines the guard of the command. - std::unique_ptr<storm::ir::expressions::BaseExpression> guardExpression; - - // The list of updates of the command. - std::vector<storm::ir::Update> updates; - - // The global index of the command. - uint_fast64_t globalIndex; - }; - - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_COMMAND_H_ */ diff --git a/src/ir/IR.h b/src/ir/IR.h deleted file mode 100644 index 80a794002..000000000 --- a/src/ir/IR.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * IR.h - * - * Created on: 06.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_IR_H_ -#define STORM_IR_IR_H_ - -// Bundle all headers to make it easy to include them. -#include "expressions/Expressions.h" -#include "Assignment.h" -#include "Update.h" -#include "Command.h" -#include "Variable.h" -#include "BooleanVariable.h" -#include "IntegerVariable.h" -#include "Module.h" -#include "StateReward.h" -#include "TransitionReward.h" -#include "RewardModel.h" -#include "Program.h" - -#endif /* STORM_IR_IR_H_ */ diff --git a/src/ir/IntegerVariable.cpp b/src/ir/IntegerVariable.cpp deleted file mode 100644 index b98b30d07..000000000 --- a/src/ir/IntegerVariable.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * IntegerVariable.cpp - * - * Created on: 12.01.2013 - * Author: Christian Dehnert - */ - -#include <sstream> -#include <iostream> - -#include "IntegerVariable.h" -#include "src/parser/prismparser/VariableState.h" - -namespace storm { - namespace ir { - - IntegerVariable::IntegerVariable() : lowerBound(), upperBound() { - // Nothing to do here. - } - - IntegerVariable::IntegerVariable(uint_fast64_t localIndex, uint_fast64_t globalIndex, std::string const& variableName, std::unique_ptr<storm::ir::expressions::BaseExpression>&& lowerBound, std::unique_ptr<storm::ir::expressions::BaseExpression>&& upperBound, std::unique_ptr<storm::ir::expressions::BaseExpression>&& initialValue) - : Variable(localIndex, globalIndex, variableName, std::move(initialValue)), lowerBound(std::move(lowerBound)), upperBound(std::move(upperBound)) { - // Nothing to do here. - } - - IntegerVariable::IntegerVariable(IntegerVariable const& oldVariable, std::string const& newName, uint_fast64_t newGlobalIndex, std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) - : Variable(oldVariable, newName, newGlobalIndex, renaming, variableState), lowerBound(oldVariable.lowerBound->clone(renaming, variableState)), upperBound(oldVariable.upperBound->clone(renaming, variableState)) { - // Nothing to do here. - } - - IntegerVariable::IntegerVariable(IntegerVariable const& otherVariable) : Variable(otherVariable.getLocalIndex(), otherVariable.getGlobalIndex(), otherVariable.getName(), nullptr), lowerBound(), upperBound() { - if (otherVariable.getInitialValue() != nullptr) { - setInitialValue(otherVariable.getInitialValue()->clone()); - } - if (otherVariable.lowerBound != nullptr) { - lowerBound = otherVariable.lowerBound->clone(); - } - if (otherVariable.upperBound != nullptr) { - upperBound = otherVariable.upperBound->clone(); - } - } - - IntegerVariable& IntegerVariable::operator=(IntegerVariable const& otherVariable) { - if (this != &otherVariable) { - Variable::operator=(otherVariable); - this->lowerBound = otherVariable.lowerBound->clone(); - this->upperBound = otherVariable.upperBound->clone(); - } - - return *this; - } - - std::unique_ptr<storm::ir::expressions::BaseExpression> const& IntegerVariable::getLowerBound() const { - return this->lowerBound; - } - - std::unique_ptr<storm::ir::expressions::BaseExpression> const& IntegerVariable::getUpperBound() const { - return this->upperBound; - } - - std::string IntegerVariable::toString() const { - std::stringstream result; - result << this->getName() << ": [" << lowerBound->toString() << ".." << upperBound->toString() << "]"; - if (this->getInitialValue() != nullptr) { - result << " init " + this->getInitialValue()->toString(); - } - result << ";"; - return result.str(); - } - - } // namespace ir -} // namespace storm diff --git a/src/ir/IntegerVariable.h b/src/ir/IntegerVariable.h deleted file mode 100644 index 930e1d0b8..000000000 --- a/src/ir/IntegerVariable.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * IntegerVariable.h - * - * Created on: 08.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_INTEGERVARIABLE_H_ -#define STORM_IR_INTEGERVARIABLE_H_ - -#include <memory> - -#include "src/ir/Variable.h" -#include "expressions/BaseExpression.h" - -namespace storm { - - namespace parser { - namespace prism { - class VariableState; - } // namespace prismparser - } // namespace parser - - namespace ir { - - /*! - * A class representing an integer variable. - */ - class IntegerVariable : public Variable { - public: - /*! - * Default constructor. Creates an integer variable without a name and lower and upper bounds. - */ - IntegerVariable(); - - /*! - * Creates a boolean variable with the given name and the given initial value. - * - * @param localIndex A module-local unique index for the variable. - * @param globalIndex A globally unique index for the variable. - * @param variableName The name of the variable. - * @param lowerBound the lower bound of the domain of the variable. - * @param upperBound the upper bound of the domain of the variable. - * @param initialValue the expression that defines the initial value of the variable. - */ - IntegerVariable(uint_fast64_t localIndex, uint_fast64_t globalIndex, std::string const& variableName, std::unique_ptr<storm::ir::expressions::BaseExpression>&& lowerBound, std::unique_ptr<storm::ir::expressions::BaseExpression>&& upperBound, std::unique_ptr<storm::ir::expressions::BaseExpression>&& initialValue = nullptr); - - /*! - * Creates a copy of the given integer variable and performs the provided renaming. - * - * @param oldVariable The variable to copy. - * @param newName New name of this variable. - * @param newGlobalIndex The new global index of the variable. - * @param renaming A mapping from names that are to be renamed to the names they are to be - * replaced with. - * @param variableState An object knowing about the variables in the system. - */ - IntegerVariable(IntegerVariable const& oldVariable, std::string const& newName, uint_fast64_t newGlobalIndex, std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState); - - /*! - * Performs a deep-copy of the given variable. - * - * @param otherVariable The variable to copy. - */ - IntegerVariable(IntegerVariable const& otherVariable); - - IntegerVariable& operator=(IntegerVariable const& otherVariable); - - /*! - * Retrieves the lower bound for this integer variable. - * @returns the lower bound for this integer variable. - */ - std::unique_ptr<storm::ir::expressions::BaseExpression> const& getLowerBound() const; - - /*! - * Retrieves the upper bound for this integer variable. - * @returns the upper bound for this integer variable. - */ - std::unique_ptr<storm::ir::expressions::BaseExpression> const& getUpperBound() const; - - /*! - * Retrieves a string representation of this variable. - * @returns a string representation of this variable. - */ - std::string toString() const; - - private: - // The lower bound of the domain of the variable. - std::unique_ptr<storm::ir::expressions::BaseExpression> lowerBound; - - // The upper bound of the domain of the variable. - std::unique_ptr<storm::ir::expressions::BaseExpression> upperBound; - }; - - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_INTEGERVARIABLE_H_ */ diff --git a/src/ir/Module.cpp b/src/ir/Module.cpp deleted file mode 100644 index 6b67ee579..000000000 --- a/src/ir/Module.cpp +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Module.cpp - * - * Created on: 12.01.2013 - * Author: Christian Dehnert - */ - -#include <sstream> -#include <iostream> -#include "utility/OsDetection.h" - -#include "Module.h" -#include "src/parser/prismparser/VariableState.h" -#include "src/exceptions/OutOfRangeException.h" -#include "src/exceptions/InvalidArgumentException.h" - -#include "log4cplus/logger.h" -#include "log4cplus/loggingmacros.h" -extern log4cplus::Logger logger; - -namespace storm { - namespace ir { - - Module::Module() : moduleName(), booleanVariables(), integerVariables(), booleanVariableToLocalIndexMap(), - integerVariableToLocalIndexMap(), commands(), actions(), actionsToCommandIndexMap() { - // Nothing to do here. - } - - Module::Module(std::string const& moduleName, - std::vector<storm::ir::BooleanVariable> const& booleanVariables, - std::vector<storm::ir::IntegerVariable> const& integerVariables, - std::map<std::string, uint_fast64_t> const& booleanVariableToLocalIndexMap, - std::map<std::string, uint_fast64_t> const& integerVariableToLocalIndexMap, - std::vector<storm::ir::Command> const& commands) - : moduleName(moduleName), booleanVariables(booleanVariables), integerVariables(integerVariables), - booleanVariableToLocalIndexMap(booleanVariableToLocalIndexMap), - integerVariableToLocalIndexMap(integerVariableToLocalIndexMap), commands(commands), actions(), actionsToCommandIndexMap() { - // Initialize the internal mappings for fast information retrieval. - this->collectActions(); - } - - Module::Module(Module const& oldModule, std::string const& newModuleName, std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState& variableState) - : moduleName(newModuleName), booleanVariableToLocalIndexMap(oldModule.booleanVariableToLocalIndexMap), integerVariableToLocalIndexMap(oldModule.integerVariableToLocalIndexMap) { - LOG4CPLUS_TRACE(logger, "Start renaming " << oldModule.getName() << " to " << moduleName << "."); - - // Iterate over boolean variables and rename them. If a variable was not renamed, this is an error and an exception - // is thrown. - this->booleanVariables.reserve(oldModule.getNumberOfBooleanVariables()); - for (BooleanVariable const& booleanVariable : oldModule.booleanVariables) { - auto renamingPair = renaming.find(booleanVariable.getName()); - if (renamingPair == renaming.end()) { - LOG4CPLUS_ERROR(logger, "Boolean variable " << moduleName << "." << booleanVariable.getName() << " was not renamed."); - throw storm::exceptions::InvalidArgumentException() << "Boolean variable " << moduleName << "." << booleanVariable.getName() << " was not renamed."; - } else { - uint_fast64_t globalIndex = variableState.addBooleanVariable(renamingPair->second); - this->booleanVariables.emplace_back(booleanVariable, renamingPair->second, globalIndex, renaming, variableState); - } - } - // Now do the same for the integer variables. - this->integerVariables.reserve(oldModule.getNumberOfIntegerVariables()); - for (IntegerVariable const& integerVariable : oldModule.integerVariables) { - auto renamingPair = renaming.find(integerVariable.getName()); - if (renamingPair == renaming.end()) { - LOG4CPLUS_ERROR(logger, "Integer variable " << moduleName << "." << integerVariable.getName() << " was not renamed."); - throw storm::exceptions::InvalidArgumentException() << "Integer variable " << moduleName << "." << integerVariable.getName() << " was not renamed."; - } else { - uint_fast64_t globalIndex = variableState.addIntegerVariable(renamingPair->second); - this->integerVariables.emplace_back(integerVariable, renamingPair->second, globalIndex, renaming, variableState); - } - } - - // Now we are ready to clone all commands and rename them if requested. - this->commands.reserve(oldModule.getNumberOfCommands()); - for (Command const& command : oldModule.commands) { - this->commands.emplace_back(command, variableState.getNextGlobalCommandIndex(), renaming, variableState); - variableState.nextGlobalCommandIndex++; - } - this->collectActions(); - - LOG4CPLUS_TRACE(logger, "Finished renaming..."); - } - - uint_fast64_t Module::getNumberOfBooleanVariables() const { - return this->booleanVariables.size(); - } - - storm::ir::BooleanVariable const& Module::getBooleanVariable(uint_fast64_t index) const { - return this->booleanVariables[index]; - } - - storm::ir::BooleanVariable const& Module::getBooleanVariable(std::string const& variableName) const { - uint_fast64_t index = this->getBooleanVariableIndex(variableName); - return this->booleanVariables[index]; - } - - uint_fast64_t Module::getNumberOfIntegerVariables() const { - return this->integerVariables.size(); - } - - storm::ir::IntegerVariable const& Module::getIntegerVariable(uint_fast64_t index) const { - return this->integerVariables[index]; - } - - storm::ir::IntegerVariable const& Module::getIntegerVariable(std::string const& variableName) const { - uint_fast64_t index = this->getIntegerVariableIndex(variableName); - return this->integerVariables[index]; - } - - uint_fast64_t Module::getNumberOfCommands() const { - return this->commands.size(); - } - - uint_fast64_t Module::getBooleanVariableIndex(std::string const& variableName) const { - auto it = booleanVariableToLocalIndexMap.find(variableName); - if (it != booleanVariableToLocalIndexMap.end()) { - return it->second; - } - LOG4CPLUS_ERROR(logger, "Cannot retrieve index of unknown boolean variable " << variableName << "."); - throw storm::exceptions::InvalidArgumentException() << "Cannot retrieve index of unknown boolean variable " << variableName << "."; - } - - uint_fast64_t Module::getIntegerVariableIndex(std::string const& variableName) const { - auto it = integerVariableToLocalIndexMap.find(variableName); - if (it != integerVariableToLocalIndexMap.end()) { - return it->second; - } - LOG4CPLUS_ERROR(logger, "Cannot retrieve index of unknown integer variable " << variableName << "."); - throw storm::exceptions::InvalidArgumentException() << "Cannot retrieve index of unknown integer variable " << variableName << "."; - } - - storm::ir::Command const& Module::getCommand(uint_fast64_t index) const { - return this->commands[index]; - } - - std::string const& Module::getName() const { - return this->moduleName; - } - - std::string Module::toString() const { - std::stringstream result; - result << "module " << moduleName << std::endl; - for (auto variable : booleanVariables) { - result << "\t" << variable.toString() << std::endl; - } - for (auto variable : integerVariables) { - result << "\t" << variable.toString() << std::endl; - } - for (auto command : commands) { - result << "\t" << command.toString() << std::endl; - } - result << "endmodule" << std::endl; - return result.str(); - } - - std::set<std::string> const& Module::getActions() const { - return this->actions; - } - - bool Module::hasAction(std::string const& action) const { - auto const& actionEntry = this->actions.find(action); - if (actionEntry != this->actions.end()) { - return true; - } - return false; - } - - std::set<uint_fast64_t> const& Module::getCommandsByAction(std::string const& action) const { - auto actionsCommandSetPair = this->actionsToCommandIndexMap.find(action); - if (actionsCommandSetPair != this->actionsToCommandIndexMap.end()) { - return actionsCommandSetPair->second; - } - LOG4CPLUS_ERROR(logger, "Action name '" << action << "' does not exist in module."); - throw storm::exceptions::OutOfRangeException() << "Action name '" << action << "' does not exist in module."; - } - - void Module::collectActions() { - for (unsigned int id = 0; id < this->commands.size(); id++) { - std::string const& action = this->commands[id].getActionName(); - if (action != "") { - if (this->actionsToCommandIndexMap.find(action) == this->actionsToCommandIndexMap.end()) { - this->actionsToCommandIndexMap.emplace(action, std::set<uint_fast64_t>()); - } - this->actionsToCommandIndexMap[action].insert(id); - this->actions.insert(action); - } - } - } - - void Module::restrictCommands(boost::container::flat_set<uint_fast64_t> const& indexSet) { - std::vector<storm::ir::Command> newCommands; - for (auto const& command : commands) { - if (indexSet.find(command.getGlobalIndex()) != indexSet.end()) { - newCommands.push_back(std::move(command)); - } - } - commands = std::move(newCommands); - } - - } // namespace ir -} // namespace storm diff --git a/src/ir/Module.h b/src/ir/Module.h deleted file mode 100644 index e6762bb8d..000000000 --- a/src/ir/Module.h +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Module.h - * - * Created on: 04.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_MODULE_H_ -#define STORM_IR_MODULE_H_ - -#include "utility/OsDetection.h" -#include <boost/container/flat_set.hpp> - -#ifdef LINUX -#include <boost/container/map.hpp> -#endif -#include <map> - -#include <set> -#include <string> -#include <vector> -#include <memory> - -#include "BooleanVariable.h" -#include "IntegerVariable.h" -#include "Command.h" -#include "expressions/VariableExpression.h" - -namespace storm { - - namespace parser { - namespace prism { - class VariableState; - } // namespace prismparser - } // namespace parser - - namespace ir { - - /*! - * A class representing a module. - */ - class Module { - public: - /*! - * Default constructor. Creates an empty module. - */ - Module(); - - /*! - * Creates a module with the given name, variables and commands. - * - * @param moduleName The name of the module. - * @param booleanVariables The boolean variables defined by the module. - * @param integerVariables The integer variables defined by the module. - * @param booleanVariableToLocalIndexMap A mapping of boolean variables to local (i.e. module-local) indices. - * @param integerVariableToLocalIndexMap A mapping of integer variables to local (i.e. module-local) indices. - * @param commands The commands of the module. - */ - Module(std::string const& moduleName, std::vector<storm::ir::BooleanVariable> const& booleanVariables, - std::vector<storm::ir::IntegerVariable> const& integerVariables, - std::map<std::string, uint_fast64_t> const& booleanVariableToLocalIndexMap, - std::map<std::string, uint_fast64_t> const& integerVariableToLocalIndexMap, - std::vector<storm::ir::Command> const& commands); - - /*! - * Special copy constructor, implementing the module renaming functionality. - * This will create a new module having all identifiers renamed according to the given map. - * - * @param oldModule The module to be copied. - * @param newModuleName The name of the new module. - * @param renaming A mapping of identifiers to the new identifiers they are to be replaced with. - * @param variableState An object knowing about the variables in the system. - */ - Module(Module const& oldModule, std::string const& newModuleName, std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState& variableState); - - /*! - * Retrieves the number of boolean variables in the module. - * - * @return the number of boolean variables in the module. - */ - uint_fast64_t getNumberOfBooleanVariables() const; - - /*! - * Retrieves a reference to the boolean variable with the given index. - * - * @return A reference to the boolean variable with the given index. - */ - storm::ir::BooleanVariable const& getBooleanVariable(uint_fast64_t index) const; - - /*! - * Retrieves a reference to the boolean variable with the given name. - * - * @return A reference to the boolean variable with the given name. - */ - storm::ir::BooleanVariable const& getBooleanVariable(std::string const& variableName) const; - - /*! - * Retrieves the number of integer variables in the module. - * - * @return The number of integer variables in the module. - */ - uint_fast64_t getNumberOfIntegerVariables() const; - - /*! - * Retrieves a reference to the integer variable with the given index. - * - * @return A reference to the integer variable with the given index. - */ - storm::ir::IntegerVariable const& getIntegerVariable(uint_fast64_t index) const; - - /*! - * Retrieves a reference to the boolean variable with the given name. - * - * @return A reference to the boolean variable with the given name. - */ - storm::ir::IntegerVariable const& getIntegerVariable(std::string const& variableName) const; - - /*! - * Retrieves the number of commands of this module. - * - * @return the number of commands of this module. - */ - uint_fast64_t getNumberOfCommands() const; - - /*! - * Retrieves the index of the boolean variable with the given name. - * - * @param variableName The name of the boolean variable whose index to retrieve. - * @return The index of the boolean variable with the given name. - */ - uint_fast64_t getBooleanVariableIndex(std::string const& variableName) const; - - /*! - * Retrieves the index of the integer variable with the given name. - * - * @param variableName The name of the integer variable whose index to retrieve. - * @return The index of the integer variable with the given name. - */ - uint_fast64_t getIntegerVariableIndex(std::string const& variableName) const; - - /*! - * Retrieves a reference to the command with the given index. - * - * @return A reference to the command with the given index. - */ - storm::ir::Command const& getCommand(uint_fast64_t index) const; - - /*! - * Retrieves the name of the module. - * - * @return The name of the module. - */ - std::string const& getName() const; - - /*! - * Retrieves a string representation of this module. - * - * @return a string representation of this module. - */ - std::string toString() const; - - /*! - * Retrieves the set of actions present in this module. - * - * @return the set of actions present in this module. - */ - std::set<std::string> const& getActions() const; - - /*! - * Retrieves whether or not this module contains a command labeled with the given action. - * - * @param action The action name to look for in this module. - * @return True if the module has at least one command labeled with the given action. - */ - bool hasAction(std::string const& action) const; - - /*! - * Retrieves the indices of all commands within this module that are labelled by the given action. - * - * @param action The action with which the commands have to be labelled. - * @return A set of indices of commands that are labelled with the given action. - */ - std::set<uint_fast64_t> const& getCommandsByAction(std::string const& action) const; - - /*! - * Deletes all commands with indices not in the given set from the module. - * - * @param indexSet The set of indices for which to keep the commands. - */ - void restrictCommands(boost::container::flat_set<uint_fast64_t> const& indexSet); - private: - - /*! - * Computes the locally maintained mappings for fast data retrieval. - */ - void collectActions(); - - // The name of the module. - std::string moduleName; - - // A list of boolean variables. - std::vector<storm::ir::BooleanVariable> booleanVariables; - - // A list of integer variables. - std::vector<storm::ir::IntegerVariable> integerVariables; - - // A map of boolean variable names to their index. - std::map<std::string, uint_fast64_t> booleanVariableToLocalIndexMap; - - // A map of integer variable names to their index. - std::map<std::string, uint_fast64_t> integerVariableToLocalIndexMap; - - // The commands associated with the module. - std::vector<storm::ir::Command> commands; - - // The set of actions present in this module. - std::set<std::string> actions; - - // A map of actions to the set of commands labeled with this action. -#ifdef LINUX - boost::container::map<std::string, std::set<uint_fast64_t>> actionsToCommandIndexMap; -#else - std::map<std::string, std::set<uint_fast64_t>> actionsToCommandIndexMap; -#endif - }; - - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_MODULE_H_ */ diff --git a/src/ir/Program.cpp b/src/ir/Program.cpp deleted file mode 100644 index 33c75c44f..000000000 --- a/src/ir/Program.cpp +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Program.cpp - * - * Created on: 12.01.2013 - * Author: Christian Dehnert - */ - -#include <sstream> -#include <iostream> - -#include "Program.h" -#include "exceptions/InvalidArgumentException.h" -#include "src/exceptions/OutOfRangeException.h" - -#include "log4cplus/logger.h" -#include "log4cplus/loggingmacros.h" -extern log4cplus::Logger logger; - -namespace storm { - namespace ir { - - Program::Program() : modelType(UNDEFINED), booleanUndefinedConstantExpressions(), integerUndefinedConstantExpressions(), doubleUndefinedConstantExpressions(), modules(), rewards(), actions(), actionsToModuleIndexMap(), variableToModuleIndexMap() { - // Nothing to do here. - } - - Program::Program(ModelType modelType, - std::map<std::string, std::unique_ptr<storm::ir::expressions::BooleanConstantExpression>> const& booleanUndefinedConstantExpressions, - std::map<std::string, std::unique_ptr<storm::ir::expressions::IntegerConstantExpression>> const& integerUndefinedConstantExpressions, - std::map<std::string, std::unique_ptr<storm::ir::expressions::DoubleConstantExpression>> const& doubleUndefinedConstantExpressions, - std::vector<BooleanVariable> const& globalBooleanVariables, - std::vector<IntegerVariable> const& globalIntegerVariables, - std::map<std::string, uint_fast64_t> const& globalBooleanVariableToIndexMap, - std::map<std::string, uint_fast64_t> const& globalIntegerVariableToIndexMap, - std::vector<storm::ir::Module> const& modules, - std::map<std::string, storm::ir::RewardModel> const& rewards, - std::map<std::string, std::unique_ptr<storm::ir::expressions::BaseExpression>> const& labels) - : modelType(modelType), globalBooleanVariables(globalBooleanVariables), globalIntegerVariables(globalIntegerVariables), - globalBooleanVariableToIndexMap(globalBooleanVariableToIndexMap), globalIntegerVariableToIndexMap(globalIntegerVariableToIndexMap), - modules(modules), rewards(rewards), actionsToModuleIndexMap(), variableToModuleIndexMap() { - - // Perform a deep-copy of the maps. - for (auto const& booleanUndefinedConstant : booleanUndefinedConstantExpressions) { - this->booleanUndefinedConstantExpressions[booleanUndefinedConstant.first] = std::unique_ptr<storm::ir::expressions::BooleanConstantExpression>(new storm::ir::expressions::BooleanConstantExpression(*booleanUndefinedConstant.second)); - } - for (auto const& integerUndefinedConstant : integerUndefinedConstantExpressions) { - this->integerUndefinedConstantExpressions[integerUndefinedConstant.first] = std::unique_ptr<storm::ir::expressions::IntegerConstantExpression>(new storm::ir::expressions::IntegerConstantExpression(*integerUndefinedConstant.second)); - } - for (auto const& doubleUndefinedConstant : doubleUndefinedConstantExpressions) { - this->doubleUndefinedConstantExpressions[doubleUndefinedConstant.first] = std::unique_ptr<storm::ir::expressions::DoubleConstantExpression>(new storm::ir::expressions::DoubleConstantExpression(*doubleUndefinedConstant.second)); - } - for (auto const& label : labels) { - this->labels[label.first] = label.second->clone(); - } - - // Now build the mapping from action names to module indices so that the lookup can later be performed quickly. - for (unsigned int moduleIndex = 0; moduleIndex < this->modules.size(); moduleIndex++) { - Module const& module = this->modules[moduleIndex]; - - for (auto const& action : module.getActions()) { - if (this->actionsToModuleIndexMap.count(action) == 0) { - this->actionsToModuleIndexMap[action] = std::set<uint_fast64_t>(); - } - this->actionsToModuleIndexMap[action].insert(moduleIndex); - this->actions.insert(action); - } - - // Put in the appropriate entries for the mapping from variable names to module index. - for (uint_fast64_t booleanVariableIndex = 0; booleanVariableIndex < module.getNumberOfBooleanVariables(); ++booleanVariableIndex) { - this->variableToModuleIndexMap[module.getBooleanVariable(booleanVariableIndex).getName()] = moduleIndex; - } - for (uint_fast64_t integerVariableIndex = 0; integerVariableIndex < module.getNumberOfIntegerVariables(); ++integerVariableIndex) { - this->variableToModuleIndexMap[module.getIntegerVariable(integerVariableIndex).getName()] = moduleIndex; - } - } - } - - Program::Program(Program const& otherProgram) : modelType(otherProgram.modelType), globalBooleanVariables(otherProgram.globalBooleanVariables), - globalIntegerVariables(otherProgram.globalIntegerVariables), globalBooleanVariableToIndexMap(otherProgram.globalBooleanVariableToIndexMap), - globalIntegerVariableToIndexMap(otherProgram.globalIntegerVariableToIndexMap), modules(otherProgram.modules), rewards(otherProgram.rewards), - actions(otherProgram.actions), actionsToModuleIndexMap(), variableToModuleIndexMap() { - // Perform deep-copy of the maps. - for (auto const& booleanUndefinedConstant : otherProgram.booleanUndefinedConstantExpressions) { - this->booleanUndefinedConstantExpressions[booleanUndefinedConstant.first] = std::unique_ptr<storm::ir::expressions::BooleanConstantExpression>(new storm::ir::expressions::BooleanConstantExpression(*booleanUndefinedConstant.second)); - } - for (auto const& integerUndefinedConstant : otherProgram.integerUndefinedConstantExpressions) { - this->integerUndefinedConstantExpressions[integerUndefinedConstant.first] = std::unique_ptr<storm::ir::expressions::IntegerConstantExpression>(new storm::ir::expressions::IntegerConstantExpression(*integerUndefinedConstant.second)); - } - for (auto const& doubleUndefinedConstant : otherProgram.doubleUndefinedConstantExpressions) { - this->doubleUndefinedConstantExpressions[doubleUndefinedConstant.first] = std::unique_ptr<storm::ir::expressions::DoubleConstantExpression>(new storm::ir::expressions::DoubleConstantExpression(*doubleUndefinedConstant.second)); - } - for (auto const& label : otherProgram.labels) { - this->labels[label.first] = label.second->clone(); - } - } - - Program& Program::operator=(Program const& otherProgram) { - if (this != &otherProgram) { - this->modelType = otherProgram.modelType; - this->globalBooleanVariables = otherProgram.globalBooleanVariables; - this->globalIntegerVariables = otherProgram.globalIntegerVariables; - this->globalBooleanVariableToIndexMap = otherProgram.globalBooleanVariableToIndexMap; - this->globalIntegerVariableToIndexMap = otherProgram.globalIntegerVariableToIndexMap; - this->modules = otherProgram.modules; - this->rewards = otherProgram.rewards; - this->actions = otherProgram.actions; - this->actionsToModuleIndexMap = otherProgram.actionsToModuleIndexMap; - this->variableToModuleIndexMap = otherProgram.variableToModuleIndexMap; - - // Perform deep-copy of the maps. - for (auto const& booleanUndefinedConstant : otherProgram.booleanUndefinedConstantExpressions) { - this->booleanUndefinedConstantExpressions[booleanUndefinedConstant.first] = std::unique_ptr<storm::ir::expressions::BooleanConstantExpression>(new storm::ir::expressions::BooleanConstantExpression(*booleanUndefinedConstant.second)); - } - for (auto const& integerUndefinedConstant : otherProgram.integerUndefinedConstantExpressions) { - this->integerUndefinedConstantExpressions[integerUndefinedConstant.first] = std::unique_ptr<storm::ir::expressions::IntegerConstantExpression>(new storm::ir::expressions::IntegerConstantExpression(*integerUndefinedConstant.second)); - } - for (auto const& doubleUndefinedConstant : otherProgram.doubleUndefinedConstantExpressions) { - this->doubleUndefinedConstantExpressions[doubleUndefinedConstant.first] = std::unique_ptr<storm::ir::expressions::DoubleConstantExpression>(new storm::ir::expressions::DoubleConstantExpression(*doubleUndefinedConstant.second)); - } - for (auto const& label : otherProgram.labels) { - this->labels[label.first] = label.second->clone(); - } - } - - return *this; - } - - Program::ModelType Program::getModelType() const { - return modelType; - } - - std::string Program::toString() const { - std::stringstream result; - switch (modelType) { - case UNDEFINED: result << "undefined"; break; - case DTMC: result << "dtmc"; break; - case CTMC: result << "ctmc"; break; - case MDP: result << "mdp"; break; - case CTMDP: result << "ctmdp"; break; - } - result << std::endl; - - for (auto const& element : booleanUndefinedConstantExpressions) { - result << "const bool " << element.second->toString() << ";" << std::endl; - } - for (auto const& element : integerUndefinedConstantExpressions) { - result << "const int " << element.second->toString() << ";" << std::endl; - } - for (auto const& element : doubleUndefinedConstantExpressions) { - result << "const double " << element.second->toString() << ";" << std::endl; - } - result << std::endl; - - for (auto const& element : globalBooleanVariables) { - result << "global " << element.toString() << std::endl; - } - for (auto const& element : globalIntegerVariables) { - result << "global " << element.toString() << std::endl; - } - result << std::endl; - - for (auto const& module : modules) { - result << module.toString() << std::endl; - } - - for (auto const& rewardModel : rewards) { - result << rewardModel.second.toString() << std::endl; - } - - for (auto const& label : labels) { - result << "label \"" << label.first << "\" = " << label.second->toString() <<";" << std::endl; - } - - return result.str(); - } - - storm::ir::BooleanVariable const& Program::getGlobalBooleanVariable(uint_fast64_t index) const { - return this->globalBooleanVariables[index]; - } - - storm::ir::IntegerVariable const& Program::getGlobalIntegerVariable(uint_fast64_t index) const { - return this->globalIntegerVariables[index]; - } - - uint_fast64_t Program::getNumberOfModules() const { - return this->modules.size(); - } - - storm::ir::Module const& Program::getModule(uint_fast64_t index) const { - return this->modules[index]; - } - - std::set<std::string> const& Program::getActions() const { - return this->actions; - } - - std::set<uint_fast64_t> const& Program::getModulesByAction(std::string const& action) const { - auto actionModuleSetPair = this->actionsToModuleIndexMap.find(action); - if (actionModuleSetPair == this->actionsToModuleIndexMap.end()) { - LOG4CPLUS_ERROR(logger, "Action name '" << action << "' does not exist."); - throw storm::exceptions::OutOfRangeException() << "Action name '" << action << "' does not exist."; - } - return actionModuleSetPair->second; - } - - uint_fast64_t Program::getModuleIndexForVariable(std::string const& variableName) const { - auto variableNameToModuleIndexPair = this->variableToModuleIndexMap.find(variableName); - if (variableNameToModuleIndexPair != this->variableToModuleIndexMap.end()) { - return variableNameToModuleIndexPair->second; - } - throw storm::exceptions::OutOfRangeException() << "Variable '" << variableName << "' does not exist."; - } - - uint_fast64_t Program::getNumberOfGlobalBooleanVariables() const { - return this->globalBooleanVariables.size(); - } - - uint_fast64_t Program::getNumberOfGlobalIntegerVariables() const { - return this->globalIntegerVariables.size(); - } - - storm::ir::RewardModel const& Program::getRewardModel(std::string const& name) const { - auto nameRewardModelPair = this->rewards.find(name); - if (nameRewardModelPair == this->rewards.end()) { - LOG4CPLUS_ERROR(logger, "Reward model '" << name << "' does not exist."); - throw storm::exceptions::OutOfRangeException() << "Reward model '" << name << "' does not exist."; - } - return nameRewardModelPair->second; - } - - std::map<std::string, std::unique_ptr<storm::ir::expressions::BaseExpression>> const& Program::getLabels() const { - return this->labels; - } - - bool Program::hasUndefinedBooleanConstant(std::string const& constantName) const { - return this->booleanUndefinedConstantExpressions.find(constantName) != this->booleanUndefinedConstantExpressions.end(); - } - - std::unique_ptr<storm::ir::expressions::BooleanConstantExpression> const& Program::getUndefinedBooleanConstantExpression(std::string const& constantName) const { - auto constantExpressionPair = this->booleanUndefinedConstantExpressions.find(constantName); - if (constantExpressionPair != this->booleanUndefinedConstantExpressions.end()) { - return constantExpressionPair->second; - } else { - throw storm::exceptions::InvalidArgumentException() << "Unknown undefined boolean constant " << constantName << "."; - } - } - - bool Program::hasUndefinedIntegerConstant(std::string const& constantName) const { - return this->integerUndefinedConstantExpressions.find(constantName) != this->integerUndefinedConstantExpressions.end(); - } - - std::unique_ptr<storm::ir::expressions::IntegerConstantExpression> const& Program::getUndefinedIntegerConstantExpression(std::string const& constantName) const { - auto constantExpressionPair = this->integerUndefinedConstantExpressions.find(constantName); - if (constantExpressionPair != this->integerUndefinedConstantExpressions.end()) { - return constantExpressionPair->second; - } else { - throw storm::exceptions::InvalidArgumentException() << "Unknown undefined integer constant " << constantName << "."; - } - } - - bool Program::hasUndefinedDoubleConstant(std::string const& constantName) const { - return this->doubleUndefinedConstantExpressions.find(constantName) != this->doubleUndefinedConstantExpressions.end(); - } - - std::unique_ptr<storm::ir::expressions::DoubleConstantExpression> const& Program::getUndefinedDoubleConstantExpression(std::string const& constantName) const { - auto constantExpressionPair = this->doubleUndefinedConstantExpressions.find(constantName); - if (constantExpressionPair != this->doubleUndefinedConstantExpressions.end()) { - return constantExpressionPair->second; - } else { - throw storm::exceptions::InvalidArgumentException() << "Unknown undefined double constant " << constantName << "."; - } - } - - std::map<std::string, std::unique_ptr<storm::ir::expressions::BooleanConstantExpression>> const& Program::getBooleanUndefinedConstantExpressionsMap() const { - return this->booleanUndefinedConstantExpressions; - } - - std::map<std::string, std::unique_ptr<storm::ir::expressions::IntegerConstantExpression>> const& Program::getIntegerUndefinedConstantExpressionsMap() const { - return this->integerUndefinedConstantExpressions; - } - - std::map<std::string, std::unique_ptr<storm::ir::expressions::DoubleConstantExpression>> const& Program::getDoubleUndefinedConstantExpressionsMap() const { - return this->doubleUndefinedConstantExpressions; - } - - uint_fast64_t Program::getGlobalIndexOfBooleanVariable(std::string const& variableName) const { - return this->globalBooleanVariableToIndexMap.at(variableName); - } - - uint_fast64_t Program::getGlobalIndexOfIntegerVariable(std::string const& variableName) const { - return this->globalIntegerVariableToIndexMap.at(variableName); - } - - void Program::restrictCommands(boost::container::flat_set<uint_fast64_t> const& indexSet) { - for (auto& module : modules) { - module.restrictCommands(indexSet); - } - } - - } // namespace ir -} // namepsace storm diff --git a/src/ir/Program.h b/src/ir/Program.h deleted file mode 100644 index 8ce96bc87..000000000 --- a/src/ir/Program.h +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Program.h - * - * Created on: 04.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_PROGRAM_H_ -#define STORM_IR_PROGRAM_H_ - -#include <map> -#include <vector> -#include <memory> -#include <set> -#include <boost/container/flat_set.hpp> - -#include "expressions/BaseExpression.h" -#include "expressions/BooleanConstantExpression.h" -#include "expressions/IntegerConstantExpression.h" -#include "expressions/DoubleConstantExpression.h" -#include "Module.h" -#include "RewardModel.h" - -namespace storm { - namespace ir { - - /*! - * A class representing a program. - */ - class Program { - public: - - /*! - * An enum for the different model types. - */ - enum ModelType {UNDEFINED, DTMC, CTMC, MDP, CTMDP}; - - /*! - * Default constructor. Creates an empty program. - */ - Program(); - - /*! - * Creates a program with the given model type, undefined constants, modules, rewards and labels. - * - * @param modelType The type of the model that this program gives rise to. - * @param booleanUndefinedConstantExpressions A map of undefined boolean constants to their - * expression nodes. - * @param integerUndefinedConstantExpressions A map of undefined integer constants to their - * expression nodes. - * @param doubleUndefinedConstantExpressions A map of undefined double constants to their - * expression nodes. - * @param globalBooleanVariables A list of global boolean variables. - * @param globalIntegerVariables A list of global integer variables. - * @param globalBooleanVariableToIndexMap A mapping from global boolean variable names to the index in the - * list of global boolean variables. - * @param globalIntegerVariableToIndexMap A mapping from global integer variable names to the index in the - * list of global integer variables. - * @param modules The modules of the program. - * @param rewards The reward models of the program. - * @param labels The labels defined for this model. - */ - Program(ModelType modelType, - std::map<std::string, std::unique_ptr<storm::ir::expressions::BooleanConstantExpression>> const& booleanUndefinedConstantExpressions, - std::map<std::string, std::unique_ptr<storm::ir::expressions::IntegerConstantExpression>> const& integerUndefinedConstantExpressions, - std::map<std::string, std::unique_ptr<storm::ir::expressions::DoubleConstantExpression>> const& doubleUndefinedConstantExpressions, - std::vector<BooleanVariable> const& globalBooleanVariables, - std::vector<IntegerVariable> const& globalIntegerVariables, - std::map<std::string, uint_fast64_t> const& globalBooleanVariableToIndexMap, - std::map<std::string, uint_fast64_t> const& globalIntegerVariableToIndexMap, - std::vector<storm::ir::Module> const& modules, - std::map<std::string, storm::ir::RewardModel> const& rewards, - std::map<std::string, std::unique_ptr<storm::ir::expressions::BaseExpression>> const& labels); - - /*! - * Performs a deep-copy of the given program. - * - * @param otherProgram The program to copy. - */ - Program(Program const& otherProgram); - - /*! - * Performs a deep-copy of the given program and assigns it to the current one. - * - * @param otherProgram The program to assign. - */ - Program& operator=(Program const& otherProgram); - - /*! - * Retrieves the number of modules in the program. - * - * @return The number of modules in the program. - */ - uint_fast64_t getNumberOfModules() const; - - /*! - * Retrieves a reference to the module with the given index. - * - * @param index The index of the module to retrieve. - * @return The module with the given index. - */ - storm::ir::Module const& getModule(uint_fast64_t index) const; - - /*! - * Retrieves the model type of the model. - * - * @return The type of the model. - */ - ModelType getModelType() const; - - /*! - * Retrieves a string representation of this program. - * - * @return A string representation of this program. - */ - std::string toString() const; - - /*! - * Retrieves a reference to the global boolean variable with the given index. - * - * @return A reference to the global boolean variable with the given index. - */ - storm::ir::BooleanVariable const& getGlobalBooleanVariable(uint_fast64_t index) const; - - /*! - * Retrieves a reference to the global integer variable with the given index. - * - * @return A reference to the global integer variable with the given index. - */ - storm::ir::IntegerVariable const& getGlobalIntegerVariable(uint_fast64_t index) const; - - /*! - * Retrieves the set of actions present in this module. - * - * @return The set of actions present in this module. - */ - std::set<std::string> const& getActions() const; - - /*! - * Retrieves the indices of all modules within this program that contain commands that are labelled with the given - * action. - * - * @param action The name of the action the modules are supposed to possess. - * @return A set of indices of all matching modules. - */ - std::set<uint_fast64_t> const& getModulesByAction(std::string const& action) const; - - /*! - * Retrieves the index of the module in which the given variable name was declared. - * - * @param variableName The name of the variable to search. - * @return The index of the module in which the given variable name was declared. - */ - uint_fast64_t getModuleIndexForVariable(std::string const& variableName) const; - - /*! - * Retrieves the number of global boolean variables of the program. - * - * @return The number of global boolean variables of the program. - */ - uint_fast64_t getNumberOfGlobalBooleanVariables() const; - - /*! - * Retrieves the number of global integer variables of the program. - * - * @return The number of global integer variables of the program. - */ - uint_fast64_t getNumberOfGlobalIntegerVariables() const; - - /*! - * Retrieves the reward model with the given name. - * - * @param name The name of the reward model to return. - * @return The reward model with the given name. - */ - storm::ir::RewardModel const& getRewardModel(std::string const& name) const; - - /*! - * Retrieves all labels that are defined by the probabilitic program. - * - * @return A set of labels that are defined in the program. - */ - std::map<std::string, std::unique_ptr<storm::ir::expressions::BaseExpression>> const& getLabels() const; - - /*! - * Retrieves whether the given constant name is an undefined boolean constant of the program. - * - * @return True if the given constant name is an undefined boolean constant of the program. - */ - bool hasUndefinedBooleanConstant(std::string const& constantName) const; - - /*! - * Retrieves the expression associated with the given undefined boolean constant. - * - * @param constantName The name of the undefined boolean constant for which to retrieve the expression. - * @return The expression associated with the given undefined boolean constant. - */ - std::unique_ptr<storm::ir::expressions::BooleanConstantExpression> const& getUndefinedBooleanConstantExpression(std::string const& constantName) const; - - /*! - * Retrieves whether the given constant name is an undefined integer constant of the program. - * - * @return True if the given constant name is an undefined integer constant of the program. - */ - bool hasUndefinedIntegerConstant(std::string const& constantName) const; - - /*! - * Retrieves the expression associated with the given undefined integer constant. - * - * @param constantName The name of the undefined integer constant for which to retrieve the expression. - * @return The expression associated with the given undefined integer constant. - */ - std::unique_ptr<storm::ir::expressions::IntegerConstantExpression> const& getUndefinedIntegerConstantExpression(std::string const& constantName) const; - - /*! - * Retrieves whether the given constant name is an undefined double constant of the program. - * - * @return True if the given constant name is an undefined double constant of the program. - */ - bool hasUndefinedDoubleConstant(std::string const& constantName) const; - - /*! - * Retrieves the expression associated with the given undefined double constant. - * - * @param constantName The name of the undefined double constant for which to retrieve the expression. - * @return The expression associated with the given undefined double constant. - */ - std::unique_ptr<storm::ir::expressions::DoubleConstantExpression> const& getUndefinedDoubleConstantExpression(std::string const& constantName) const; - - /*! - * Retrieves the mapping of undefined boolean constant names to their expression objects. - * - * @return The mapping of undefined boolean constant names to their expression objects. - */ - std::map<std::string, std::unique_ptr<storm::ir::expressions::BooleanConstantExpression>> const& getBooleanUndefinedConstantExpressionsMap() const; - - /*! - * Retrieves the mapping of undefined integer constant names to their expression objects. - * - * @return The mapping of undefined integer constant names to their expression objects. - */ - std::map<std::string, std::unique_ptr<storm::ir::expressions::IntegerConstantExpression>> const& getIntegerUndefinedConstantExpressionsMap() const; - - /*! - * Retrieves the mapping of undefined double constant names to their expression objects. - * - * @return The mapping of undefined double constant names to their expression objects. - */ - std::map<std::string, std::unique_ptr<storm::ir::expressions::DoubleConstantExpression>> const& getDoubleUndefinedConstantExpressionsMap() const; - - /*! - * Retrieves the global index of the given boolean variable. - * - * @param variableName The name of the boolean variable whose index to retrieve. - */ - uint_fast64_t getGlobalIndexOfBooleanVariable(std::string const& variableName) const; - - /*! - * Retrieves the global index of the integer boolean variable. - * - * @param variableName The name of the integer variable whose index to retrieve. - */ - uint_fast64_t getGlobalIndexOfIntegerVariable(std::string const& variableName) const; - - /*! - * Deletes all commands with indices not in the given set from the program. - * - * @param indexSet The set of indices for which to keep the commands. - */ - void restrictCommands(boost::container::flat_set<uint_fast64_t> const& indexSet); - private: - // The type of the model. - ModelType modelType; - - // A map of undefined boolean constants to their expression nodes. - std::map<std::string, std::unique_ptr<storm::ir::expressions::BooleanConstantExpression>> booleanUndefinedConstantExpressions; - - // A map of undefined integer constants to their expressions nodes. - std::map<std::string, std::unique_ptr<storm::ir::expressions::IntegerConstantExpression>> integerUndefinedConstantExpressions; - - // A map of undefined double constants to their expressions nodes. - std::map<std::string, std::unique_ptr<storm::ir::expressions::DoubleConstantExpression>> doubleUndefinedConstantExpressions; - - // A list of global boolean variables. - std::vector<BooleanVariable> globalBooleanVariables; - - // A list of global integer variables. - std::vector<IntegerVariable> globalIntegerVariables; - - // A mapping from global boolean variable names to their indices. - std::map<std::string, uint_fast64_t> globalBooleanVariableToIndexMap; - - // A mapping from global integer variable names to their indices. - std::map<std::string, uint_fast64_t> globalIntegerVariableToIndexMap; - - // The modules associated with the program. - std::vector<storm::ir::Module> modules; - - // The reward models associated with the program. - std::map<std::string, storm::ir::RewardModel> rewards; - - // The labels that are defined for this model. - std::map<std::string, std::unique_ptr<storm::ir::expressions::BaseExpression>> labels; - - // The set of actions present in this program. - std::set<std::string> actions; - - // A map of actions to the set of modules containing commands labelled with this action. - std::map<std::string, std::set<uint_fast64_t>> actionsToModuleIndexMap; - - // A mapping from variable names to the modules in which they were declared. - std::map<std::string, uint_fast64_t> variableToModuleIndexMap; - }; - - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_PROGRAM_H_ */ diff --git a/src/ir/RewardModel.cpp b/src/ir/RewardModel.cpp deleted file mode 100644 index 9d6a79ed3..000000000 --- a/src/ir/RewardModel.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * RewardModel.cpp - * - * Created on: 12.01.2013 - * Author: Christian Dehnert - */ - -#include <sstream> - -#include "RewardModel.h" - -namespace storm { - namespace ir { - - RewardModel::RewardModel() : rewardModelName(), stateRewards(), transitionRewards() { - // Nothing to do here. - } - - RewardModel::RewardModel(std::string const& rewardModelName, std::vector<storm::ir::StateReward> const& stateRewards, std::vector<storm::ir::TransitionReward> const& transitionRewards) : rewardModelName(rewardModelName), stateRewards(stateRewards), transitionRewards(transitionRewards) { - // Nothing to do here. - } - - std::string RewardModel::toString() const { - std::stringstream result; - result << "rewards \"" << rewardModelName << "\"" << std::endl; - for (auto const& reward : stateRewards) { - result << reward.toString() << std::endl; - } - for (auto const& reward : transitionRewards) { - result << reward.toString() << std::endl; - } - result << "endrewards" << std::endl; - return result.str(); - } - - bool RewardModel::hasStateRewards() const { - return this->stateRewards.size() > 0; - } - - std::vector<storm::ir::StateReward> const& RewardModel::getStateRewards() const { - return this->stateRewards; - } - - bool RewardModel::hasTransitionRewards() const { - return this->transitionRewards.size() > 0; - } - - std::vector<storm::ir::TransitionReward> const& RewardModel::getTransitionRewards() const { - return this->transitionRewards; - } - - } // namespace ir -} // namespace storm diff --git a/src/ir/RewardModel.h b/src/ir/RewardModel.h deleted file mode 100644 index 827248810..000000000 --- a/src/ir/RewardModel.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * RewardModel.h - * - * Created on: 04.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_REWARDMODEL_H_ -#define STORM_IR_REWARDMODEL_H_ - -#include <string> -#include <vector> - -#include "StateReward.h" -#include "TransitionReward.h" - -namespace storm { - namespace ir { - - /*! - * A class representing a reward model. - */ - class RewardModel { - public: - /*! - * Default constructor. Creates an empty reward model. - */ - RewardModel(); - - /*! - * Creates a reward module with the given name, state and transition rewards. - * - * @param rewardModelName The name of the reward model. - * @param stateRewards A vector of state-based rewards. - * @param transitionRewards A vector of transition-based rewards. - */ - RewardModel(std::string const& rewardModelName, std::vector<storm::ir::StateReward> const& stateRewards, std::vector<storm::ir::TransitionReward> const& transitionRewards); - - /*! - * Retrieves a string representation of this reward model. - * - * @return a string representation of this reward model. - */ - std::string toString() const; - - /*! - * Check, if there are any state rewards. - * - * @return True, iff there are any state rewards. - */ - bool hasStateRewards() const; - - /*! - * Retrieves a vector of state rewards associated with this reward model. - * - * @return A vector containing the state rewards associated with this reward model. - */ - std::vector<storm::ir::StateReward> const& getStateRewards() const; - - /*! - * Check, if there are any transition rewards. - * - * @return True, iff there are any transition rewards associated with this reward model. - */ - bool hasTransitionRewards() const; - - /*! - * Retrieves a vector of transition rewards associated with this reward model. - * - * @return A vector of transition rewards associated with this reward model. - */ - std::vector<storm::ir::TransitionReward> const& getTransitionRewards() const; - - private: - // The name of the reward model. - std::string rewardModelName; - - // The state-based rewards associated with this reward model. - std::vector<storm::ir::StateReward> stateRewards; - - // The transition-based rewards associated with this reward model. - std::vector<storm::ir::TransitionReward> transitionRewards; - }; - - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_REWARDMODEL_H_ */ diff --git a/src/ir/StateReward.cpp b/src/ir/StateReward.cpp deleted file mode 100644 index ff3c815ce..000000000 --- a/src/ir/StateReward.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * StateReward.cpp - * - * Created on: 12.01.2013 - * Author: Christian Dehnert - */ - -#include <sstream> - -#include "StateReward.h" - -namespace storm { - namespace ir { - - StateReward::StateReward() : statePredicate(), rewardValue() { - // Nothing to do here. - } - - StateReward::StateReward(std::unique_ptr<storm::ir::expressions::BaseExpression>&& statePredicate, std::unique_ptr<storm::ir::expressions::BaseExpression>&& rewardValue) : statePredicate(std::move(statePredicate)), rewardValue(std::move(rewardValue)) { - // Nothing to do here. - } - - StateReward::StateReward(StateReward const& otherReward) { - if (otherReward.statePredicate != nullptr) { - statePredicate = otherReward.statePredicate->clone(); - } - if (otherReward.rewardValue != nullptr) { - rewardValue = otherReward.rewardValue->clone(); - } - } - - StateReward& StateReward::operator=(StateReward const& otherReward) { - if (this != & otherReward) { - if (otherReward.statePredicate != nullptr) { - this->statePredicate = otherReward.statePredicate->clone(); - } - if (otherReward.rewardValue != nullptr) { - this->rewardValue = otherReward.rewardValue->clone(); - } - } - - return *this; - } - - std::string StateReward::toString() const { - std::stringstream result; - result << "\t" << statePredicate->toString() << ": " << rewardValue->toString() << ";"; - return result.str(); - } - - std::unique_ptr<storm::ir::expressions::BaseExpression> const& StateReward::getStatePredicate() const { - return this->statePredicate; - } - - std::unique_ptr<storm::ir::expressions::BaseExpression> const& StateReward::getRewardValue() const { - return this->rewardValue; - } - - } // namespace ir -} // namespace storm diff --git a/src/ir/StateReward.h b/src/ir/StateReward.h deleted file mode 100644 index 117544d6d..000000000 --- a/src/ir/StateReward.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * StateReward.h - * - * Created on: Jan 10, 2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_STATEREWARD_H_ -#define STORM_IR_STATEREWARD_H_ - -#include <memory> - -#include "expressions/BaseExpression.h" - -namespace storm { - namespace ir { - - /*! - * A class representing a state reward. - */ - class StateReward { - public: - /*! - * Default constructor. Creates an empty state reward. - */ - StateReward(); - - /*! - * Creates a state reward for the states satisfying the given expression with the value given - * by a second expression. - * - * @param statePredicate The predicate that states earning this state-based reward need to - * satisfy. - * @param rewardValue An expression specifying the values of the rewards to attach to the - * states. - */ - StateReward(std::unique_ptr<storm::ir::expressions::BaseExpression>&& statePredicate, std::unique_ptr<storm::ir::expressions::BaseExpression>&& rewardValue); - - /*! - * Performs a deep-copy of the given reward. - * - * @param otherReward The reward to copy. - */ - StateReward(StateReward const& otherReward); - - /*! - * Performs a deep-copy of the given reward and assigns it to the current one. - * - * @param otherReward The reward to assign. - */ - StateReward& operator=(StateReward const& otherReward); - - /*! - * Retrieves a string representation of this state reward. - * - * @return A string representation of this state reward. - */ - std::string toString() const; - - /*! - * Retrieves the state predicate that is associated with this state reward. - * - * @return The state predicate that is associated with this state reward. - */ - std::unique_ptr<storm::ir::expressions::BaseExpression> const& getStatePredicate() const; - - /*! - * Retrieves the reward value associated with this state reward. - * - * @return The reward value associated with this state reward. - */ - std::unique_ptr<storm::ir::expressions::BaseExpression> const& getRewardValue() const; - - private: - // The predicate that characterizes the states that obtain this reward. - std::unique_ptr<storm::ir::expressions::BaseExpression> statePredicate; - - // The expression that specifies the value of the reward obtained. - std::unique_ptr<storm::ir::expressions::BaseExpression> rewardValue; - }; - - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_STATEREWARD_H_ */ diff --git a/src/ir/TransitionReward.cpp b/src/ir/TransitionReward.cpp deleted file mode 100644 index bd9664529..000000000 --- a/src/ir/TransitionReward.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * TransitionReward.cpp - * - * Created on: 12.01.2013 - * Author: Christian Dehnert - */ - -#include <sstream> - -#include "TransitionReward.h" - -namespace storm { - namespace ir { - - TransitionReward::TransitionReward() : commandName(), statePredicate(), rewardValue() { - // Nothing to do here. - } - - TransitionReward::TransitionReward(std::string const& commandName, std::unique_ptr<storm::ir::expressions::BaseExpression>&& statePredicate, std::unique_ptr<storm::ir::expressions::BaseExpression>&& rewardValue) : commandName(commandName), statePredicate(std::move(statePredicate)), rewardValue(std::move(rewardValue)) { - // Nothing to do here. - } - - TransitionReward::TransitionReward(TransitionReward const& otherReward) : commandName(otherReward.commandName), statePredicate(), rewardValue() { - if (otherReward.statePredicate != nullptr) { - statePredicate = otherReward.statePredicate->clone(); - } - if (otherReward.rewardValue != nullptr) { - rewardValue = otherReward.rewardValue->clone(); - } - } - - TransitionReward& TransitionReward::operator=(TransitionReward const& otherReward) { - if (this != &otherReward) { - this->commandName = otherReward.commandName; - if (otherReward.statePredicate != nullptr) { - this->statePredicate = otherReward.statePredicate->clone(); - } - if (otherReward.rewardValue != nullptr) { - this->rewardValue = otherReward.rewardValue->clone(); - } - } - - return *this; - } - - std::string TransitionReward::toString() const { - std::stringstream result; - result << "\t[" << commandName << "] " << statePredicate->toString() << ": " << rewardValue->toString() << ";"; - return result.str(); - } - - std::string const& TransitionReward::getActionName() const { - return this->commandName; - } - - std::unique_ptr<storm::ir::expressions::BaseExpression> const& TransitionReward::getStatePredicate() const { - return this->statePredicate; - } - - std::unique_ptr<storm::ir::expressions::BaseExpression> const& TransitionReward::getRewardValue() const { - return this->rewardValue; - } - - } // namespace ir -} // namespace storm diff --git a/src/ir/TransitionReward.h b/src/ir/TransitionReward.h deleted file mode 100644 index 298d17024..000000000 --- a/src/ir/TransitionReward.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * TransitionReward.h - * - * Created on: Jan 10, 2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_TRANSITIONREWARD_H_ -#define STORM_IR_TRANSITIONREWARD_H_ - -#include <memory> - -#include "expressions/BaseExpression.h" - -namespace storm { - -namespace ir { - -/*! - * A class representing a transition reward. - */ -class TransitionReward { -public: - /*! - * Default constructor. Creates an empty transition reward. - */ - TransitionReward(); - - /*! - * Creates a transition reward for the transitions with the given name emanating from states - * satisfying the given expression with the value given by another expression. - * - * @param commandName The name of the command that obtains this reward. - * @param statePredicate The predicate that needs to hold before taking a transition with the - * previously specified name in order to obtain the reward. - * @param rewardValue An expression specifying the values of the rewards to attach to the - * transitions. - */ - TransitionReward(std::string const& commandName, std::unique_ptr<storm::ir::expressions::BaseExpression>&& statePredicate, std::unique_ptr<storm::ir::expressions::BaseExpression>&& rewardValue); - - /*! - * Performs a deep-copy of the given transition reward. - * - * @param otherReward The transition reward to copy. - */ - TransitionReward(TransitionReward const& otherReward); - - /*! - * Performs a deep-copy of the given transition reward and assigns it to the current one. - * - * @param otherReward The reward to assign. - */ - TransitionReward& operator=(TransitionReward const& otherReward); - - /*! - * Retrieves a string representation of this transition reward. - * - * @return A string representation of this transition reward. - */ - std::string toString() const; - - /*! - * Retrieves the action name that is associated with this transition reward. - * - * @return The action name that is associated with this transition reward. - */ - std::string const& getActionName() const; - - /*! - * Retrieves the state predicate that is associated with this state reward. - * - * @return The state predicate that is associated with this state reward. - */ - std::unique_ptr<storm::ir::expressions::BaseExpression> const& getStatePredicate() const; - - /*! - * Retrieves the reward value associated with this state reward. - * - * @return The reward value associated with this state reward. - */ - std::unique_ptr<storm::ir::expressions::BaseExpression> const& getRewardValue() const; - -private: - // The name of the command this transition-based reward is attached to. - std::string commandName; - - // A predicate that needs to be satisfied by states for the reward to be obtained (by taking - // a corresponding command transition). - std::unique_ptr<storm::ir::expressions::BaseExpression> statePredicate; - - // The expression specifying the value of the reward obtained along the transitions. - std::unique_ptr<storm::ir::expressions::BaseExpression> rewardValue; -}; - -} // namespace ir - -} // namespace storm - -#endif /* STORM_IR_TRANSITIONREWARD_H_ */ diff --git a/src/ir/Update.cpp b/src/ir/Update.cpp deleted file mode 100644 index aac7b3ca0..000000000 --- a/src/ir/Update.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Update.cpp - * - * Created on: 12.01.2013 - * Author: Christian Dehnert - */ - -#include <sstream> - -#include "Update.h" -#include "src/parser/prismparser/VariableState.h" -#include "src/exceptions/OutOfRangeException.h" - -namespace storm { - namespace ir { - - Update::Update() : likelihoodExpression(), booleanAssignments(), integerAssignments(), globalIndex() { - // Nothing to do here. - } - - Update::Update(uint_fast64_t globalIndex, std::unique_ptr<storm::ir::expressions::BaseExpression>&& likelihoodExpression, std::map<std::string, storm::ir::Assignment> const& booleanAssignments, std::map<std::string, storm::ir::Assignment> const& integerAssignments) - : likelihoodExpression(std::move(likelihoodExpression)), booleanAssignments(booleanAssignments), integerAssignments(integerAssignments), globalIndex(globalIndex) { - // Nothing to do here. - } - - Update::Update(Update const& update, uint_fast64_t newGlobalIndex, std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState& variableState) : globalIndex(newGlobalIndex) { - for (auto const& variableAssignmentPair : update.booleanAssignments) { - if (renaming.count(variableAssignmentPair.first) > 0) { - this->booleanAssignments[renaming.at(variableAssignmentPair.first)] = Assignment(variableAssignmentPair.second, renaming, variableState); - } else { - this->booleanAssignments[variableAssignmentPair.first] = Assignment(variableAssignmentPair.second, renaming, variableState); - } - } - for (auto const& variableAssignmentPair : update.integerAssignments) { - if (renaming.count(variableAssignmentPair.first) > 0) { - this->integerAssignments[renaming.at(variableAssignmentPair.first)] = Assignment(variableAssignmentPair.second, renaming, variableState); - } else { - this->integerAssignments[variableAssignmentPair.first] = Assignment(variableAssignmentPair.second, renaming, variableState); - } - } - this->likelihoodExpression = update.likelihoodExpression->clone(renaming, variableState); - } - - Update::Update(Update const& otherUpdate) : likelihoodExpression(), booleanAssignments(otherUpdate.booleanAssignments), integerAssignments(otherUpdate.integerAssignments), globalIndex(otherUpdate.globalIndex) { - if (otherUpdate.likelihoodExpression != nullptr) { - likelihoodExpression = otherUpdate.likelihoodExpression->clone(); - } - } - - Update& Update::operator=(Update const& otherUpdate) { - if (this != &otherUpdate) { - if (otherUpdate.likelihoodExpression != nullptr) { - this->likelihoodExpression = otherUpdate.likelihoodExpression->clone(); - } - this->booleanAssignments = otherUpdate.booleanAssignments; - this->integerAssignments = otherUpdate.integerAssignments; - this->globalIndex = otherUpdate.globalIndex; - } - - return *this; - } - - std::unique_ptr<storm::ir::expressions::BaseExpression> const& Update::getLikelihoodExpression() const { - return likelihoodExpression; - } - - uint_fast64_t Update::getNumberOfBooleanAssignments() const { - return booleanAssignments.size(); - } - - uint_fast64_t Update::getNumberOfIntegerAssignments() const { - return integerAssignments.size(); - } - - std::map<std::string, storm::ir::Assignment> const& Update::getBooleanAssignments() const { - return booleanAssignments; - } - - std::map<std::string, storm::ir::Assignment> const& Update::getIntegerAssignments() const { - return integerAssignments; - } - - storm::ir::Assignment const& Update::getBooleanAssignment(std::string const& variableName) const { - auto variableAssignmentPair = booleanAssignments.find(variableName); - if (variableAssignmentPair == booleanAssignments.end()) { - throw storm::exceptions::OutOfRangeException() << "Cannot find boolean assignment for variable '" - << variableName << "' in update " << this->toString() << "."; - } - - return variableAssignmentPair->second; - } - - storm::ir::Assignment const& Update::getIntegerAssignment(std::string const& variableName) const { - auto variableAssignmentPair = integerAssignments.find(variableName); - if (variableAssignmentPair == integerAssignments.end()) { - throw storm::exceptions::OutOfRangeException() << "Cannot find integer assignment for variable '" - << variableName << "' in update " << this->toString() << "."; - } - - return variableAssignmentPair->second; - } - - uint_fast64_t Update::getGlobalIndex() const { - return this->globalIndex; - } - - std::string Update::toString() const { - std::stringstream result; - result << likelihoodExpression->toString() << " : "; - uint_fast64_t i = 0; - for (auto const& assignment : booleanAssignments) { - result << assignment.second.toString(); - if (i < booleanAssignments.size() - 1 || integerAssignments.size() > 0) { - result << " & "; - } - ++i; - } - i = 0; - for (auto const& assignment : integerAssignments) { - result << assignment.second.toString(); - if (i < integerAssignments.size() - 1) { - result << " & "; - } - ++i; - } - return result.str(); - } - - } // namespace ir -} // namespace storm diff --git a/src/ir/Update.h b/src/ir/Update.h deleted file mode 100644 index 07b9a7d24..000000000 --- a/src/ir/Update.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Update.h - * - * Created on: 06.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_UPDATE_H_ -#define STORM_IR_UPDATE_H_ - -#include <map> -#include <memory> - -#include "expressions/BaseExpression.h" -#include "Assignment.h" - -namespace storm { - - namespace parser { - namespace prism { - class VariableState; - } // namespace prismparser - } // namespace parser - - namespace ir { - - /*! - * A class representing an update of a command. - */ - class Update { - public: - /*! - * Default constructor. Creates an empty update. - */ - Update(); - - /*! - * Creates an update with the given expression specifying the likelihood and the mapping of - * variable to their assignments. - * - * @param globalIndex The global index of the update. - * @param likelihoodExpression An expression specifying the likelihood of this update. - * @param assignments A map of variable names to their assignments. - */ - Update(uint_fast64_t globalIndex, std::unique_ptr<storm::ir::expressions::BaseExpression>&& likelihoodExpression, std::map<std::string, storm::ir::Assignment> const& booleanAssignments, std::map<std::string, storm::ir::Assignment> const& integerAssignments); - - /*! - * Creates a copy of the given update and performs the provided renaming. - * - * @param update The update that is to be copied. - * @param newGlobalIndex The global index of the resulting update. - * @param renaming A mapping from names that are to be renamed to the names they are to be - * replaced with. - * @param variableState An object knowing about the variables in the system. - */ - Update(Update const& update, uint_fast64_t newGlobalIndex, std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState& variableState); - - /*! - * Peforms a deep-copy of the given update. - * - * @param otherUpdate The update to copy. - */ - Update(Update const& otherUpdate); - - /*! - * Performs a deep-copy of the given update and assigns it to the current one. - * - * @param otherUpdate The update to assign. - */ - Update& operator=(Update const& otherUpdate); - - /*! - * Retrieves the expression for the likelihood of this update. - * - * @return The expression for the likelihood of this update. - */ - std::unique_ptr<storm::ir::expressions::BaseExpression> const& getLikelihoodExpression() const; - - /*! - * Retrieves the number of boolean assignments associated with this update. - * - * @return The number of boolean assignments associated with this update. - */ - uint_fast64_t getNumberOfBooleanAssignments() const; - - /*! - * Retrieves the number of integer assignments associated with this update. - * - * @return The number of integer assignments associated with this update. - */ - uint_fast64_t getNumberOfIntegerAssignments() const; - - /*! - * Retrieves a reference to the map of boolean variable names to their respective assignments. - * - * @return A reference to the map of boolean variable names to their respective assignments. - */ - std::map<std::string, storm::ir::Assignment> const& getBooleanAssignments() const; - - /*! - * Retrieves a reference to the map of integer variable names to their respective assignments. - * - * @return A reference to the map of integer variable names to their respective assignments. - */ - std::map<std::string, storm::ir::Assignment> const& getIntegerAssignments() const; - - /*! - * Retrieves a reference to the assignment for the boolean variable with the given name. - * - * @return A reference to the assignment for the boolean variable with the given name. - */ - storm::ir::Assignment const& getBooleanAssignment(std::string const& variableName) const; - - /*! - * Retrieves a reference to the assignment for the integer variable with the given name. - * - * @return A reference to the assignment for the integer variable with the given name. - */ - storm::ir::Assignment const& getIntegerAssignment(std::string const& variableName) const; - - /*! - * Retrieves the global index of the update, that is, a unique index over all modules. - * - * @return The global index of the update. - */ - uint_fast64_t getGlobalIndex() const; - - /*! - * Retrieves a string representation of this update. - * - * @return A string representation of this update. - */ - std::string toString() const; - - private: - // An expression specifying the likelihood of taking this update. - std::unique_ptr<storm::ir::expressions::BaseExpression> likelihoodExpression; - - // A mapping of boolean variable names to their assignments in this update. - std::map<std::string, storm::ir::Assignment> booleanAssignments; - - // A mapping of integer variable names to their assignments in this update. - std::map<std::string, storm::ir::Assignment> integerAssignments; - - // The global index of the update. - uint_fast64_t globalIndex; - }; - - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_UPDATE_H_ */ diff --git a/src/ir/Variable.cpp b/src/ir/Variable.cpp deleted file mode 100644 index 83cb2a33a..000000000 --- a/src/ir/Variable.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Variable.cpp - * - * Created on: 12.01.2013 - * Author: Christian Dehnert - */ - -#include <sstream> -#include <map> -#include <iostream> - -#include "Variable.h" -#include "src/parser/prismparser/VariableState.h" - -namespace storm { - namespace ir { - - Variable::Variable() : localIndex(0), globalIndex(0), variableName(), initialValue() { - // Nothing to do here. - } - - Variable::Variable(uint_fast64_t localIndex, uint_fast64_t globalIndex, std::string const& variableName, std::unique_ptr<storm::ir::expressions::BaseExpression>&& initialValue) - : localIndex(localIndex), globalIndex(globalIndex), variableName(variableName), initialValue(std::move(initialValue)) { - // Nothing to do here. - } - - Variable::Variable(Variable const& otherVariable) : localIndex(otherVariable.localIndex), globalIndex(otherVariable.globalIndex), variableName(otherVariable.variableName), initialValue() { - if (otherVariable.initialValue != nullptr) { - initialValue = otherVariable.initialValue->clone(); - } - } - - Variable::Variable(Variable const& var, std::string const& newName, uint_fast64_t newGlobalIndex, std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) - : localIndex(var.getLocalIndex()), globalIndex(newGlobalIndex), variableName(newName), initialValue() { - if (var.initialValue != nullptr) { - this->initialValue = var.initialValue->clone(renaming, variableState); - } - } - - Variable& Variable::operator=(Variable const& otherVariable) { - if (this != &otherVariable) { - this->localIndex = otherVariable.localIndex; - this->globalIndex = otherVariable.globalIndex; - this->variableName = otherVariable.variableName; - if (otherVariable.initialValue != nullptr) { - this->initialValue = otherVariable.initialValue->clone(); - } - } - - return *this; - } - - std::string const& Variable::getName() const { - return variableName; - } - - uint_fast64_t Variable::getGlobalIndex() const { - return globalIndex; - } - - uint_fast64_t Variable::getLocalIndex() const { - return localIndex; - } - - std::unique_ptr<storm::ir::expressions::BaseExpression> const& Variable::getInitialValue() const { - return initialValue; - } - - void Variable::setInitialValue(std::unique_ptr<storm::ir::expressions::BaseExpression>&& initialValue) { - this->initialValue = std::move(initialValue); - } - - } // namespace ir -} // namespace storm diff --git a/src/ir/Variable.h b/src/ir/Variable.h deleted file mode 100644 index 53b2f3b98..000000000 --- a/src/ir/Variable.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Variable.h - * - * Created on: 06.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_VARIABLE_H_ -#define STORM_IR_VARIABLE_H_ - -#include <string> -#include <memory> - -#include "expressions/BaseExpression.h" - -namespace storm { - - namespace parser { - namespace prism { - class VariableState; - } // namespace prismparser - } // namespace parser - - namespace ir { - - /*! - * A class representing a untyped variable. - */ - class Variable { - public: - /*! - * Default constructor. Creates an unnamed, untyped variable without initial value. - */ - Variable(); - - /*! - * Creates an untyped variable with the given name and initial value. - * - * @param localIndex A module-local index for the variable. - * @param globalIndex A globally unique (among the variables of equal type) index for the variable. - * @param variableName the name of the variable. - * @param initialValue the expression that defines the initial value of the variable. - */ - Variable(uint_fast64_t localIndex, uint_fast64_t globalIndex, std::string const& variableName, std::unique_ptr<storm::ir::expressions::BaseExpression>&& initialValue = nullptr); - - /*! - * Creates a copy of the given variable and performs the provided renaming. - * - * @param oldVariable The variable to copy. - * @param newName New name of this variable. - * @param newGlobalIndex The new global index of the variable. - * @param renaming A mapping from names that are to be renamed to the names they are to be - * replaced with. - * @param variableState An object knowing about the variables in the system. - */ - Variable(Variable const& oldVariable, std::string const& newName, uint_fast64_t newGlobalIndex, std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState); - - /*! - * Creates a deep-copy of the given variable. - * - * @param otherVariable The variable to copy. - */ - Variable(Variable const& otherVariable); - - /*! - * Creates a deep-copy of the given variable and assigns it to the current one. - */ - Variable& operator=(Variable const& otherVariable); - - /*! - * Retrieves the name of the variable. - * - * @return The name of the variable. - */ - std::string const& getName() const; - - /*! - * Retrieves the global index of the variable, i.e. the index in all variables of equal type - * of all modules. - * - * @return The global index of the variable. - */ - uint_fast64_t getGlobalIndex() const; - - /*! - * Retrieves the global index of the variable, i.e. the index in all variables of equal type in - * the same module. - * - * @return The local index of the variable. - */ - uint_fast64_t getLocalIndex() const; - - /*! - * Retrieves the expression defining the initial value of the variable. - * - * @return The expression defining the initial value of the variable. - */ - std::unique_ptr<storm::ir::expressions::BaseExpression> const& getInitialValue() const; - - /*! - * Sets the initial value to the given expression. - * - * @param initialValue The new initial value. - */ - void setInitialValue(std::unique_ptr<storm::ir::expressions::BaseExpression>&& initialValue); - - private: - // A unique (among the variables of equal type) index for the variable inside its module. - uint_fast64_t localIndex; - - // A unique (among the variables of equal type) index for the variable over all modules. - uint_fast64_t globalIndex; - - // The name of the variable. - std::string variableName; - - // The expression defining the initial value of the variable. - std::unique_ptr<storm::ir::expressions::BaseExpression> initialValue; - }; - - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_VARIABLE_H_ */ diff --git a/src/ir/expressions/BaseExpression.cpp b/src/ir/expressions/BaseExpression.cpp deleted file mode 100644 index 66771d6b0..000000000 --- a/src/ir/expressions/BaseExpression.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - * BaseExpression.cpp - * - * Created on: 10.06.2013 - * Author: Christian Dehnert - */ - -#include "BaseExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - BaseExpression::BaseExpression() : type(undefined) { - // Nothing to do here. - } - - BaseExpression::BaseExpression(ReturnType type) : type(type) { - // Nothing to do here. - } - - BaseExpression::BaseExpression(BaseExpression const& baseExpression) : type(baseExpression.type) { - // Nothing to do here. - } - - BaseExpression::~BaseExpression() { - // Nothing to do here. - } - - std::unique_ptr<BaseExpression> BaseExpression::substitute(std::unique_ptr<BaseExpression>&& expression, std::map<std::string, std::reference_wrapper<BaseExpression>> const& substitution) { - BaseExpression* result = expression->performSubstitution(substitution); - - if (result != expression.get()) { - return std::unique_ptr<BaseExpression>(result); - } else { - return std::move(expression); - } - } - - int_fast64_t BaseExpression::getValueAsInt(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const { - if (type != int_) { - throw storm::exceptions::ExpressionEvaluationException() << "Cannot evaluate expression of type '" - << this->getTypeName() << "' as 'int'."; - } - throw storm::exceptions::NotImplementedException() << "Cannot evaluate expression of type '" - << this->getTypeName() << " because evaluation implementation is missing."; - } - - bool BaseExpression::getValueAsBool(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const { - if (type != bool_) { - throw storm::exceptions::ExpressionEvaluationException() << "Cannot evaluate expression of type '" - << this->getTypeName() << "' as 'bool'."; - } - throw storm::exceptions::NotImplementedException() << "Cannot evaluate expression of type '" - << this->getTypeName() << " because evaluation implementation is missing."; - } - - double BaseExpression::getValueAsDouble(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const { - if (type != double_ && type != int_) { - throw storm::exceptions::ExpressionEvaluationException() << "Cannot evaluate expression of type '" - << this->getTypeName() << "' as 'double'."; - } - throw storm::exceptions::NotImplementedException() << "Cannot evaluate expression of type '" - << this->getTypeName() << " because evaluation implementation is missing."; - } - - std::string BaseExpression::getTypeName() const { - switch(type) { - case bool_: return std::string("bool"); - case int_: return std::string("int"); - case double_: return std::string("double"); - default: return std::string("undefined"); - } - } - - BaseExpression::ReturnType BaseExpression::getType() const { - return type; - } - - BaseExpression* BaseExpression::performSubstitution(std::map<std::string, std::reference_wrapper<BaseExpression>> const& substitution) { - return this; - } - - } // namespace expressions - } // namespace ir -} // namespace storm diff --git a/src/ir/expressions/BaseExpression.h b/src/ir/expressions/BaseExpression.h deleted file mode 100644 index 948ed9d88..000000000 --- a/src/ir/expressions/BaseExpression.h +++ /dev/null @@ -1,172 +0,0 @@ -/* - * BaseExpression.h - * - * Created on: 03.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_EXPRESSIONS_BASEEXPRESSION_H_ -#define STORM_IR_EXPRESSIONS_BASEEXPRESSION_H_ - -#include <string> -#include <vector> -#include <map> -#include <memory> - -#include "src/exceptions/ExpressionEvaluationException.h" -#include "src/exceptions/NotImplementedException.h" -#include "ExpressionVisitor.h" - -namespace storm { - - // Forward-declare VariableState. - namespace parser { - namespace prism { - class VariableState; - } // namespace prismparser - } // namespace parser - - namespace ir { - namespace expressions { - - /*! - * The base class for all expressions. - */ - class BaseExpression { - public: - // Forward declare friend classes to allow access to substitute. - friend class BinaryExpression; - friend class UnaryExpression; - - /*! - * Each node in an expression tree has a uniquely defined type from this enum. - */ - enum ReturnType {undefined, bool_, int_, double_}; - - /*! - * Creates an expression with undefined type. - */ - BaseExpression(); - - /*! - * Creates an expression with the given type. - * - * @param type The type of the expression. - */ - BaseExpression(ReturnType type); - - /*! - * Copy-constructs from the given expression. - * - * @param baseExpression The expression to copy. - */ - BaseExpression(BaseExpression const& baseExpression); - - /*! - * Destructor. - */ - virtual ~BaseExpression(); - - /*! - * Performes a deep-copy of the expression. - * - * @return A deep-copy of the expression. - */ - virtual std::unique_ptr<BaseExpression> clone() const = 0; - - /*! - * Copies the expression tree underneath (including) the current node and performs the provided renaming. - * - * @param renaming A mapping from identifier names to strings they are to be replaced with. - * @param variableState An object knowing about the global variable state. - */ - virtual std::unique_ptr<BaseExpression> clone(std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) const = 0; - - /*! - * Performs the given substitution by replacing each variable in the given expression that is a key in - * the map by a copy of the mapped expression. - * - * @param expression The expression in which to perform the substitution. - * @param substitution The substitution to apply. - * @return The resulting expression. - */ - static std::unique_ptr<BaseExpression> substitute(std::unique_ptr<BaseExpression>&& expression, std::map<std::string, std::reference_wrapper<BaseExpression>> const& substitution); - - /*! - * Retrieves the value of the expression as an integer given the provided variable valuation. - * - * @param variableValues The variable valuation under which to evaluate the expression. If set to null, - * constant expressions can be evaluated without variable values. However, upon encountering a variable - * expression an expression is thrown, because evaluation is impossible without the variable values then. - * @return The value of the expression as an integer. - */ - virtual int_fast64_t getValueAsInt(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const; - - /*! - * Retrieves the value of the expression as a boolean given the provided variable valuation. - * - * @param variableValues The variable valuation under which to evaluate the expression. If set to null, - * constant expressions can be evaluated without variable values. However, upon encountering a variable - * expression an expression is thrown, because evaluation is impossible without the variable values then. - * @return The value of the expression as a boolean. - */ - virtual bool getValueAsBool(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const; - - /*! - * Retrieves the value of the expression as a double given the provided variable valuation. - * - * @param variableValues The variable valuation under which to evaluate the expression. If set to null, - * constant expressions can be evaluated without variable values. However, upon encountering a variable - * expression an expression is thrown, because evaluation is impossible without the variable values then. - * @return The value of the expression as a double. - */ - virtual double getValueAsDouble(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const; - - /*! - * Acceptor method for visitor pattern. - * - * @param visitor The visitor that is supposed to visit each node of the expression tree. - */ - virtual void accept(ExpressionVisitor* visitor) = 0; - - /*! - * Retrieves a string representation of the expression tree underneath the current node. - * - * @return A string representation of the expression tree underneath the current node. - */ - virtual std::string toString() const = 0; - - /*! - * Retrieves a string representation of the type to which this node evaluates. - * - * @return A string representation of the type to which this node evaluates. - */ - std::string getTypeName() const; - - /*! - * Retrieves the type to which the node evaluates. - * - * @return The type to which the node evaluates. - */ - ReturnType getType() const; - - protected: - /*! - * Performs the given substitution on the expression, i.e. replaces all variables whose names are keys - * of the map by a copy of the expression they are associated with in the map. This is intended as a helper - * function for substitute. - * - * @param substitution The substitution to perform - */ - virtual BaseExpression* performSubstitution(std::map<std::string, std::reference_wrapper<BaseExpression>> const& substitution); - - private: - // The type to which this node evaluates. - ReturnType type; - }; - - } // namespace expressions - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_EXPRESSIONS_BASEEXPRESSION_H_ */ diff --git a/src/ir/expressions/BinaryBooleanFunctionExpression.cpp b/src/ir/expressions/BinaryBooleanFunctionExpression.cpp deleted file mode 100644 index 83cb1d82f..000000000 --- a/src/ir/expressions/BinaryBooleanFunctionExpression.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * BinaryBooleanFunctionExpression.cpp - * - * Created on: 10.06.2013 - * Author: Christian Dehnert - */ - -#include <sstream> - -#include "BinaryBooleanFunctionExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - BinaryBooleanFunctionExpression::BinaryBooleanFunctionExpression(std::unique_ptr<BaseExpression>&& left, std::unique_ptr<BaseExpression>&& right, FunctionType functionType) - : BinaryExpression(bool_, std::move(left), std::move(right)), functionType(functionType) { - // Nothing to do here. - } - - BinaryBooleanFunctionExpression::BinaryBooleanFunctionExpression(BinaryBooleanFunctionExpression const& binaryBooleanFunctionExpression) - : BinaryExpression(binaryBooleanFunctionExpression), functionType(binaryBooleanFunctionExpression.functionType) { - // Nothing to do here. - } - - std::unique_ptr<BaseExpression> BinaryBooleanFunctionExpression::clone() const { - return std::unique_ptr<BaseExpression>(new BinaryBooleanFunctionExpression(this->getLeft()->clone(), this->getRight()->clone(), functionType)); - } - - std::unique_ptr<BaseExpression> BinaryBooleanFunctionExpression::clone(std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) const { - return std::unique_ptr<BaseExpression>(new BinaryBooleanFunctionExpression(this->getLeft()->clone(renaming, variableState), this->getRight()->clone(renaming, variableState), this->functionType)); - } - - bool BinaryBooleanFunctionExpression::getValueAsBool(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const { - bool resultLeft = this->getLeft()->getValueAsBool(variableValues); - bool resultRight = this->getRight()->getValueAsBool(variableValues); - switch(functionType) { - case AND: return resultLeft & resultRight; break; - case OR: return resultLeft | resultRight; break; - default: throw storm::exceptions::ExpressionEvaluationException() << "Cannot evaluate expression: " - << "Unknown boolean binary operator: '" << functionType << "'."; - } - } - - BinaryBooleanFunctionExpression::FunctionType BinaryBooleanFunctionExpression::getFunctionType() const { - return functionType; - } - - void BinaryBooleanFunctionExpression::accept(ExpressionVisitor* visitor) { - visitor->visit(this); - } - - std::string BinaryBooleanFunctionExpression::toString() const { - std::stringstream result; - result << "(" << this->getLeft()->toString(); - switch (functionType) { - case AND: result << " & "; break; - case OR: result << " | "; break; - } - result << this->getRight()->toString() << ")"; - - return result.str(); - } - - } // namespace expressions - } // namespace ir -} // namespace storm diff --git a/src/ir/expressions/BinaryBooleanFunctionExpression.h b/src/ir/expressions/BinaryBooleanFunctionExpression.h deleted file mode 100644 index b0cec5906..000000000 --- a/src/ir/expressions/BinaryBooleanFunctionExpression.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * BinaryBooleanFunctionExpression.h - * - * Created on: 03.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_EXPRESSIONS_BINARYBOOLEANFUNCTIONEXPRESSION_H_ -#define STORM_IR_EXPRESSIONS_BINARYBOOLEANFUNCTIONEXPRESSION_H_ - -#include "BinaryExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - /*! - * A class representing a binary function expression of boolean type. - */ - class BinaryBooleanFunctionExpression : public BinaryExpression { - public: - /*! - * An enum type specifying the different functions applicable. - */ - enum FunctionType {AND, OR}; - - /*! - * Creates a binary boolean function expression tree node with the given children and function type. - * - * @param left The left child of the node. - * @param right The right child of the node. - * @param functionType The operator that is to be applied to the two children. - */ - BinaryBooleanFunctionExpression(std::unique_ptr<BaseExpression>&& left, std::unique_ptr<BaseExpression>&& right, FunctionType functionType); - - /*! - * Copy-constructs from the given expression. - * - * @param binaryBooleanFunctionExpression The expression to copy. - */ - BinaryBooleanFunctionExpression(BinaryBooleanFunctionExpression const& binaryBooleanFunctionExpression); - - virtual std::unique_ptr<BaseExpression> clone() const override; - - virtual std::unique_ptr<BaseExpression> clone(std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) const override; - - virtual bool getValueAsBool(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const override; - - /*! - * Retrieves the operator that is associated with this node. - * - * @param The operator that is associated with this node. - */ - FunctionType getFunctionType() const; - - virtual void accept(ExpressionVisitor* visitor) override; - - virtual std::string toString() const override; - - private: - // The operator that is associated with this node. - FunctionType functionType; - }; - - } // namespace expressions - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_EXPRESSIONS_BINARYBOOLEANFUNCTIONEXPRESSION_H_ */ diff --git a/src/ir/expressions/BinaryExpression.cpp b/src/ir/expressions/BinaryExpression.cpp deleted file mode 100644 index 417cc9301..000000000 --- a/src/ir/expressions/BinaryExpression.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * BinaryBooleanFunctionExpression.cpp - * - * Created on: 10.06.2013 - * Author: Christian Dehnert - */ - -#include "BinaryExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - BinaryExpression::BinaryExpression(ReturnType type, std::unique_ptr<BaseExpression>&& left, std::unique_ptr<BaseExpression>&& right) - : BaseExpression(type), left(std::move(left)), right(std::move(right)) { - // Nothing to do here. - } - - BinaryExpression::BinaryExpression(BinaryExpression const& binaryExpression) : BaseExpression(binaryExpression.getType()), left(binaryExpression.left->clone()), right(binaryExpression.right->clone()) { - // Nothing to do here. - } - - BaseExpression* BinaryExpression::performSubstitution(std::map<std::string, std::reference_wrapper<BaseExpression>> const& substitution) { - // Get the new left successor recursively. - BaseExpression* newLeftSuccessor = left->performSubstitution(substitution); - - // If the left successor changed, we need to update it. If it did not change, this must not be executed, - // because assigning to the unique_ptr will destroy the current successor immediately. - if (newLeftSuccessor != left.get()) { - left = std::unique_ptr<BaseExpression>(newLeftSuccessor); - } - - // Now do the same thing for the right successor. - BaseExpression* newRightSuccessor = right->performSubstitution(substitution); - if (newRightSuccessor != right.get()) { - right = std::unique_ptr<BaseExpression>(newRightSuccessor); - } - - return this; - } - - std::unique_ptr<BaseExpression> const& BinaryExpression::getLeft() const { - return left; - } - - std::unique_ptr<BaseExpression> const& BinaryExpression::getRight() const { - return right; - } - - } // namespace expressions - } // namespace ir -} // namespace storm \ No newline at end of file diff --git a/src/ir/expressions/BinaryExpression.h b/src/ir/expressions/BinaryExpression.h deleted file mode 100644 index 1d6560863..000000000 --- a/src/ir/expressions/BinaryExpression.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * BinaryExpression.h - * - * Created on: 27.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_EXPRESSIONS_BINARYEXPRESSION_H_ -#define STORM_IR_EXPRESSIONS_BINARYEXPRESSION_H_ - -#include "src/ir/expressions/BaseExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - /*! - * A class representing a generic binary expression. - */ - class BinaryExpression : public BaseExpression { - public: - /*! - * Constructs a binary expression with the given type and children. - * @param type The type of the binary expression. - * @param left The left child of the binary expression. - * @param right The right child of the binary expression. - */ - BinaryExpression(ReturnType type, std::unique_ptr<BaseExpression>&& left, std::unique_ptr<BaseExpression>&& right); - - /*! - * Copy-constructs from the given expression. - * - * @param binaryExpression The expression to copy. - */ - BinaryExpression(BinaryExpression const& binaryExpression); - - /*! - * Retrieves the left child of the expression node. - * - * @return The left child of the expression node. - */ - std::unique_ptr<BaseExpression> const& getLeft() const; - - /*! - * Retrieves the right child of the expression node. - * - * @return The right child of the expression node. - */ - std::unique_ptr<BaseExpression> const& getRight() const; - - protected: - virtual BaseExpression* performSubstitution(std::map<std::string, std::reference_wrapper<BaseExpression>> const& substitution) override; - - private: - // The left child of the binary expression. - std::unique_ptr<BaseExpression> left; - - // The right child of the binary expression. - std::unique_ptr<BaseExpression> right; - }; - - } // namespace expressions - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_EXPRESSIONS_BINARYEXPRESSION_H_ */ diff --git a/src/ir/expressions/BinaryNumericalFunctionExpression.cpp b/src/ir/expressions/BinaryNumericalFunctionExpression.cpp deleted file mode 100644 index a80e04ae4..000000000 --- a/src/ir/expressions/BinaryNumericalFunctionExpression.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * BinaryBooleanFunctionExpression.cpp - * - * Created on: 10.06.2013 - * Author: Christian Dehnert - */ - -#include <sstream> -#include <algorithm> - -#include "BinaryNumericalFunctionExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - BinaryNumericalFunctionExpression::BinaryNumericalFunctionExpression(ReturnType type, std::unique_ptr<BaseExpression>&& left, std::unique_ptr<BaseExpression>&& right, FunctionType functionType) - : BinaryExpression(type, std::move(left), std::move(right)), functionType(functionType) { - // Nothing to do here. - } - - BinaryNumericalFunctionExpression::BinaryNumericalFunctionExpression(BinaryNumericalFunctionExpression const& binaryNumericalFunctionExpression) - : BinaryExpression(binaryNumericalFunctionExpression), functionType(binaryNumericalFunctionExpression.functionType) { - // Nothing to do here. - } - - std::unique_ptr<BaseExpression> BinaryNumericalFunctionExpression::clone() const { - return std::unique_ptr<BaseExpression>(new BinaryNumericalFunctionExpression(this->getType(), this->getLeft()->clone(), this->getRight()->clone(), functionType)); - } - - std::unique_ptr<BaseExpression> BinaryNumericalFunctionExpression::clone(std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) const { - return std::unique_ptr<BaseExpression>(new BinaryNumericalFunctionExpression(this->getType(), this->getLeft()->clone(renaming, variableState), this->getRight()->clone(renaming, variableState), this->functionType)); - } - - BinaryNumericalFunctionExpression::FunctionType BinaryNumericalFunctionExpression::getFunctionType() const { - return functionType; - } - - int_fast64_t BinaryNumericalFunctionExpression::getValueAsInt(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const { - if (this->getType() != int_) { - BaseExpression::getValueAsInt(variableValues); - } - - int_fast64_t resultLeft = this->getLeft()->getValueAsInt(variableValues); - int_fast64_t resultRight = this->getRight()->getValueAsInt(variableValues); - switch(functionType) { - case PLUS: return resultLeft + resultRight; break; - case MINUS: return resultLeft - resultRight; break; - case TIMES: return resultLeft * resultRight; break; - case DIVIDE: return resultLeft / resultRight; break; - case MIN: return std::min(resultLeft, resultRight); break; - case MAX: return std::max(resultLeft, resultRight); break; - default: throw storm::exceptions::ExpressionEvaluationException() << "Cannot evaluate expression: " - << "Unknown numeric binary operator: '" << functionType << "'."; - } - } - - double BinaryNumericalFunctionExpression::getValueAsDouble(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const { - if (this->getType() != double_ && this->getType() != int_) { - BaseExpression::getValueAsDouble(variableValues); - } - - double resultLeft = this->getLeft()->getValueAsDouble(variableValues); - double resultRight = this->getRight()->getValueAsDouble(variableValues); - switch(functionType) { - case PLUS: return resultLeft + resultRight; break; - case MINUS: return resultLeft - resultRight; break; - case TIMES: return resultLeft * resultRight; break; - case DIVIDE: return resultLeft / resultRight; break; - case MIN: return std::min(resultLeft, resultRight); break; - case MAX: return std::max(resultLeft, resultRight); break; - default: throw storm::exceptions::ExpressionEvaluationException() << "Cannot evaluate expression: " - << "Unknown numeric binary operator: '" << functionType << "'."; - } - } - - void BinaryNumericalFunctionExpression::accept(ExpressionVisitor* visitor) { - visitor->visit(this); - } - - std::string BinaryNumericalFunctionExpression::toString() const { - std::stringstream result; - switch (functionType) { - case PLUS: result << "(" << this->getLeft()->toString() << " + " << this->getRight()->toString() << ")"; break; - case MINUS: result << "(" << this->getLeft()->toString() << " - " << this->getRight()->toString() << ")"; break; - case TIMES: result << "(" << this->getLeft()->toString() << " * " << this->getRight()->toString() << ")"; break; - case DIVIDE: result << "(" << this->getLeft()->toString() << " / " << this->getRight()->toString() << ")"; break; - case MIN: result << "min(" << this->getLeft()->toString() << ", " << this->getRight()->toString() << ")"; break; - case MAX: result << "max(" << this->getLeft()->toString() << ", " << this->getRight()->toString() << ")"; break; - } - return result.str(); - } - - } // namespace expressions - } // namespace ir -} // namespace storm \ No newline at end of file diff --git a/src/ir/expressions/BinaryNumericalFunctionExpression.h b/src/ir/expressions/BinaryNumericalFunctionExpression.h deleted file mode 100644 index 846ceb99a..000000000 --- a/src/ir/expressions/BinaryNumericalFunctionExpression.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * BinaryFunctionExpression.h - * - * Created on: 03.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_EXPRESSIONS_BINARYFUNCTIONEXPRESSION_H_ -#define STORM_IR_EXPRESSIONS_BINARYFUNCTIONEXPRESSION_H_ - -#include "src/ir/expressions/BinaryExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - /*! - * A class representing a binary function expression of numerical type. - */ - class BinaryNumericalFunctionExpression : public BinaryExpression { - public: - /*! - * An enum type specifying the different functions applicable. - */ - enum FunctionType {PLUS, MINUS, TIMES, DIVIDE, MIN, MAX}; - - /*! - * Creates a binary numerical function expression with the given type, children and function type. - * - * @param type The type of the expression tree node. - * @param left The left child of the expression tree node. - * @param right The right child of the expression tree node. - * @param functionType The function that is applied to the children of this node. - */ - BinaryNumericalFunctionExpression(ReturnType type, std::unique_ptr<BaseExpression>&& left, std::unique_ptr<BaseExpression>&& right, FunctionType functionType); - - /*! - * Performs a deep-copy of the given expression. - * - * @param binaryNumericalFunctionExpression The expression to copy. - */ - BinaryNumericalFunctionExpression(BinaryNumericalFunctionExpression const& binaryNumericalFunctionExpression); - - virtual std::unique_ptr<BaseExpression> clone() const override; - - virtual std::unique_ptr<BaseExpression> clone(std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) const override; - - /*! - * Retrieves the operator that is associated with this node. - * - * @param The operator that is associated with this node. - */ - FunctionType getFunctionType() const; - - virtual int_fast64_t getValueAsInt(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const override; - - virtual double getValueAsDouble(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const override; - - virtual void accept(ExpressionVisitor* visitor) override; - - virtual std::string toString() const override; - - private: - // The operator that is associated with this node. - FunctionType functionType; - }; - - } // namespace expressions - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_EXPRESSIONS_BINARYFUNCTIONEXPRESSION_H_ */ diff --git a/src/ir/expressions/BinaryRelationExpression.cpp b/src/ir/expressions/BinaryRelationExpression.cpp deleted file mode 100644 index 5c08e7f33..000000000 --- a/src/ir/expressions/BinaryRelationExpression.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * BinaryBooleanFunctionExpression.cpp - * - * Created on: 10.06.2013 - * Author: Christian Dehnert - */ - -#include <sstream> - -#include "BinaryRelationExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - BinaryRelationExpression::BinaryRelationExpression(std::unique_ptr<BaseExpression>&& left, std::unique_ptr<BaseExpression>&& right, RelationType relationType) - : BinaryExpression(bool_, std::move(left), std::move(right)), relationType(relationType) { - // Nothing to do here. - } - - BinaryRelationExpression::BinaryRelationExpression(BinaryRelationExpression const& binaryRelationExpression) - : BinaryExpression(binaryRelationExpression), relationType(binaryRelationExpression.relationType) { - // Nothing to do here. - } - - std::unique_ptr<BaseExpression> BinaryRelationExpression::clone() const { - return std::unique_ptr<BaseExpression>(new BinaryRelationExpression(this->getLeft()->clone(), this->getRight()->clone(), relationType)); - } - - std::unique_ptr<BaseExpression> BinaryRelationExpression::clone(std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) const { - return std::unique_ptr<BaseExpression>(new BinaryRelationExpression(this->getLeft()->clone(renaming, variableState), this->getRight()->clone(renaming, variableState), this->relationType)); - } - - bool BinaryRelationExpression::getValueAsBool(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const { - int_fast64_t resultLeft = this->getLeft()->getValueAsInt(variableValues); - int_fast64_t resultRight = this->getRight()->getValueAsInt(variableValues); - switch(relationType) { - case EQUAL: return resultLeft == resultRight; break; - case NOT_EQUAL: return resultLeft != resultRight; break; - case LESS: return resultLeft < resultRight; break; - case LESS_OR_EQUAL: return resultLeft <= resultRight; break; - case GREATER: return resultLeft > resultRight; break; - case GREATER_OR_EQUAL: return resultLeft >= resultRight; break; - default: throw storm::exceptions::ExpressionEvaluationException() << "Cannot evaluate expression: " - << "Unknown boolean binary relation: '" << relationType << "'."; - } - } - - BinaryRelationExpression::RelationType BinaryRelationExpression::getRelationType() const { - return relationType; - } - - void BinaryRelationExpression::accept(ExpressionVisitor* visitor) { - visitor->visit(this); - } - - std::string BinaryRelationExpression::toString() const { - std::stringstream result; - result << "(" << this->getLeft()->toString(); - switch (relationType) { - case EQUAL: result << " = "; break; - case NOT_EQUAL: result << " != "; break; - case LESS: result << " < "; break; - case LESS_OR_EQUAL: result << " <= "; break; - case GREATER: result << " > "; break; - case GREATER_OR_EQUAL: result << " >= "; break; - } - result << this->getRight()->toString() << ")"; - - return result.str(); - } - - } // namespace expressions - } // namespace ir -} // namespace storm \ No newline at end of file diff --git a/src/ir/expressions/BinaryRelationExpression.h b/src/ir/expressions/BinaryRelationExpression.h deleted file mode 100644 index 873cbe5bf..000000000 --- a/src/ir/expressions/BinaryRelationExpression.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * BinaryRelationExpression.h - * - * Created on: 03.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_EXPRESSIONS_BINARYRELATIONEXPRESSION_H_ -#define STORM_IR_EXPRESSIONS_BINARYRELATIONEXPRESSION_H_ - -#include "src/ir/expressions/BinaryExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - /*! - * A class representing a binary relation expression of boolean type. - */ - class BinaryRelationExpression : public BinaryExpression { - public: - /*! - * An enum type specifying the different relations applicable. - */ - enum RelationType {EQUAL, NOT_EQUAL, LESS, LESS_OR_EQUAL, GREATER, GREATER_OR_EQUAL}; - - /*! - * Creates a binary relation expression tree node with the given children and relation type. - * - * @param left The left child of the binary expression. - * @param right The right child of the binary expression. - * @param relationType The type of the relation associated with this node. - */ - BinaryRelationExpression(std::unique_ptr<BaseExpression>&& left, std::unique_ptr<BaseExpression>&& right, RelationType relationType); - - /*! - * Copy-constructs from the given expression. - * - * @param binaryRelationExpression The expression to copy. - */ - BinaryRelationExpression(BinaryRelationExpression const& binaryRelationExpression); - - virtual std::unique_ptr<BaseExpression> clone() const override; - - virtual std::unique_ptr<BaseExpression> clone(std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) const override; - - virtual bool getValueAsBool(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const override; - - /*! - * Retrieves the relation that is associated with this node. - * - * @param The relation that is associated with this node. - */ - RelationType getRelationType() const; - - virtual void accept(ExpressionVisitor* visitor) override; - - virtual std::string toString() const override; - - private: - // The relation operator associated with this node. - RelationType relationType; - }; - - } // namespace expressions - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_EXPRESSIONS_BINARYRELATIONEXPRESSION_H_ */ diff --git a/src/ir/expressions/BooleanConstantExpression.cpp b/src/ir/expressions/BooleanConstantExpression.cpp deleted file mode 100644 index 2022e8796..000000000 --- a/src/ir/expressions/BooleanConstantExpression.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * BooleanConstantExpression.cpp - * - * Created on: 10.06.2013 - * Author: Christian Dehnert - */ - -#include "BooleanConstantExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - BooleanConstantExpression::BooleanConstantExpression(std::string const& constantName) : ConstantExpression<bool>(bool_, constantName) { - // Nothing to do here. - } - - BooleanConstantExpression::BooleanConstantExpression(BooleanConstantExpression const& booleanConstantExpression) : ConstantExpression(booleanConstantExpression) { - // Nothing to do here. - } - - std::unique_ptr<BaseExpression> BooleanConstantExpression::clone() const { - return std::unique_ptr<BaseExpression>(new BooleanConstantExpression(*this)); - } - - std::unique_ptr<BaseExpression> BooleanConstantExpression::clone(std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) const { - return std::unique_ptr<BaseExpression>(new BooleanConstantExpression(*this)); - } - - bool BooleanConstantExpression::getValueAsBool(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const { - if (!this->isDefined()) { - throw storm::exceptions::ExpressionEvaluationException() << "Cannot evaluate expression: " - << "Boolean constant '" << this->getConstantName() << "' is undefined."; - } else { - return this->getValue(); - } - } - - void BooleanConstantExpression::accept(ExpressionVisitor* visitor) { - visitor->visit(this); - } - - } // namespace expressions - } // namespace ir -} // namespace storm \ No newline at end of file diff --git a/src/ir/expressions/BooleanConstantExpression.h b/src/ir/expressions/BooleanConstantExpression.h deleted file mode 100644 index 4838ba595..000000000 --- a/src/ir/expressions/BooleanConstantExpression.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * BooleanConstantExpression.h - * - * Created on: 04.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_EXPRESSIONS_BOOLEANCONSTANTEXPRESSION_H_ -#define STORM_IR_EXPRESSIONS_BOOLEANCONSTANTEXPRESSION_H_ - -#include "ConstantExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - /*! - * A class representing a boolean constant expression. - */ - class BooleanConstantExpression : public ConstantExpression<bool> { - public: - /*! - * Creates a boolean constant expression with the given constant name. - * - * @param constantName The name of the constant to use. - */ - BooleanConstantExpression(std::string const& constantName); - - /*! - * Copy-constructs from the given expression. - * - * @param booleanConstantExpression The expression to copy. - */ - BooleanConstantExpression(BooleanConstantExpression const& booleanConstantExpression); - - virtual std::unique_ptr<BaseExpression> clone() const override; - - virtual std::unique_ptr<BaseExpression> clone(std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) const override; - - virtual bool getValueAsBool(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const override; - - virtual void accept(ExpressionVisitor* visitor) override; - }; - - } // namespace expressions - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_EXPRESSIONS_BOOLEANCONSTANTEXPRESSION_H_ */ diff --git a/src/ir/expressions/BooleanLiteralExpression.cpp b/src/ir/expressions/BooleanLiteralExpression.cpp deleted file mode 100644 index 89e6b7876..000000000 --- a/src/ir/expressions/BooleanLiteralExpression.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * BooleanLiteralExpression.cpp - * - * Created on: 04.01.2013 - * Author: Christian Dehnert - */ - -#include "BooleanLiteralExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - BooleanLiteralExpression::BooleanLiteralExpression(bool value) : BaseExpression(bool_), value(value) { - // Nothing to do here. - } - - BooleanLiteralExpression::BooleanLiteralExpression(BooleanLiteralExpression const& booleanLiteralExpression) - : BaseExpression(booleanLiteralExpression), value(booleanLiteralExpression.value) { - // Nothing to do here. - } - - std::unique_ptr<BaseExpression> BooleanLiteralExpression::clone() const { - return std::unique_ptr<BaseExpression>(new BooleanLiteralExpression(*this)); - } - - std::unique_ptr<BaseExpression> BooleanLiteralExpression::clone(std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) const { - return std::unique_ptr<BaseExpression>(new BooleanLiteralExpression(this->value)); - } - - bool BooleanLiteralExpression::getValueAsBool(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const { - return value; - } - - void BooleanLiteralExpression::accept(ExpressionVisitor* visitor) { - visitor->visit(this); - } - - std::string BooleanLiteralExpression::toString() const { - if (value) { - return std::string("true"); - } else { - return std::string("false"); - } - } - - } // namespace expressions - } // namespace ir -} // namespace storm \ No newline at end of file diff --git a/src/ir/expressions/BooleanLiteralExpression.h b/src/ir/expressions/BooleanLiteralExpression.h deleted file mode 100644 index 1fb747a78..000000000 --- a/src/ir/expressions/BooleanLiteralExpression.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * BooleanLiteralExpression.h - * - * Created on: 03.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_EXPRESSIONS_BOOLEANLITERALEXPRESSION_H_ -#define STORM_IR_EXPRESSIONS_BOOLEANLITERALEXPRESSION_H_ - -#include "src/ir/expressions/BaseExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - /*! - * A class representing a boolean literal. - */ - class BooleanLiteralExpression : public BaseExpression { - public: - /*! - * Creates a boolean literal expression with the given value. - * - * @param value The value for the boolean literal. - */ - BooleanLiteralExpression(bool value); - - /*! - * Copy-constructs from the given expression. - * - * @param booleanLiteralExpression The expression to copy. - */ - BooleanLiteralExpression(BooleanLiteralExpression const& booleanLiteralExpression); - - virtual std::unique_ptr<BaseExpression> clone() const override; - - virtual std::unique_ptr<BaseExpression> clone(std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) const override; - - virtual bool getValueAsBool(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const override; - - virtual void accept(ExpressionVisitor* visitor) override; - - virtual std::string toString() const override; - - private: - // The value of the boolean literal. - bool value; - }; - - } // namespace expressions - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_EXPRESSIONS_BOOLEANLITERALEXPRESSION_H_ */ diff --git a/src/ir/expressions/ConstantExpression.h b/src/ir/expressions/ConstantExpression.h deleted file mode 100644 index dc1763c33..000000000 --- a/src/ir/expressions/ConstantExpression.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * ConstantExpression.h - * - * Created on: 03.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_EXPRESSIONS_CONSTANTEXPRESSION_H_ -#define STORM_IR_EXPRESSIONS_CONSTANTEXPRESSION_H_ - -#include "BaseExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - // A struct for storing whether the constant was defined and if so, what value it holds. - template<class T> - struct ConstantDefinitionStruct { - /*! - * Constructs a structure indicating that the constant has been defined with the given value. - * - * @param value The value with which the constant was defined. - */ - ConstantDefinitionStruct(T value) : value(value), defined(true) { - // Nothing to do here. - } - - /*! - * Constructs a structure indicating that the constant has not yet been defined. - * - * @param value The value with which the constant was defined. - */ - ConstantDefinitionStruct() : value(), defined(false) { - // Nothing to do here. - } - - T value; - bool defined; - }; - - /*! - * A class representing a generic constant expression. - */ - template<class T> - class ConstantExpression : public BaseExpression { - public: - /*! - * Constructs a constant expression of the given type with the given constant name. - * - * @param type The type of the constant. - * @param constantName The name of the constant. - */ - ConstantExpression(ReturnType type, std::string const& constantName) : BaseExpression(type), constantName(constantName), valueStructPointer(new ConstantDefinitionStruct<T>()) { - // Nothing to do here. - } - - /*! - * Copy-constructs from the given expression. - * - * @param constantExpression The expression to copy. - */ - ConstantExpression(ConstantExpression const& constantExpression) : BaseExpression(constantExpression), constantName(constantExpression.constantName), valueStructPointer(constantExpression.valueStructPointer) { - // Nothing to do here. - } - - /*! - * Retrieves the name of the constant. - * - * @return The name of the constant. - */ - std::string const& getConstantName() const { - return constantName; - } - - virtual std::string toString() const override { - std::stringstream result; - if (this->valueStructPointer->defined) { - result << this->valueStructPointer->value; - } else { - result << this->getConstantName(); - } - return result.str(); - } - - /*! - * Retrieves whether the constant is defined or not. - * - * @return True if the constant is defined. - */ - bool isDefined() const { - return this->valueStructPointer->defined; - } - - /*! - * Retrieves the value of the constant if it is defined. - */ - T getValue() const { - return this->valueStructPointer->value; - } - - /*! - * Defines the constant using the given value. - * - * @param value The value to use for defining the constant. - */ - void define(T value) { - this->valueStructPointer->defined = true; - this->valueStructPointer->value = value; - } - - /*! - * Undefines the value that was previously set for this constant (if any). - */ - void undefine() { - this->valueStructPointer->defined = false; - this->valueStructPointer->value = T(); - } - - private: - // The name of the constant. - std::string constantName; - - // The definedness status and (if applicable) the value of the constant. - std::shared_ptr<ConstantDefinitionStruct<T>> valueStructPointer; - }; - - } // namespace expressions - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_EXPRESSIONS_CONSTANTEXPRESSION_H_ */ diff --git a/src/ir/expressions/DoubleConstantExpression.cpp b/src/ir/expressions/DoubleConstantExpression.cpp deleted file mode 100644 index bc29cbf26..000000000 --- a/src/ir/expressions/DoubleConstantExpression.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * DoubleConstantExpression.cpp - * - * Created on: 10.06.2013 - * Author: Christian Dehnert - */ - -#include "DoubleConstantExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - DoubleConstantExpression::DoubleConstantExpression(std::string const& constantName) : ConstantExpression<double>(double_, constantName) { - // Nothing to do here. - } - - DoubleConstantExpression::DoubleConstantExpression(DoubleConstantExpression const& doubleConstantExpression) : ConstantExpression(doubleConstantExpression) { - // Nothing to do here. - } - - std::unique_ptr<BaseExpression> DoubleConstantExpression::clone() const { - return std::unique_ptr<BaseExpression>(new DoubleConstantExpression(*this)); - } - - std::unique_ptr<BaseExpression> DoubleConstantExpression::clone(std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) const { - return std::unique_ptr<BaseExpression>(new DoubleConstantExpression(*this)); - } - - double DoubleConstantExpression::getValueAsDouble(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const { - if (!this->isDefined()) { - throw storm::exceptions::ExpressionEvaluationException() << "Cannot evaluate expression: " - << "Double constant '" << this->getConstantName() << "' is undefined."; - } else { - return this->getValue(); - } - } - - void DoubleConstantExpression::accept(ExpressionVisitor* visitor) { - visitor->visit(this); - } - - } // namespace expressions - } // namespace ir -} // namespace storm \ No newline at end of file diff --git a/src/ir/expressions/DoubleConstantExpression.h b/src/ir/expressions/DoubleConstantExpression.h deleted file mode 100644 index 9eaa6eb18..000000000 --- a/src/ir/expressions/DoubleConstantExpression.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * DoubleConstantExpression.h - * - * Created on: 04.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_EXPRESSIONS_DOUBLECONSTANTEXPRESSION_H_ -#define STORM_IR_EXPRESSIONS_DOUBLECONSTANTEXPRESSION_H_ - -#include "ConstantExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - /*! - * A class representing a constant expression of type double. - */ - class DoubleConstantExpression : public ConstantExpression<double> { - public: - /*! - * Creates a double constant expression with the given constant name. - * - * @param constantName The name of the constant to use. - */ - DoubleConstantExpression(std::string const& constantName); - - /*! - * Copy-constructs from the given expression. - * - * @param doubleConstantExpression The expression to copy. - */ - DoubleConstantExpression(DoubleConstantExpression const& doubleConstantExpression); - - virtual std::unique_ptr<BaseExpression> clone() const override; - - virtual std::unique_ptr<BaseExpression> clone(std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) const override; - - virtual double getValueAsDouble(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const override; - - virtual void accept(ExpressionVisitor* visitor) override; - }; - - } // namespace expressions - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_EXPRESSIONS_DOUBLECONSTANTEXPRESSION_H_ */ diff --git a/src/ir/expressions/DoubleLiteralExpression.cpp b/src/ir/expressions/DoubleLiteralExpression.cpp deleted file mode 100644 index 08663f8f7..000000000 --- a/src/ir/expressions/DoubleLiteralExpression.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * DoubleLiteralExpression.cpp - * - * Created on: 10.06.2013 - * Author: Christian Dehnert - */ - -#include <sstream> - -#include "DoubleLiteralExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - DoubleLiteralExpression::DoubleLiteralExpression(double value) : BaseExpression(double_), value(value) { - // Nothing to do here. - } - - DoubleLiteralExpression::DoubleLiteralExpression(DoubleLiteralExpression const& doubleLiteralExpression) - : BaseExpression(doubleLiteralExpression), value(doubleLiteralExpression.value) { - // Nothing to do here. - } - - std::unique_ptr<BaseExpression> DoubleLiteralExpression::clone() const { - return std::unique_ptr<BaseExpression>(new DoubleLiteralExpression(*this)); - } - - std::unique_ptr<BaseExpression> DoubleLiteralExpression::clone(std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) const { - return std::unique_ptr<BaseExpression>(new DoubleLiteralExpression(this->value)); - } - - double DoubleLiteralExpression::getValueAsDouble(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const { - return value; - } - - void DoubleLiteralExpression::accept(ExpressionVisitor* visitor) { - visitor->visit(this); - } - - std::string DoubleLiteralExpression::toString() const { - std::stringstream result; - result << value; - return result.str(); - } - - } // namespace expressions - } // namespace ir -} // namespace storm \ No newline at end of file diff --git a/src/ir/expressions/DoubleLiteralExpression.h b/src/ir/expressions/DoubleLiteralExpression.h deleted file mode 100644 index 7105858c2..000000000 --- a/src/ir/expressions/DoubleLiteralExpression.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * DoubleLiteralExpression.h - * - * Created on: 03.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_EXPRESSIONS_DOUBLELITERALEXPRESSION_H_ -#define STORM_IR_EXPRESSIONS_DOUBLELITERALEXPRESSION_H_ - -#include "src/ir/expressions/BaseExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - /*! - * A class representing a double literal. - */ - class DoubleLiteralExpression : public BaseExpression { - public: - /*! - * Creates a double literal expression with the given value. - * - * @param value The value for the double literal. - */ - DoubleLiteralExpression(double value); - - /*! - * Copy-constructs from the given expression. - * - * @param doubleLiteralExpression The expression to copy. - */ - DoubleLiteralExpression(DoubleLiteralExpression const& doubleLiteralExpression); - - virtual std::unique_ptr<BaseExpression> clone() const override; - - virtual std::unique_ptr<BaseExpression> clone(std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) const override; - - virtual double getValueAsDouble(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const override; - - virtual void accept(ExpressionVisitor* visitor) override; - - virtual std::string toString() const override; - - private: - // The value of the double literal. - double value; - }; - - } // namespace expressions - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_EXPRESSIONS_DOUBLELITERALEXPRESSION_H_ */ diff --git a/src/ir/expressions/ExpressionVisitor.h b/src/ir/expressions/ExpressionVisitor.h deleted file mode 100644 index d301e9792..000000000 --- a/src/ir/expressions/ExpressionVisitor.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * ExpressionVisitor.h - * - * Created on: 26.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_EXPRESSIONS_EXPRESSIONVISITOR_H_ -#define STORM_IR_EXPRESSIONS_EXPRESSIONVISITOR_H_ - -namespace storm { - namespace ir { - namespace expressions { - - class BaseExpression; - class BinaryBooleanFunctionExpression; - class BinaryNumericalFunctionExpression; - class BinaryRelationExpression; - class BooleanConstantExpression; - class BooleanLiteralExpression; - class DoubleConstantExpression; - class DoubleLiteralExpression; - class IntegerConstantExpression; - class IntegerLiteralExpression; - class UnaryBooleanFunctionExpression; - class UnaryNumericalFunctionExpression; - class VariableExpression; - - class ExpressionVisitor { - public: - virtual void visit(BinaryBooleanFunctionExpression* expression) = 0; - virtual void visit(BinaryNumericalFunctionExpression* expression) = 0; - virtual void visit(BinaryRelationExpression* expression) = 0; - virtual void visit(BooleanConstantExpression* expression) = 0; - virtual void visit(BooleanLiteralExpression* expression) = 0; - virtual void visit(DoubleConstantExpression* expression) = 0; - virtual void visit(DoubleLiteralExpression* expression) = 0; - virtual void visit(IntegerConstantExpression* expression) = 0; - virtual void visit(IntegerLiteralExpression* expression) = 0; - virtual void visit(UnaryBooleanFunctionExpression* expression) = 0; - virtual void visit(UnaryNumericalFunctionExpression* expression) = 0; - virtual void visit(VariableExpression* expression) = 0; - }; - - } // namespace expressions - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_EXPRESSIONS_EXPRESSIONVISITOR_H_ */ diff --git a/src/ir/expressions/Expressions.h b/src/ir/expressions/Expressions.h deleted file mode 100644 index 634a0585d..000000000 --- a/src/ir/expressions/Expressions.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Expressions.h - * - * Created on: 03.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_EXPRESSIONS_EXPRESSIONS_H_ -#define STORM_IR_EXPRESSIONS_EXPRESSIONS_H_ - -#include "BaseExpression.h" -#include "BinaryBooleanFunctionExpression.h" -#include "BinaryNumericalFunctionExpression.h" -#include "BinaryRelationExpression.h" -#include "BooleanLiteralExpression.h" -#include "DoubleLiteralExpression.h" -#include "IntegerLiteralExpression.h" -#include "UnaryBooleanFunctionExpression.h" -#include "UnaryNumericalFunctionExpression.h" -#include "VariableExpression.h" -#include "ConstantExpression.h" -#include "BooleanConstantExpression.h" -#include "IntegerConstantExpression.h" -#include "DoubleConstantExpression.h" - -#endif /* STORM_IR_EXPRESSIONS_EXPRESSIONS_H_ */ diff --git a/src/ir/expressions/IntegerConstantExpression.cpp b/src/ir/expressions/IntegerConstantExpression.cpp deleted file mode 100644 index bcc5cb0eb..000000000 --- a/src/ir/expressions/IntegerConstantExpression.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * IntegerConstantExpression.cpp - * - * Created on: 10.06.2013 - * Author: Christian Dehnert - */ - -#include "IntegerConstantExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - IntegerConstantExpression::IntegerConstantExpression(std::string const& constantName) : ConstantExpression(int_, constantName) { - // Nothing to do here. - } - - IntegerConstantExpression::IntegerConstantExpression(IntegerConstantExpression const& integerConstantExpression) : ConstantExpression(integerConstantExpression) { - // Nothing to do here. - } - - std::unique_ptr<BaseExpression> IntegerConstantExpression::clone() const { - return std::unique_ptr<BaseExpression>(new IntegerConstantExpression(*this)); - } - - std::unique_ptr<BaseExpression> IntegerConstantExpression::clone(std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) const { - return std::unique_ptr<BaseExpression>(new IntegerConstantExpression(*this)); - } - - double IntegerConstantExpression::getValueAsDouble(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const { - return static_cast<double>(getValueAsInt(variableValues)); - } - - int_fast64_t IntegerConstantExpression::getValueAsInt(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const { - if (!this->isDefined()) { - throw storm::exceptions::ExpressionEvaluationException() << "Cannot evaluate expression: " - << "Integer constant '" << this->getConstantName() << "' is undefined."; - } else { - return this->getValue(); - } - } - - void IntegerConstantExpression::accept(ExpressionVisitor* visitor) { - visitor->visit(this); - } - } // namespace expressions - } // namespace ir -} // namespace storm \ No newline at end of file diff --git a/src/ir/expressions/IntegerConstantExpression.h b/src/ir/expressions/IntegerConstantExpression.h deleted file mode 100644 index 04370574d..000000000 --- a/src/ir/expressions/IntegerConstantExpression.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * IntegerConstantExpression.h - * - * Created on: 04.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_EXPRESSIONS_INTEGERCONSTANTEXPRESSION_H_ -#define STORM_IR_EXPRESSIONS_INTEGERCONSTANTEXPRESSION_H_ - -#include "ConstantExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - /*! - * A class representing a constant expression of type integer. - */ - class IntegerConstantExpression : public ConstantExpression<int_fast64_t> { - public: - /*! - * Creates an integer constant expression with the given constant name. - * - * @param constantName The name of the constant to use. - */ - IntegerConstantExpression(std::string const& constantName); - - /*! - * Copy-constructs from the given expression. - * - * @param integerConstantExpression The expression to copy. - */ - IntegerConstantExpression(IntegerConstantExpression const& integerConstantExpression); - - virtual std::unique_ptr<BaseExpression> clone() const override; - - virtual std::unique_ptr<BaseExpression> clone(std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) const override; - - virtual double getValueAsDouble(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const override; - - virtual int_fast64_t getValueAsInt(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const override; - - virtual void accept(ExpressionVisitor* visitor) override; - }; - - } // namespace expressions - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_EXPRESSIONS_INTEGERCONSTANTEXPRESSION_H_ */ diff --git a/src/ir/expressions/IntegerLiteralExpression.cpp b/src/ir/expressions/IntegerLiteralExpression.cpp deleted file mode 100644 index ed3277f18..000000000 --- a/src/ir/expressions/IntegerLiteralExpression.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * IntegerLiteralExpression.cpp - * - * Created on: 03.01.2013 - * Author: Christian Dehnert - */ - -#include <sstream> - -#include "IntegerLiteralExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - IntegerLiteralExpression::IntegerLiteralExpression(int_fast64_t value) : BaseExpression(int_), value(value) { - // Nothing to do here. - } - - IntegerLiteralExpression::IntegerLiteralExpression(IntegerLiteralExpression const& integerLiteralExpression) - : BaseExpression(integerLiteralExpression), value(integerLiteralExpression.value) { - // Nothing to do here. - } - - std::unique_ptr<BaseExpression> IntegerLiteralExpression::clone() const { - return std::unique_ptr<BaseExpression>(new IntegerLiteralExpression(*this)); - } - - std::unique_ptr<BaseExpression> IntegerLiteralExpression::clone(std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) const { - return std::unique_ptr<BaseExpression>(new IntegerLiteralExpression(this->value)); - } - - double IntegerLiteralExpression::getValueAsDouble(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const { - return static_cast<double>(value); - } - - int_fast64_t IntegerLiteralExpression::getValueAsInt(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const { - return value; - } - - void IntegerLiteralExpression::accept(ExpressionVisitor* visitor) { - visitor->visit(this); - } - - std::string IntegerLiteralExpression::toString() const { - std::stringstream result; - result << value; - return result.str(); - } - - } // namespace expressions - } // namespace ir -} // namespace storm \ No newline at end of file diff --git a/src/ir/expressions/IntegerLiteralExpression.h b/src/ir/expressions/IntegerLiteralExpression.h deleted file mode 100644 index cb1041684..000000000 --- a/src/ir/expressions/IntegerLiteralExpression.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * IntegerLiteralExpression.h - * - * Created on: 03.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_EXPRESSIONS_INTEGERLITERALEXPRESSION_H_ -#define STORM_IR_EXPRESSIONS_INTEGERLITERALEXPRESSION_H_ - -#include "src/ir/expressions/BaseExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - /*! - * A class representing an integer literal. - */ - class IntegerLiteralExpression : public BaseExpression { - public: - /*! - * Creates an integer literal expression with the given value. - * - * @param value The value for the integer literal. - */ - IntegerLiteralExpression(int_fast64_t value); - - /*! - * Copy-constructs from the given expression. - * - * @param integerLiteralExpression The expression to copy. - */ - IntegerLiteralExpression(IntegerLiteralExpression const& integerLiteralExpression); - - virtual std::unique_ptr<BaseExpression> clone() const override; - - virtual std::unique_ptr<BaseExpression> clone(std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) const override; - - virtual double getValueAsDouble(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const override; - - virtual int_fast64_t getValueAsInt(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const override; - - virtual void accept(ExpressionVisitor* visitor) override; - - virtual std::string toString() const override; - - private: - // The value of the double literal. - int_fast64_t value; - }; - - } // namespace expressions - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_EXPRESSIONS_INTEGERLITERALEXPRESSION_H_ */ diff --git a/src/ir/expressions/UnaryBooleanFunctionExpression.cpp b/src/ir/expressions/UnaryBooleanFunctionExpression.cpp deleted file mode 100644 index 3b0e6ca03..000000000 --- a/src/ir/expressions/UnaryBooleanFunctionExpression.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * UnaryBooleanFunctionExpression.cpp - * - * Created on: 10.06.2013 - * Author: Christian Dehnert - */ - -#include <sstream> - -#include "UnaryBooleanFunctionExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - UnaryBooleanFunctionExpression::UnaryBooleanFunctionExpression(std::unique_ptr<BaseExpression>&& child, FunctionType functionType) : UnaryExpression(bool_, std::move(child)), functionType(functionType) { - // Nothing to do here. - } - - UnaryBooleanFunctionExpression::UnaryBooleanFunctionExpression(UnaryBooleanFunctionExpression const& unaryBooleanFunctionExpression) : UnaryExpression(unaryBooleanFunctionExpression), functionType(unaryBooleanFunctionExpression.functionType) { - // Nothing to do here. - } - - std::unique_ptr<BaseExpression> UnaryBooleanFunctionExpression::clone() const { - return std::unique_ptr<BaseExpression>(new UnaryBooleanFunctionExpression(this->getChild()->clone(), functionType)); - } - - std::unique_ptr<BaseExpression> UnaryBooleanFunctionExpression::clone(std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) const { - return std::unique_ptr<BaseExpression>(new UnaryBooleanFunctionExpression(this->getChild()->clone(renaming, variableState), this->functionType)); - } - - UnaryBooleanFunctionExpression::FunctionType UnaryBooleanFunctionExpression::getFunctionType() const { - return functionType; - } - - bool UnaryBooleanFunctionExpression::getValueAsBool(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const { - bool resultChild = this->getChild()->getValueAsBool(variableValues); - switch(functionType) { - case NOT: return !resultChild; break; - default: throw storm::exceptions::ExpressionEvaluationException() << "Cannot evaluate expression: " - << "Unknown boolean unary operator: '" << functionType << "'."; - } - } - - void UnaryBooleanFunctionExpression::accept(ExpressionVisitor* visitor) { - visitor->visit(this); - } - - std::string UnaryBooleanFunctionExpression::toString() const { - std::stringstream result; - result << "("; - switch (functionType) { - case NOT: result << "!"; break; - } - result << this->getChild()->toString() << ")"; - - return result.str(); - } - - } // namespace expressions - } // namespace ir -} // namespace storm diff --git a/src/ir/expressions/UnaryBooleanFunctionExpression.h b/src/ir/expressions/UnaryBooleanFunctionExpression.h deleted file mode 100644 index a3087f5e5..000000000 --- a/src/ir/expressions/UnaryBooleanFunctionExpression.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * UnaryBooleanFunctionExpression.h - * - * Created on: 03.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_EXPRESSIONS_UNARYBOOLEANFUNCTIONEXPRESSION_H_ -#define STORM_IR_EXPRESSIONS_UNARYBOOLEANFUNCTIONEXPRESSION_H_ - -#include "UnaryExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - /*! - * A class representing a unary function expression of boolean type. - */ - class UnaryBooleanFunctionExpression : public UnaryExpression { - public: - /*! - * An enum type specifying the different functions applicable. - */ - enum FunctionType {NOT}; - - /*! - * Creates a unary boolean function expression tree node with the given child and function type. - * - * @param child The child of the node. - * @param functionType The operator that is to be applied to the two children. - */ - UnaryBooleanFunctionExpression(std::unique_ptr<BaseExpression>&& child, FunctionType functionType); - - /*! - * Copy-constructs from the given expression. - * - * @param unaryBooleanFunctionExpression The expression to copy. - */ - UnaryBooleanFunctionExpression(UnaryBooleanFunctionExpression const& unaryBooleanFunctionExpression); - - virtual std::unique_ptr<BaseExpression> clone() const override; - - virtual std::unique_ptr<BaseExpression> clone(std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) const override; - - /*! - * Retrieves the operator that is associated with this node. - * - * @param The operator that is associated with this node. - */ - FunctionType getFunctionType() const; - - virtual bool getValueAsBool(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const override; - - virtual void accept(ExpressionVisitor* visitor) override; - - virtual std::string toString() const override; - - private: - // The operator that is associated with this node. - FunctionType functionType; - }; - - } // namespace expressions - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_EXPRESSIONS_UNARYBOOLEANFUNCTIONEXPRESSION_H_ */ diff --git a/src/ir/expressions/UnaryExpression.cpp b/src/ir/expressions/UnaryExpression.cpp deleted file mode 100644 index 31463aff3..000000000 --- a/src/ir/expressions/UnaryExpression.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * UnaryExpression.cpp - * - * Created on: 27.01.2013 - * Author: Christian Dehnert - */ - -#include "UnaryExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - UnaryExpression::UnaryExpression(ReturnType type, std::unique_ptr<BaseExpression>&& child) : BaseExpression(type), child(std::move(child)) { - // Nothing to do here. - } - - UnaryExpression::UnaryExpression(UnaryExpression const& unaryExpression) : BaseExpression(unaryExpression), child(unaryExpression.child->clone()) { - // Nothing to do here. - } - - std::unique_ptr<BaseExpression> const& UnaryExpression::getChild() const { - return child; - } - - BaseExpression* UnaryExpression::performSubstitution(std::map<std::string, std::reference_wrapper<BaseExpression>> const& substitution) { - BaseExpression* newChild = child->performSubstitution(substitution); - - // Only update the child if it changed, because otherwise the child gets destroyed. - if (newChild != child.get()) { - child = std::unique_ptr<BaseExpression>(newChild); - } - - return this; - } - - } // namespace expressions - } // namespace ir -} // namespace storm diff --git a/src/ir/expressions/UnaryExpression.h b/src/ir/expressions/UnaryExpression.h deleted file mode 100644 index cfc685455..000000000 --- a/src/ir/expressions/UnaryExpression.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * UnaryExpression.h - * - * Created on: 27.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_EXPRESSIONS_UNARYEXPRESSION_H_ -#define STORM_IR_EXPRESSIONS_UNARYEXPRESSION_H_ - -#include "BaseExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - /*! - * A class representing a generic unary expression. - */ - class UnaryExpression : public BaseExpression { - public: - /*! - * Constructs a unary expression with the given type and child. - * @param type The type of the unary expression. - * @param right The child of the unary expression. - */ - UnaryExpression(ReturnType type, std::unique_ptr<BaseExpression>&& child); - - /*! - * Copy-constructs from the given expression. - * - * @param unaryExpression The expression to copy. - */ - UnaryExpression(UnaryExpression const& unaryExpression); - - /*! - * Retrieves the child of the expression node. - * - * @return The child of the expression node. - */ - std::unique_ptr<BaseExpression> const& getChild() const; - - protected: - virtual BaseExpression* performSubstitution(std::map<std::string, std::reference_wrapper<BaseExpression>> const& substitution) override; - - private: - // The left child of the unary expression. - std::unique_ptr<BaseExpression> child; - }; - - } // namespace expressions - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_EXPRESSIONS_UNARYEXPRESSION_H_ */ diff --git a/src/ir/expressions/UnaryNumericalFunctionExpression.cpp b/src/ir/expressions/UnaryNumericalFunctionExpression.cpp deleted file mode 100644 index 6b4b1fbb2..000000000 --- a/src/ir/expressions/UnaryNumericalFunctionExpression.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * UnaryFunctionExpression.h - * - * Created on: 03.01.2013 - * Author: Christian Dehnert - */ - -#include <cmath> - -#include "UnaryNumericalFunctionExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - UnaryNumericalFunctionExpression::UnaryNumericalFunctionExpression(ReturnType type, std::unique_ptr<BaseExpression>&& child, FunctionType functionType) : UnaryExpression(type, std::move(child)), functionType(functionType) { - // Nothing to do here. - } - - UnaryNumericalFunctionExpression::UnaryNumericalFunctionExpression(UnaryNumericalFunctionExpression const& unaryNumericalFunctionExpression) : UnaryExpression(unaryNumericalFunctionExpression), functionType(unaryNumericalFunctionExpression.functionType) { - // Nothing to do here. - } - - std::unique_ptr<BaseExpression> UnaryNumericalFunctionExpression::clone() const { - return std::unique_ptr<BaseExpression>(new UnaryNumericalFunctionExpression(this->getType(), this->getChild()->clone(), functionType)); - } - - std::unique_ptr<BaseExpression> UnaryNumericalFunctionExpression::clone(std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) const { - return std::unique_ptr<BaseExpression>(new UnaryNumericalFunctionExpression(this->getType(), this->getChild()->clone(renaming, variableState), this->functionType)); - } - - int_fast64_t UnaryNumericalFunctionExpression::getValueAsInt(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const { - if (this->getType() != int_) { - BaseExpression::getValueAsInt(variableValues); - } - - int_fast64_t resultChild = this->getChild()->getValueAsInt(variableValues); - switch(functionType) { - case MINUS: return -resultChild; break; - case FLOOR: return static_cast<int_fast64_t>(std::floor(resultChild)); break; - case CEIL: return static_cast<int_fast64_t>(std::ceil(resultChild)); break; - default: throw storm::exceptions::ExpressionEvaluationException() << "Cannot evaluate expression: " - << "Unknown numerical unary operator: '" << functionType << "'."; - } - } - - double UnaryNumericalFunctionExpression::getValueAsDouble(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const { - if (this->getType() != double_ && this->getType() != int_) { - BaseExpression::getValueAsDouble(variableValues); - } - - double resultChild = this->getChild()->getValueAsDouble(variableValues); - switch(functionType) { - case MINUS: return -resultChild; break; - case FLOOR: return std::floor(resultChild); break; - case CEIL: return std::ceil(resultChild); break; - default: throw storm::exceptions::ExpressionEvaluationException() << "Cannot evaluate expression: " - << "Unknown numerical unary operator: '" << functionType << "'."; - } - } - - UnaryNumericalFunctionExpression::FunctionType UnaryNumericalFunctionExpression::getFunctionType() const { - return functionType; - } - - void UnaryNumericalFunctionExpression::accept(ExpressionVisitor* visitor) { - visitor->visit(this); - } - - std::string UnaryNumericalFunctionExpression::toString() const { - std::stringstream result; - result << "("; - switch (functionType) { - case MINUS: result << "-"; break; - case FLOOR: result << "floor("; break; - case CEIL: result << "ceil("; break; - } - result << this->getChild()->toString() << ")"; - - return result.str(); - } - - } // namespace expressions - } // namespace ir -} // namespace storm - diff --git a/src/ir/expressions/UnaryNumericalFunctionExpression.h b/src/ir/expressions/UnaryNumericalFunctionExpression.h deleted file mode 100644 index bcb942378..000000000 --- a/src/ir/expressions/UnaryNumericalFunctionExpression.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * UnaryFunctionExpression.h - * - * Created on: 03.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_EXPRESSIONS_UNARYFUNCTIONEXPRESSION_H_ -#define STORM_IR_EXPRESSIONS_UNARYFUNCTIONEXPRESSION_H_ - -#include "UnaryExpression.h" - -namespace storm { - namespace ir { - namespace expressions { - - /*! - * A class representing a unary function expression of numerical type. - */ - class UnaryNumericalFunctionExpression : public UnaryExpression { - public: - /*! - * An enum type specifying the different functions applicable. - */ - enum FunctionType {MINUS, FLOOR, CEIL}; - - /*! - * Creates a unary numerical function expression tree node with the given child and function type. - * - * @param child The child of the node. - * @param functionType The operator that is to be applied to the two children. - */ - UnaryNumericalFunctionExpression(ReturnType type, std::unique_ptr<BaseExpression>&& child, FunctionType functionType); - - /*! - * Copy-constructs from the given expression. - * - * @param unaryNumericalFunctionExpression The expression to copy. - */ - UnaryNumericalFunctionExpression(UnaryNumericalFunctionExpression const& unaryNumericalFunctionExpression); - - virtual std::unique_ptr<BaseExpression> clone() const override; - - virtual std::unique_ptr<BaseExpression> clone(std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) const override; - - virtual int_fast64_t getValueAsInt(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const override; - - virtual double getValueAsDouble(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const override; - - /*! - * Retrieves the operator that is associated with this node. - * - * @param The operator that is associated with this node. - */ - FunctionType getFunctionType() const; - - virtual void accept(ExpressionVisitor* visitor) override; - - virtual std::string toString() const override; - - private: - // The operator that is associated with this node. - FunctionType functionType; - }; - - } // namespace expressions - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_EXPRESSIONS_UNARYFUNCTIONEXPRESSION_H_ */ diff --git a/src/ir/expressions/VariableExpression.cpp b/src/ir/expressions/VariableExpression.cpp deleted file mode 100644 index 9bf09a734..000000000 --- a/src/ir/expressions/VariableExpression.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * VariableExpression.cpp - * - * Created on: 10.06.2013 - * Author: Christian Dehnert - */ - -#include "VariableExpression.h" -#include "src/parser/prismparser/VariableState.h" -#include "src/exceptions/ExpressionEvaluationException.h" - -namespace storm { - namespace ir { - namespace expressions { - - VariableExpression::VariableExpression(ReturnType type, std::string const& variableName) : BaseExpression(type), globalIndex(0), variableName(variableName) { - // Nothing to do here. - } - - VariableExpression::VariableExpression(ReturnType type, uint_fast64_t globalIndex, std::string const& variableName) - : BaseExpression(type), globalIndex(globalIndex), variableName(variableName) { - // Nothing to do here. - } - - VariableExpression::VariableExpression(VariableExpression const& variableExpression) : BaseExpression(variableExpression), globalIndex(variableExpression.globalIndex), variableName(variableExpression.variableName) { - // Nothing to do here. - } - - std::unique_ptr<BaseExpression> VariableExpression::clone() const { - return std::unique_ptr<BaseExpression>(new VariableExpression(*this)); - } - - std::unique_ptr<BaseExpression> VariableExpression::clone(std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) const { - // Perform the proper cloning. - auto renamingPair = renaming.find(this->variableName); - if (renamingPair != renaming.end()) { - if (this->getType() == int_) { - return variableState.getIntegerVariableExpression(renamingPair->second)->clone(); - } else { - return variableState.getBooleanVariableExpression(renamingPair->second)->clone(); - } - } else { - if (this->getType() == int_) { - return variableState.getIntegerVariableExpression(this->variableName)->clone(); - } else { - return variableState.getBooleanVariableExpression(this->variableName)->clone(); - } - } - } - - BaseExpression* VariableExpression::performSubstitution(std::map<std::string, std::reference_wrapper<BaseExpression>> const& substitution) { - // If the name of the variable is a key of the map, we need to replace it. - auto substitutionIterator = substitution.find(variableName); - - if (substitutionIterator != substitution.end()) { - std::unique_ptr<BaseExpression> expressionClone = substitutionIterator->second.get().clone(); - BaseExpression* rawPointer = expressionClone.release(); - return rawPointer; - } else { - // Otherwise, we don't need to replace anything. - return this; - } - } - - void VariableExpression::accept(ExpressionVisitor* visitor) { - visitor->visit(this); - } - - std::string VariableExpression::toString() const { - return this->variableName; - } - - int_fast64_t VariableExpression::getValueAsInt(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const { - if (this->getType() != int_) { - BaseExpression::getValueAsInt(variableValues); - } - - if (variableValues != nullptr) { - return variableValues->second[globalIndex]; - } else { - throw storm::exceptions::ExpressionEvaluationException() << "Cannot evaluate expression involving variables without variable values."; - } - } - - bool VariableExpression::getValueAsBool(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const { - if (this->getType() != bool_) { - BaseExpression::getValueAsBool(variableValues); - } - - if (variableValues != nullptr) { - return variableValues->first[globalIndex]; - } else { - throw storm::exceptions::ExpressionEvaluationException() << "Cannot evaluate expression involving variables without variable values."; - } - } - - double VariableExpression::getValueAsDouble(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const { - if (this->getType() != double_ && this->getType() != int_) { - BaseExpression::getValueAsDouble(variableValues); - } - - // Because only int variables can deliver a double value, we only need to check them. - if (variableValues != nullptr) { - return static_cast<double>(variableValues->second[globalIndex]); - } else { - throw storm::exceptions::ExpressionEvaluationException() << "Cannot evaluate expression with variable '" << variableName << "' of type double."; - } - } - - std::string const& VariableExpression::getVariableName() const { - return variableName; - } - - uint_fast64_t VariableExpression::getGlobalVariableIndex() const { - return this->globalIndex; - } - - } // namespace expressions - } // namespace ir -} // namespace storm diff --git a/src/ir/expressions/VariableExpression.h b/src/ir/expressions/VariableExpression.h deleted file mode 100644 index 99358d377..000000000 --- a/src/ir/expressions/VariableExpression.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * VariableExpression.h - * - * Created on: 03.01.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_IR_EXPRESSIONS_VARIABLEEXPRESSION_H_ -#define STORM_IR_EXPRESSIONS_VARIABLEEXPRESSION_H_ - -#include "BaseExpression.h" - -namespace storm { - - // Forward-declare VariableState. - namespace parser { - namespace prismparser { - class VariableState; - } // namespace prismparser - } // namespace parser - - namespace ir { - namespace expressions { - - /*! - * A class representing a variable in the expression tree. - */ - class VariableExpression : public BaseExpression { - public: - /*! - * Creates a variable expression of the given type with the given name. As this variable has no indices - * it is only meant as a dummy and needs to be replaced with a "full" variable expression. - * - * @param type The type of the variable. - * @param variableName The name of the variable. - */ - VariableExpression(ReturnType type, std::string const& variableName); - - /*! - * Creates a variable expression of the given type with the given name and indices. - * - * @param type The type of the variable. - * @param globalIndex The global (i.e. program-wide) index of the variable. - * @param variableName The name of the variable. - */ - VariableExpression(ReturnType type, uint_fast64_t globalIndex, std::string const& variableName); - - /*! - * Copy-constructs from the given expression. - * - * @param variableExpression The expression to copy. - */ - VariableExpression(VariableExpression const& variableExpression); - - virtual std::unique_ptr<BaseExpression> clone() const override; - - virtual std::unique_ptr<BaseExpression> clone(std::map<std::string, std::string> const& renaming, storm::parser::prism::VariableState const& variableState) const override; - - virtual void accept(ExpressionVisitor* visitor) override; - - virtual std::string toString() const override; - - virtual int_fast64_t getValueAsInt(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const override; - - virtual bool getValueAsBool(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const override; - - virtual double getValueAsDouble(std::pair<std::vector<bool>, std::vector<int_fast64_t>> const* variableValues) const override; - - /*! - * Retrieves the name of the variable. - * - * @return The name of the variable. - */ - std::string const& getVariableName() const; - - /*! - * Retrieves the global (i.e. program-wide) index of the variable. - * - * @return The global index of the variable. - */ - uint_fast64_t getGlobalVariableIndex() const; - - protected: - virtual BaseExpression* performSubstitution(std::map<std::string, std::reference_wrapper<BaseExpression>> const& substitution) override; - - private: - // The global index of the variable. - uint_fast64_t globalIndex; - - // The name of the variable. - std::string variableName; - }; - - } // namespace expressions - } // namespace ir -} // namespace storm - -#endif /* STORM_IR_EXPRESSIONS_VARIABLEEXPRESSION_H_ */ diff --git a/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp b/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp index 82590e31e..b5fa4bad4 100644 --- a/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp +++ b/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp @@ -5,4 +5,4 @@ bool SparseMarkovAutomatonCslModelCheckerOptionsRegistered = storm::settings::Se instance->addOption(storm::settings::OptionBuilder("GmmxxLinearEquationSolver", "digiprecision", "", "Precision used for iterative solving of linear equation systems").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("precision value", "Precision").setDefaultValueDouble(1e-4).addValidationFunctionDouble(storm::settings::ArgumentValidators::doubleRangeValidatorExcluding(0.0, 1.0)).build()).build()); return true; -}); \ No newline at end of file +}); diff --git a/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h b/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h index a4cd0844a..dbcf4d8b1 100644 --- a/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h +++ b/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h @@ -103,10 +103,10 @@ namespace storm { for (auto state : markovianNonGoalStates) { for (auto& element : aMarkovian.getRow(rowIndex)) { ValueType eTerm = std::exp(-exitRates[state] * delta); - if (element.first == rowIndex) { - element.second = (storm::utility::constantOne<ValueType>() - eTerm) * element.second + eTerm; + if (element.getColumn() == rowIndex) { + element.getValue() = (storm::utility::constantOne<ValueType>() - eTerm) * element.getValue() + eTerm; } else { - element.second = (storm::utility::constantOne<ValueType>() - eTerm) * element.second; + element.getValue() = (storm::utility::constantOne<ValueType>() - eTerm) * element.getValue(); } } ++rowIndex; @@ -116,7 +116,7 @@ namespace storm { rowIndex = 0; for (auto state : markovianNonGoalStates) { for (auto& element : aMarkovianToProbabilistic.getRow(rowIndex)) { - element.second = (1 - std::exp(-exitRates[state] * delta)) * element.second; + element.getValue() = (1 - std::exp(-exitRates[state] * delta)) * element.getValue(); } ++rowIndex; } @@ -133,8 +133,8 @@ namespace storm { bMarkovianFixed.push_back(storm::utility::constantZero<ValueType>()); for (auto& element : transitionMatrix.getRowGroup(state)) { - if (goalStates.get(element.first)) { - bMarkovianFixed.back() += (1 - std::exp(-exitRates[state] * delta)) * element.second; + if (goalStates.get(element.getColumn())) { + bMarkovianFixed.back() += (1 - std::exp(-exitRates[state] * delta)) * element.getValue(); } } } @@ -314,13 +314,13 @@ namespace storm { b.push_back(storm::utility::constantZero<ValueType>()); for (auto element : transitionMatrix.getRow(choice)) { - if (statesNotContainedInAnyMec.get(element.first)) { + if (statesNotContainedInAnyMec.get(element.getColumn())) { // If the target state is not contained in an MEC, we can copy over the entry. - sspMatrixBuilder.addNextValue(currentChoice, statesNotInMecsBeforeIndex[element.first], element.second); + sspMatrixBuilder.addNextValue(currentChoice, statesNotInMecsBeforeIndex[element.getColumn()], element.getValue()); } else { // If the target state is contained in MEC i, we need to add the probability to the corresponding field in the vector // so that we are able to write the cumulative probability to the MEC into the matrix. - auxiliaryStateToProbabilityMap[stateToMecIndexMap[element.first]] += element.second; + auxiliaryStateToProbabilityMap[stateToMecIndexMap[element.getColumn()]] += element.getValue(); } } @@ -350,13 +350,13 @@ namespace storm { b.push_back(storm::utility::constantZero<ValueType>()); for (auto element : transitionMatrix.getRow(choice)) { - if (statesNotContainedInAnyMec.get(element.first)) { + if (statesNotContainedInAnyMec.get(element.getColumn())) { // If the target state is not contained in an MEC, we can copy over the entry. - sspMatrixBuilder.addNextValue(currentChoice, statesNotInMecsBeforeIndex[element.first], element.second); + sspMatrixBuilder.addNextValue(currentChoice, statesNotInMecsBeforeIndex[element.getColumn()], element.getValue()); } else { // If the target state is contained in MEC i, we need to add the probability to the corresponding field in the vector // so that we are able to write the cumulative probability to the MEC into the matrix. - auxiliaryStateToProbabilityMap[stateToMecIndexMap[element.first]] += element.second; + auxiliaryStateToProbabilityMap[stateToMecIndexMap[element.getColumn()]] += element.getValue(); } } @@ -428,61 +428,61 @@ namespace storm { */ static ValueType computeLraForMaximalEndComponent(bool min, storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<uint_fast64_t> const& nondeterministicChoiceIndices, storm::storage::BitVector const& markovianStates, std::vector<ValueType> const& exitRates, storm::storage::BitVector const& goalStates, storm::storage::MaximalEndComponent const& mec, uint_fast64_t mecIndex = 0) { std::shared_ptr<storm::solver::LpSolver> solver = storm::utility::solver::getLpSolver("LRA for MEC"); - solver->setModelSense(min ? storm::solver::LpSolver::MAXIMIZE : storm::solver::LpSolver::MINIMIZE); + solver->setModelSense(min ? storm::solver::LpSolver::ModelSense::Maximize : storm::solver::LpSolver::ModelSense::Minimize); // First, we need to create the variables for the problem. - std::map<uint_fast64_t, uint_fast64_t> stateToVariableIndexMap; + std::map<uint_fast64_t, std::string> stateToVariableNameMap; for (auto const& stateChoicesPair : mec) { - stateToVariableIndexMap[stateChoicesPair.first] = solver->createContinuousVariable("x" + std::to_string(stateChoicesPair.first), storm::solver::LpSolver::UNBOUNDED, 0, 0, 0); + std::string variableName = "x" + std::to_string(stateChoicesPair.first); + stateToVariableNameMap[stateChoicesPair.first] = variableName; + solver->addUnboundedContinuousVariable(variableName); } - uint_fast64_t lraValueVariableIndex = solver->createContinuousVariable("k", storm::solver::LpSolver::UNBOUNDED, 0, 0, 1); + solver->addUnboundedContinuousVariable("k", 1); solver->update(); // Now we encode the problem as constraints. - std::vector<uint_fast64_t> variables; - std::vector<double> coefficients; for (auto const& stateChoicesPair : mec) { uint_fast64_t state = stateChoicesPair.first; // Now, based on the type of the state, create a suitable constraint. if (markovianStates.get(state)) { - variables.clear(); - coefficients.clear(); - - variables.push_back(stateToVariableIndexMap.at(state)); - coefficients.push_back(1); + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(stateToVariableNameMap.at(state)); for (auto element : transitionMatrix.getRow(nondeterministicChoiceIndices[state])) { - variables.push_back(stateToVariableIndexMap.at(element.first)); - coefficients.push_back(-element.second); + constraint = constraint - storm::expressions::Expression::createDoubleVariable(stateToVariableNameMap.at(element.getColumn())); } - variables.push_back(lraValueVariableIndex); - coefficients.push_back(storm::utility::constantOne<ValueType>() / exitRates[state]); - - solver->addConstraint("state" + std::to_string(state), variables, coefficients, min ? storm::solver::LpSolver::LESS_EQUAL : storm::solver::LpSolver::GREATER_EQUAL, goalStates.get(state) ? storm::utility::constantOne<ValueType>() / exitRates[state] : storm::utility::constantZero<ValueType>()); + constraint = constraint + storm::expressions::Expression::createDoubleLiteral(storm::utility::constantOne<ValueType>() / exitRates[state]) * storm::expressions::Expression::createDoubleVariable("k"); + storm::expressions::Expression rightHandSide = goalStates.get(state) ? storm::expressions::Expression::createDoubleLiteral(storm::utility::constantOne<ValueType>() / exitRates[state]) : storm::expressions::Expression::createDoubleLiteral(storm::utility::constantZero<ValueType>()); + if (min) { + constraint = constraint <= rightHandSide; + } else { + constraint = constraint >= rightHandSide; + } + solver->addConstraint("state" + std::to_string(state), constraint); } else { // For probabilistic states, we want to add the constraint x_s <= sum P(s, a, s') * x_s' where a is the current action // and the sum ranges over all states s'. for (auto choice : stateChoicesPair.second) { - variables.clear(); - coefficients.clear(); - - variables.push_back(stateToVariableIndexMap.at(state)); - coefficients.push_back(1); - + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(stateToVariableNameMap.at(state)); + for (auto element : transitionMatrix.getRow(choice)) { - variables.push_back(stateToVariableIndexMap.at(element.first)); - coefficients.push_back(-element.second); + constraint = constraint - storm::expressions::Expression::createDoubleVariable(stateToVariableNameMap.at(element.getColumn())); } - solver->addConstraint("state" + std::to_string(state), variables, coefficients, min ? storm::solver::LpSolver::LESS_EQUAL : storm::solver::LpSolver::GREATER_EQUAL, storm::utility::constantZero<ValueType>()); + storm::expressions::Expression rightHandSide = storm::expressions::Expression::createDoubleLiteral(storm::utility::constantZero<ValueType>()); + if (min) { + constraint = constraint <= rightHandSide; + } else { + constraint = constraint >= rightHandSide; + } + solver->addConstraint("state" + std::to_string(state), constraint); } } } solver->optimize(); - return solver->getContinuousValue(lraValueVariableIndex); + return solver->getContinuousValue("k"); } /*! diff --git a/src/modelchecker/prctl/SparseDtmcPrctlModelChecker.h b/src/modelchecker/prctl/SparseDtmcPrctlModelChecker.h index 2f03603d8..9d43dfb52 100644 --- a/src/modelchecker/prctl/SparseDtmcPrctlModelChecker.h +++ b/src/modelchecker/prctl/SparseDtmcPrctlModelChecker.h @@ -393,7 +393,7 @@ public: // Perform the actual matrix-vector multiplication as long as the bound of the formula is met. if (linearEquationSolver != nullptr) { - this->linearEquationSolver->performMatrixVectorMultiplication(this->getModel().getTransitionMatrix(), result, &totalRewardVector, formula.getBound()); + this->linearEquationSolver->performMatrixVectorMultiplication(this->getModel().getTransitionMatrix(), result, &totalRewardVector, static_cast<uint_fast64_t>(formula.getBound())); } else { throw storm::exceptions::InvalidStateException() << "No valid linear equation solver available."; } diff --git a/src/modelchecker/prctl/SparseMdpPrctlModelChecker.h b/src/modelchecker/prctl/SparseMdpPrctlModelChecker.h index 868f78722..c3e2aec58 100644 --- a/src/modelchecker/prctl/SparseMdpPrctlModelChecker.h +++ b/src/modelchecker/prctl/SparseMdpPrctlModelChecker.h @@ -410,7 +410,7 @@ namespace storm { result.resize(this->getModel().getNumberOfStates()); } - this->nondeterministicLinearEquationSolver->performMatrixVectorMultiplication(this->minimumOperatorStack.top(), this->getModel().getTransitionMatrix(), result, &totalRewardVector, formula.getBound()); + this->nondeterministicLinearEquationSolver->performMatrixVectorMultiplication(this->minimumOperatorStack.top(), this->getModel().getTransitionMatrix(), result, &totalRewardVector, static_cast<uint_fast64_t>(formula.getBound())); return result; } diff --git a/src/models/AbstractDeterministicModel.h b/src/models/AbstractDeterministicModel.h index c72b914de..88e0d1006 100644 --- a/src/models/AbstractDeterministicModel.h +++ b/src/models/AbstractDeterministicModel.h @@ -87,9 +87,9 @@ class AbstractDeterministicModel: public AbstractModel<T> { for (uint_fast64_t i = 0; i < this->transitionMatrix.getRowCount(); ++i, ++rowIt) { typename storm::storage::SparseMatrix<T>::const_rows row = this->transitionMatrix.getRow(i); for (auto const& transition : row) { - if (transition.second != storm::utility::constantZero<T>()) { - if (subsystem == nullptr || subsystem->get(transition.first)) { - outStream << "\t" << i << " -> " << transition.first << " [ label= \"" << transition.second << "\" ];" << std::endl; + if (transition.getValue() != storm::utility::constantZero<T>()) { + if (subsystem == nullptr || subsystem->get(transition.getColumn())) { + outStream << "\t" << i << " -> " << transition.getColumn() << " [ label= \"" << transition.getValue() << "\" ];" << std::endl; } } } diff --git a/src/models/AbstractModel.h b/src/models/AbstractModel.h index 301c806d3..05296c584 100644 --- a/src/models/AbstractModel.h +++ b/src/models/AbstractModel.h @@ -220,7 +220,7 @@ class AbstractModel: public std::enable_shared_from_this<AbstractModel<T>> { * @return The number of (non-zero) transitions of the model. */ virtual uint_fast64_t getNumberOfTransitions() const { - return this->getTransitionMatrix().getEntryCount(); + return this->getTransitionMatrix().getNonzeroEntryCount(); } /*! diff --git a/src/models/AbstractNondeterministicModel.h b/src/models/AbstractNondeterministicModel.h index 0058377a6..3a738cbe6 100644 --- a/src/models/AbstractNondeterministicModel.h +++ b/src/models/AbstractNondeterministicModel.h @@ -189,8 +189,8 @@ namespace storm { // Now draw all probabilitic arcs that belong to this nondeterminstic choice. for (auto const& transition : row) { - if (subsystem == nullptr || subsystem->get(transition.first)) { - outStream << "\t\"" << state << "c" << choice << "\" -> " << transition.first << " [ label= \"" << transition.second << "\" ]"; + if (subsystem == nullptr || subsystem->get(transition.getColumn())) { + outStream << "\t\"" << state << "c" << choice << "\" -> " << transition.getColumn() << " [ label= \"" << transition.getValue() << "\" ]"; // If we were given a scheduler to highlight, we do so now. if (scheduler != nullptr) { diff --git a/src/models/Dtmc.h b/src/models/Dtmc.h index b599774f9..eb0e669dd 100644 --- a/src/models/Dtmc.h +++ b/src/models/Dtmc.h @@ -170,7 +170,7 @@ public: for(uint_fast64_t row = 0; row < origMat.getRowCount(); ++row) { if(subSysStates.get(row)){ for(auto const& entry : origMat.getRow(row)) { - if(subSysStates.get(entry.first)) { + if(subSysStates.get(entry.getColumn())) { subSysTransitionCount++; } } @@ -198,10 +198,10 @@ public: if(subSysStates.get(row)){ // Transfer transitions for(auto& entry : origMat.getRow(row)) { - if(subSysStates.get(entry.first)) { - newMatBuilder.addNextValue(newRow, stateMapping[entry.first], entry.second); + if(subSysStates.get(entry.getColumn())) { + newMatBuilder.addNextValue(newRow, stateMapping[entry.getColumn()], entry.getValue()); } else { - rest += entry.second; + rest += entry.getValue(); } } @@ -251,8 +251,8 @@ public: if(subSysStates.get(row)){ // Transfer transition rewards for(auto& entry : this->getTransitionRewardMatrix().getRow(row)) { - if(subSysStates.get(entry.first)) { - newTransRewardsBuilder.addNextValue(newRow, stateMapping[entry.first], entry.second); + if(subSysStates.get(entry.getColumn())) { + newTransRewardsBuilder.addNextValue(newRow, stateMapping[entry.getColumn()], entry.getValue()); } } diff --git a/src/models/MarkovAutomaton.h b/src/models/MarkovAutomaton.h index 1f9250c10..89ce7ccee 100644 --- a/src/models/MarkovAutomaton.h +++ b/src/models/MarkovAutomaton.h @@ -123,7 +123,7 @@ namespace storm { } // Then compute how many rows the new matrix is going to have. - uint_fast64_t newNumberOfRows = this->getNumberOfChoices() - numberOfHybridStates; + //uint_fast64_t newNumberOfRows = this->getNumberOfChoices() - numberOfHybridStates; // Create the matrix for the new transition relation and the corresponding nondeterministic choice vector. storm::storage::SparseMatrixBuilder<T> newTransitionMatrixBuilder(0, 0, 0, true, this->getNumberOfStates() + 1); @@ -147,7 +147,7 @@ namespace storm { for (uint_fast64_t row = this->getTransitionMatrix().getRowGroupIndices()[state] + (this->isHybridState(state) ? 1 : 0); row < this->getTransitionMatrix().getRowGroupIndices()[state + 1]; ++row) { for (auto const& entry : this->transitionMatrix.getRow(row)) { - newTransitionMatrixBuilder.addNextValue(currentChoice, entry.first, entry.second); + newTransitionMatrixBuilder.addNextValue(currentChoice, entry.getColumn(), entry.getValue()); } ++currentChoice; } @@ -220,8 +220,8 @@ namespace storm { // Now draw all probabilitic arcs that belong to this nondeterminstic choice. for (auto const& transition : row) { - if (subsystem == nullptr || subsystem->get(transition.first)) { - outStream << "\t\"" << state << "c" << choice << "\" -> " << transition.first << " [ label= \"" << transition.second << "\" ]"; + if (subsystem == nullptr || subsystem->get(transition.getColumn())) { + outStream << "\t\"" << state << "c" << choice << "\" -> " << transition.getColumn() << " [ label= \"" << transition.getValue() << "\" ]"; // If we were given a scheduler to highlight, we do so now. if (scheduler != nullptr) { @@ -237,8 +237,8 @@ namespace storm { } else { // In this case we are emitting a Markovian choice, so draw the arrows directly to the target states. for (auto const& transition : row) { - if (subsystem == nullptr || subsystem->get(transition.first)) { - outStream << "\t\"" << state << "\" -> " << transition.first << " [ label= \"" << transition.second << " (" << this->exitRates[state] << ")\" ]"; + if (subsystem == nullptr || subsystem->get(transition.getColumn())) { + outStream << "\t\"" << state << "\" -> " << transition.getColumn() << " [ label= \"" << transition.getValue() << " (" << this->exitRates[state] << ")\" ]"; } } } @@ -259,7 +259,7 @@ namespace storm { void turnRatesToProbabilities() { for (auto state : this->markovianStates) { for (auto& transition : this->transitionMatrix.getRowGroup(state)) { - transition.second /= this->exitRates[state]; + transition.getValue() /= this->exitRates[state]; } } } diff --git a/src/parser/AtomicPropositionLabelingParser.cpp b/src/parser/AtomicPropositionLabelingParser.cpp index 427d64ba9..0dac9a8c9 100644 --- a/src/parser/AtomicPropositionLabelingParser.cpp +++ b/src/parser/AtomicPropositionLabelingParser.cpp @@ -7,192 +7,171 @@ #include "src/parser/AtomicPropositionLabelingParser.h" -#include <errno.h> -#include <time.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <locale.h> - -#include <cstdlib> -#include <cstdio> #include <cstring> #include <string> -#include <clocale> #include <iostream> +#include "src/utility/cstring.h" +#include "src/parser/MappedFile.h" #include "src/exceptions/WrongFormatException.h" #include "src/exceptions/FileIoException.h" -#include "src/utility/OsDetection.h" #include "log4cplus/logger.h" #include "log4cplus/loggingmacros.h" extern log4cplus::Logger logger; namespace storm { -namespace parser { + namespace parser { + using namespace storm::utility::cstring; -/*! - * Reads a label file and puts the result in a labeling structure. - * - * Labelings created with this method have to be freed with the delete operator. - * @param node_count the number of states. - * @param filename input .lab file's name. - * @return The pointer to the created labeling object. - */ -storm::models::AtomicPropositionsLabeling AtomicPropositionLabelingParser(uint_fast64_t node_count, std::string const & filename) { - /* - * Open file. - */ - if (!storm::parser::fileExistsAndIsReadable(filename.c_str())) { - LOG4CPLUS_ERROR(logger, "Error while parsing " << filename << ": File does not exist or is not readable."); - throw storm::exceptions::FileIoException() << "The supplied Labeling input file \"" << filename << "\" does not exist or is not readable by this process."; - } - - /* - * Find out about the used line endings. - */ - SupportedLineEndingsEnum lineEndings = findUsedLineEndings(filename, true); - - MappedFile file(filename.c_str()); - char* buf = file.data; - - /* - * First run: obtain number of propositions. - */ - char separator[5];// = " \r\n\t"; - storm::parser::getMatchingSeparatorString(separator, sizeof(separator), lineEndings); - - bool foundDecl = false, foundEnd = false; - uint_fast32_t proposition_count = 0; - { - size_t cnt = 0; - /* - * Iterate over tokens until we hit #END or end of file. - */ - while(buf[0] != '\0') { - buf += cnt; - cnt = strcspn(buf, separator); // position of next separator - if (cnt > 0) { - /* - * next token is #DECLARATION: just skip it - * next token is #END: stop search - * otherwise increase proposition_count - */ - if (strncmp(buf, "#DECLARATION", cnt) == 0) { - foundDecl = true; - continue; - } else if (strncmp(buf, "#END", cnt) == 0) { - foundEnd = true; - break; + storm::models::AtomicPropositionsLabeling AtomicPropositionLabelingParser::parseAtomicPropositionLabeling(uint_fast64_t stateCount, std::string const & filename) { + + // Open the given file. + if (!MappedFile::fileExistsAndIsReadable(filename.c_str())) { + LOG4CPLUS_ERROR(logger, "Error while parsing " << filename << ": File does not exist or is not readable."); + throw storm::exceptions::FileIoException() << "The supplied Labeling input file \"" << filename << "\" does not exist or is not readable by this process."; + } + + MappedFile file(filename.c_str()); + char* buf = file.getData(); + + // First pass: Count the number of propositions. + bool foundDecl = false, foundEnd = false; + uint_fast32_t proposition_count = 0; + size_t cnt = 0; + + // Iterate over tokens until we hit #END or the end of the file. + while(buf[0] != '\0') { + + //Move the buffer to the beginning of the next word. + buf += cnt; + buf = trimWhitespaces(buf); + + // Get the number of characters until the next separator. + cnt = skipWord(buf) - buf; + if (cnt > 0) { + + // If the next token is #DECLARATION: Just skip it. + // If the next token is #END: Stop the search. + // Otherwise increase proposition_count. + if (strncmp(buf, "#DECLARATION", cnt) == 0) { + foundDecl = true; + continue; + } else if (strncmp(buf, "#END", cnt) == 0) { + foundEnd = true; + break; + } + proposition_count++; } - proposition_count++; - } else { - buf++; // next char is separator, one step forward } - } - /* - * If #DECLARATION or #END were not found, the file format is wrong - */ - if (!(foundDecl && foundEnd)) { - LOG4CPLUS_ERROR(logger, "Wrong file format in (" << filename << "). File header is corrupted."); - if (!foundDecl) LOG4CPLUS_ERROR(logger, "\tDid not find #DECLARATION token."); - if (!foundEnd) LOG4CPLUS_ERROR(logger, "\tDid not find #END token."); - throw storm::exceptions::WrongFormatException(); - } - } - - /* - * create labeling object with given node and proposition count - */ - storm::models::AtomicPropositionsLabeling labeling(node_count, proposition_count); - - /* - * second run: add propositions and node labels to labeling - * - * first thing to do: reset file pointer - */ - buf = file.data; - { - /* - * load propositions - * As we already checked the file format, we can be a bit sloppy here... - */ - char proposition[128]; // buffer for proposition names - size_t cnt = 0; - do { - buf += cnt; - cnt = strcspn(buf, separator); // position of next separator - if (cnt >= sizeof(proposition)) { - /* - * if token is longer than our buffer, the following strncpy code might get risky... - */ - LOG4CPLUS_ERROR(logger, "Wrong file format in (" << filename << "). Atomic proposition with length > " << (sizeof(proposition)-1) << " was found."); + // If #DECLARATION or #END have not been found, the file format is wrong. + if (!(foundDecl && foundEnd)) { + LOG4CPLUS_ERROR(logger, "Wrong file format in (" << filename << "). File header is corrupted."); + if (!foundDecl) LOG4CPLUS_ERROR(logger, "\tDid not find #DECLARATION token."); + if (!foundEnd) LOG4CPLUS_ERROR(logger, "\tDid not find #END token."); throw storm::exceptions::WrongFormatException(); - } else if (cnt > 0) { - /* - * next token is: #DECLARATION: just skip it - * next token is: #END: stop search - * otherwise: copy token to buffer, append trailing null byte and hand it to labeling - */ - if (strncmp(buf, "#DECLARATION", cnt) == 0) continue; - if (strncmp(buf, "#END", cnt) == 0) break; - strncpy(proposition, buf, cnt); - proposition[cnt] = '\0'; - labeling.addAtomicProposition(proposition); - } else { - cnt = 1; // next char is separator, one step forward } - } while (cnt > 0); - /* - * Right here, the buf pointer is still pointing to our last token, - * i.e. to #END. We want to skip this one... - */ - buf += 4; - } - - { - /* - * now parse node label assignments - */ - uint_fast64_t node; - char proposition[128]; - size_t cnt; - /* - * iterate over nodes - */ - while (buf[0] != '\0') { - /* - * parse node number, then iterate over propositions - */ - node = checked_strtol(buf, &buf); - while ((buf[0] != '\r') && (buf[0] != '\n') && (buf[0] != '\0')) { - cnt = strcspn(buf, separator); - if (cnt == 0) { - /* - * next char is a separator - * if it's a newline, we continue with next node - * otherwise we skip it and try again - */ - if (buf[0] == '\n' || buf[0] == '\r') break; - buf++; - } else { - /* - * copy proposition to buffer and add it to labeling - */ + + + // Create labeling object with given node and proposition count. + storm::models::AtomicPropositionsLabeling labeling(stateCount, proposition_count); + + // Second pass: Add propositions and node labels to labeling. + // First thing to do: Reset the file pointer. + buf = file.getData(); + + // Prepare a buffer for proposition names. + char proposition[128]; + cnt = 0; + + // Parse proposition names. + // As we already checked the file header, we know that #DECLARATION and #END are tokens in the character stream. + while(buf[0] != '\0') { + + //Move the buffer to the beginning of the next word. + buf += cnt; + buf = trimWhitespaces(buf); + + // Get the number of characters until the next separator. + cnt = skipWord(buf) - buf; + + if (cnt >= sizeof(proposition)) { + + // if token is longer than our buffer, the following strncpy code might get risky... + LOG4CPLUS_ERROR(logger, "Wrong file format in (" << filename << "). Atomic proposition with length > " << (sizeof(proposition)-1) << " was found."); + throw storm::exceptions::WrongFormatException(); + + } else if (cnt > 0) { + + // If the next token is #DECLARATION: Just skip it. + if (strncmp(buf, "#DECLARATION", cnt) == 0) continue; + + // If the next token is #END: Stop the search. + if (strncmp(buf, "#END", cnt) == 0) break; + + // Otherwise copy the token to the buffer, append a trailing null byte and hand it to labeling. strncpy(proposition, buf, cnt); proposition[cnt] = '\0'; - labeling.addAtomicPropositionToState(proposition, node); - buf += cnt; + labeling.addAtomicProposition(proposition); + } + } + + // At this point, the pointer buf is still pointing to our last token, i.e. to #END. + // We want to skip it. + buf += 4; + + // Now eliminate remaining whitespaces such as empty lines and start parsing. + buf = trimWhitespaces(buf); + + uint_fast64_t state = 0; + uint_fast64_t lastState = (uint_fast64_t)-1; + cnt = 0; + + // Now parse the assignments of labels to nodes. + while (buf[0] != '\0') { + + // Parse the state number and iterate over its labels (atomic propositions). + // Stop at the end of the line. + state = checked_strtol(buf, &buf); + + // If the state has already been read or skipped once there might be a problem with the file (doubled lines, or blocks). + if(state <= lastState && lastState != (uint_fast64_t)-1) { + LOG4CPLUS_ERROR(logger, "Wrong file format in (" << filename << "). State " << state << " was found but has already been read or skipped previously."); + throw storm::exceptions::WrongFormatException() << "State " << state << " was found but has already been read or skipped previously."; + } + + while ((buf[0] != '\r') && (buf[0] != '\n') && (buf[0] != '\0')) { + cnt = skipWord(buf) - buf; + if (cnt == 0) { + + // The next character is a separator. + // If it is a line separator, we continue with next node. + // Otherwise, we skip it and try again. + if (buf[0] == '\n' || buf[0] == '\r') break; + buf++; + } else { + + // Copy the label to the buffer, null terminate it and add it to labeling. + strncpy(proposition, buf, cnt); + proposition[cnt] = '\0'; + + // Has the label been declared in the header? + if(!labeling.containsAtomicProposition(proposition)) { + LOG4CPLUS_ERROR(logger, "Wrong file format in (" << filename << "). Atomic proposition" << proposition << " was found but not declared."); + throw storm::exceptions::WrongFormatException(); + } + labeling.addAtomicPropositionToState(proposition, state); + buf += cnt; + } } + buf = trimWhitespaces(buf); + lastState = state; } - buf = storm::parser::trimWhitespaces(buf); + + return labeling; } - } - - return labeling; -} -} // namespace parser + } // namespace parser } // namespace storm diff --git a/src/parser/AtomicPropositionLabelingParser.h b/src/parser/AtomicPropositionLabelingParser.h index 578b1d9ae..19d172235 100644 --- a/src/parser/AtomicPropositionLabelingParser.h +++ b/src/parser/AtomicPropositionLabelingParser.h @@ -1,23 +1,33 @@ -#ifndef STORM_PARSER_LABPARSER_H_ -#define STORM_PARSER_LABPARSER_H_ +#ifndef STORM_PARSER_ATOMICPROPOSITIONLABELINGPARSER_H_ +#define STORM_PARSER_ATOMICPROPOSITIONLABELINGPARSER_H_ #include "src/models/AtomicPropositionsLabeling.h" #include <cstdint> -#include "src/parser/Parser.h" +namespace storm { + namespace parser { -#include <memory> + /*! + * This class can be used to parse a labeling file. + * + * Since the labeling is state based, the same label parser can be used for all models. + */ + class AtomicPropositionLabelingParser { -namespace storm { -namespace parser { + public: + + /*! + * Reads a label file and puts the result in an AtomicPropositionsLabeling object. + * + * @param stateCount The number of states of the model to be labeled. + * @param filename The path and name of the labeling (.lab) file. + * @return The parsed labeling as an AtomicPropositionsLabeling object. + */ + static storm::models::AtomicPropositionsLabeling parseAtomicPropositionLabeling(uint_fast64_t stateCount, std::string const &filename); -/*! - * @brief Load label file and return initialized AtomicPropositionsLabeling object. - * - */ -storm::models::AtomicPropositionsLabeling AtomicPropositionLabelingParser(uint_fast64_t node_count, std::string const &filename); + }; -} // namespace parser + } // namespace parser } // namespace storm -#endif /* STORM_PARSER_LABPARSER_H_ */ +#endif /* STORM_PARSER_ATOMICPROPOSITIONLABELINGPARSER_H_ */ diff --git a/src/parser/AutoParser.cpp b/src/parser/AutoParser.cpp new file mode 100644 index 000000000..808d36f0b --- /dev/null +++ b/src/parser/AutoParser.cpp @@ -0,0 +1,101 @@ +/* + * AutoParser.cpp + * + * Created on: Jan 20, 2014 + * Author: Manuel S. Weiand + */ + +#include "src/parser/AutoParser.h" + +#include "src/parser/MappedFile.h" + +#include "src/parser/DeterministicModelParser.h" +#include "src/parser/NondeterministicModelParser.h" +#include "src/parser/MarkovAutomatonParser.h" +#include "src/exceptions/WrongFormatException.h" + +#include "src/utility/cstring.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace parser { + + using namespace storm::utility::cstring; + + std::shared_ptr<storm::models::AbstractModel<double>> AutoParser::parseModel(std::string const & transitionsFilename, + std::string const & labelingFilename, + std::string const & stateRewardFilename, + std::string const & transitionRewardFilename) { + + // Find and parse the model type hint. + storm::models::ModelType type = AutoParser::analyzeHint(transitionsFilename); + + // In case the hint string is unknown or could not be found, throw an exception. + if (type == storm::models::Unknown) { + LOG4CPLUS_ERROR(logger, "Could not determine file type of " << transitionsFilename << "."); + LOG4CPLUS_ERROR(logger, "The first line of the file should contain a format hint. Please fix your file and try again."); + throw storm::exceptions::WrongFormatException() << "Could not determine type of file " << transitionsFilename; + } else { + LOG4CPLUS_INFO(logger, "Model type seems to be " << type); + } + + // Do the actual parsing. + std::shared_ptr<storm::models::AbstractModel<double>> model; + switch (type) { + case storm::models::DTMC: { + model.reset(new storm::models::Dtmc<double>(std::move(DeterministicModelParser::parseDtmc(transitionsFilename, labelingFilename, stateRewardFilename, transitionRewardFilename)))); + break; + } + case storm::models::CTMC: { + model.reset(new storm::models::Ctmc<double>(std::move(DeterministicModelParser::parseCtmc(transitionsFilename, labelingFilename, stateRewardFilename, transitionRewardFilename)))); + break; + } + case storm::models::MDP: { + model.reset(new storm::models::Mdp<double>(std::move(NondeterministicModelParser::parseMdp(transitionsFilename, labelingFilename, stateRewardFilename, transitionRewardFilename)))); + break; + } + case storm::models::CTMDP: { + model.reset(new storm::models::Ctmdp<double>(std::move(NondeterministicModelParser::parseCtmdp(transitionsFilename, labelingFilename, stateRewardFilename, transitionRewardFilename)))); + break; + } + case storm::models::MA: { + model.reset(new storm::models::MarkovAutomaton<double>(storm::parser::MarkovAutomatonParser::parseMarkovAutomaton(transitionsFilename, labelingFilename, stateRewardFilename, transitionRewardFilename))); + break; + } + default: + LOG4CPLUS_WARN(logger, "Unknown/Unhandled Model Type which cannot be parsed."); // Unknown + } + + return model; + } + + storm::models::ModelType AutoParser::analyzeHint(std::string const & filename) { + storm::models::ModelType hintType = storm::models::Unknown; + + // Open the file. + MappedFile file(filename.c_str()); + char* buf = file.getData(); + + // Find and read in the hint. + char hint[65]; + // %60s => The input hint can be AT MOST 60 chars long. + #ifdef WINDOWS + sscanf_s(buf, "%60s", hint, sizeof(hint)); + #else + sscanf(buf, "%60s", hint); + #endif + + for (char* c = hint; *c != '\0'; c++) *c = toupper(*c); + + // Check if the hint value is known and store the appropriate enum value. + if (strncmp(hint, "DTMC", sizeof(hint)) == 0) hintType = storm::models::DTMC; + else if (strncmp(hint, "CTMC", sizeof(hint)) == 0) hintType = storm::models::CTMC; + else if (strncmp(hint, "MDP", sizeof(hint)) == 0) hintType = storm::models::MDP; + else if (strncmp(hint, "CTMDP", sizeof(hint)) == 0) hintType = storm::models::CTMDP; + else if (strncmp(hint, "MA", sizeof(hint)) == 0) hintType = storm::models::MA; + + return hintType; + } + + } // namespace parser +} // namespace storm diff --git a/src/parser/AutoParser.h b/src/parser/AutoParser.h index 32baaae40..993cbe6bb 100644 --- a/src/parser/AutoParser.h +++ b/src/parser/AutoParser.h @@ -1,140 +1,63 @@ #ifndef STORM_PARSER_AUTOPARSER_H_ #define STORM_PARSER_AUTOPARSER_H_ -#include "src/parser/Parser.h" #include "src/models/AbstractModel.h" -#include "src/exceptions/WrongFormatException.h" -#include "src/models/AbstractModel.h" -#include "src/parser/DeterministicModelParser.h" -#include "src/parser/NondeterministicModelParser.h" -#include "src/parser/MarkovAutomatonParser.h" - -#include <memory> -#include <iostream> -#include <utility> #include <string> -#include <cctype> namespace storm { -namespace parser { - -/*! - * @brief Checks the given files and parses the model within these files. - * - * This parser analyzes the format hint in the first line of the transition - * file. If this is a valid format, it will use the parser for this format, - * otherwise it will throw an exception. - * - * When the files are parsed successfully, the parsed ModelType and Model - * can be obtained via getType() and getModel<ModelClass>(). - */ -template<class T> -class AutoParser { - public: - AutoParser(std::string const & transitionSystemFile, std::string const & labelingFile, - std::string const & stateRewardFile = "", std::string const & transitionRewardFile = "") : model(nullptr) { - storm::models::ModelType type = this->analyzeHint(transitionSystemFile); - - if (type == storm::models::Unknown) { - LOG4CPLUS_ERROR(logger, "Could not determine file type of " << transitionSystemFile << "."); - LOG4CPLUS_ERROR(logger, "The first line of the file should contain a format hint. Please fix your file and try again."); - throw storm::exceptions::WrongFormatException() << "Could not determine type of file " << transitionSystemFile; - } else { - LOG4CPLUS_INFO(logger, "Model type seems to be " << type); - } - - // Do actual parsing - switch (type) { - case storm::models::DTMC: { - this->model.reset(new storm::models::Dtmc<double>(std::move(DeterministicModelParserAsDtmc(transitionSystemFile, labelingFile, stateRewardFile, transitionRewardFile)))); - break; - } - case storm::models::CTMC: { - this->model.reset(new storm::models::Ctmc<double>(std::move(DeterministicModelParserAsCtmc(transitionSystemFile, labelingFile, stateRewardFile, transitionRewardFile)))); - break; - } - case storm::models::MDP: { - this->model.reset(new storm::models::Mdp<double>(std::move(NondeterministicModelParserAsMdp(transitionSystemFile, labelingFile, stateRewardFile, transitionRewardFile)))); - break; - } - case storm::models::CTMDP: { - this->model.reset(new storm::models::Ctmdp<double>(std::move(NondeterministicModelParserAsCtmdp(transitionSystemFile, labelingFile, stateRewardFile, transitionRewardFile)))); - break; - } - case storm::models::MA: { - this->model.reset(new storm::models::MarkovAutomaton<double>(storm::parser::MarkovAutomatonParser::parseMarkovAutomaton(transitionSystemFile, labelingFile, stateRewardFile, transitionRewardFile))); - break; - } - default: ; // Unknown - } - - - if (!this->model) { - LOG4CPLUS_WARN(logger, "Unknown/Unhandled Model Type. Model is still null."); - } - } - - /*! - * @brief Returns the type of model that was parsed. - */ - storm::models::ModelType getType() { - if (this->model) { - return this->model->getType(); - } else { - return storm::models::Unknown; - } - } - - /*! - * @brief Returns the model with the given type. - */ - template <typename Model> - std::shared_ptr<Model> getModel() { - return this->model->template as<Model>(); - } + /*! + * Contains all file parsers and helper classes. + * + * This namespace contains everything needed to load data files (like + * atomic propositions, transition systems, formulas, etc.) including + * methods for efficient file access (see MappedFile). + */ + namespace parser { - private: - /*! - * @brief Open file and read file format hint. + * This class automatically chooses the correct parser for the given files and returns the corresponding model. + * The choice of the parser is made using the model hint at the beginning of the given transition file. */ - storm::models::ModelType analyzeHint(const std::string& filename) { - storm::models::ModelType hintType = storm::models::Unknown; - - // Parse the File and check for the Line Endings - storm::parser::SupportedLineEndingsEnum lineEndings = storm::parser::findUsedLineEndings(filename); - - // Open file - MappedFile file(filename.c_str()); - char* buf = file.data; - - // parse hint - char hint[128]; - // %20s => The Input Hint can be AT MOST 120 chars long - storm::parser::scanForModelHint(hint, sizeof(hint), buf, lineEndings); - - for (char* c = hint; *c != '\0'; c++) *c = toupper(*c); - - // check hint - if (strncmp(hint, "DTMC", sizeof(hint)) == 0) hintType = storm::models::DTMC; - else if (strncmp(hint, "CTMC", sizeof(hint)) == 0) hintType = storm::models::CTMC; - else if (strncmp(hint, "MDP", sizeof(hint)) == 0) hintType = storm::models::MDP; - else if (strncmp(hint, "CTMDP", sizeof(hint)) == 0) hintType = storm::models::CTMDP; - else if (strncmp(hint, "MA", sizeof(hint)) == 0) hintType = storm::models::MA; - - return hintType; - } + class AutoParser { + public: + + /*! + * Checks the given files and parses the model within these files. + * + * This parser analyzes the format hint in the first line of the transition file. + * If this is a valid format, it will use the parser for this format, otherwise it will throw an exception. + * + * When the files are parsed successfully, a shared pointer owning the resulting model is returned. + * The concrete model can be obtained using the as<Type>() member of the AbstractModel class. + * + * @note The number of states of the model is determined by the transitions file. + * The labeling file may therefore not contain labels of states that are not contained in the transitions file. + * + * @param transitionsFilename The path and name of the file containing the transitions of the model. + * @param labelingFilename The path and name of the file containing the labels for the states of the model. + * @param stateRewardFilename The path and name of the file that contains the state reward of the model. This file is optional. + * @param transitionRewardFilename The path and name of the file that contains the transition rewards of the model. This file is optional. + * @return A shared_ptr containing the resulting model. + */ + static std::shared_ptr<storm::models::AbstractModel<double>> parseModel(std::string const & transitionsFilename, + std::string const & labelingFilename, + std::string const & stateRewardFilename = "", + std::string const & transitionRewardFilename = ""); + + private: + + /*! + * Opens the given file and parses the file format hint. + * + * @param filename The path and name of the file that is to be analysed. + * @return The type of the model as an enum value. + */ + static storm::models::ModelType analyzeHint(std::string const & filename); + }; - /*! - * @brief Pointer to a parser that has parsed the given transition system. - */ - std::shared_ptr<storm::models::AbstractModel<T>> model; -}; - -} // namespace parser - + } // namespace parser } // namespace storm #endif /* STORM_PARSER_AUTOPARSER_H_ */ diff --git a/src/parser/CslParser.h b/src/parser/CslParser.h index 8dd4315d3..2ef4c64e3 100644 --- a/src/parser/CslParser.h +++ b/src/parser/CslParser.h @@ -8,8 +8,6 @@ #ifndef STORM_PARSER_CSLPARSER_H_ #define STORM_PARSER_CSLPARSER_H_ -#include "Parser.h" - #include "src/formula/Csl.h" #include <functional> diff --git a/src/parser/DeterministicModelParser.cpp b/src/parser/DeterministicModelParser.cpp index 86e150f67..379858b24 100644 --- a/src/parser/DeterministicModelParser.cpp +++ b/src/parser/DeterministicModelParser.cpp @@ -15,63 +15,45 @@ #include "src/parser/SparseStateRewardParser.h" namespace storm { -namespace parser { + namespace parser { -/*! - * Parses a transition file and a labeling file - * Note that the labeling file may have at most as many nodes as the transition file! - * - * @param transitionSystemFile String containing the location of the transition file (....tra) - * @param labelingFile String containing the location of the labeling file (....lab) - * @param stateRewardFile String containing the location of the state reward file (...srew) - * @param transitionRewardFile String containing the location of the transition reward file (...trew) - */ -DeterministicModelParserResultContainer<double> parseDeterministicModel(std::string const & transitionSystemFile, std::string const & labelingFile, - std::string const & stateRewardFile, std::string const & transitionRewardFile) { - storm::storage::SparseMatrix<double> resultTransitionSystem(std::move(storm::parser::DeterministicSparseTransitionParser(transitionSystemFile))); + DeterministicModelParser::Result DeterministicModelParser::parseDeterministicModel(std::string const & transitionsFilename, std::string const & labelingFilename, std::string const & stateRewardFilename, std::string const & transitionRewardFilename) { - uint_fast64_t stateCount = resultTransitionSystem.getColumnCount(); + // Parse the transitions. + storm::storage::SparseMatrix<double> transitions(std::move(storm::parser::DeterministicSparseTransitionParser::parseDeterministicTransitions(transitionsFilename))); - uint_fast64_t rowCount = resultTransitionSystem.getRowCount(); + uint_fast64_t stateCount = transitions.getColumnCount(); - storm::models::AtomicPropositionsLabeling resultLabeling(std::move(storm::parser::AtomicPropositionLabelingParser(stateCount, labelingFile))); + // Parse the state labeling. + storm::models::AtomicPropositionsLabeling labeling(std::move(storm::parser::AtomicPropositionLabelingParser::parseAtomicPropositionLabeling(stateCount, labelingFilename))); - DeterministicModelParserResultContainer<double> result(std::move(resultTransitionSystem), std::move(resultLabeling)); + // Construct the result. + DeterministicModelParser::Result result(std::move(transitions), std::move(labeling)); - if (stateRewardFile != "") { - result.stateRewards.reset(storm::parser::SparseStateRewardParser(stateCount, stateRewardFile)); - } - if (transitionRewardFile != "") { - RewardMatrixInformationStruct* rewardMatrixInfo = new RewardMatrixInformationStruct(rowCount, stateCount, nullptr); - result.transitionRewards.reset(std::move(storm::parser::DeterministicSparseTransitionParser(transitionRewardFile, false, rewardMatrixInfo))); - delete rewardMatrixInfo; - } + // Only parse state rewards if a file is given. + if (stateRewardFilename != "") { + result.stateRewards = storm::parser::SparseStateRewardParser::parseSparseStateReward(stateCount, stateRewardFilename); + } - return result; -} + // Only parse transition rewards if a file is given. + if (transitionRewardFilename != "") { + result.transitionRewards = storm::parser::DeterministicSparseTransitionParser::parseDeterministicTransitionRewards(transitionRewardFilename, result.transitionSystem); + } -/*! - * Uses the Function parseDeterministicModel internally to parse the given input files. - * @note This is a Short-Hand for Constructing a Dtmc directly from the data returned by @parseDeterministicModel - * @return A Dtmc Model - */ -storm::models::Dtmc<double> DeterministicModelParserAsDtmc(std::string const & transitionSystemFile, std::string const & labelingFile, - std::string const & stateRewardFile, std::string const & transitionRewardFile) { - DeterministicModelParserResultContainer<double> parserResult(std::move(parseDeterministicModel(transitionSystemFile, labelingFile, stateRewardFile, transitionRewardFile))); - return storm::models::Dtmc<double>(std::move(parserResult.transitionSystem), std::move(parserResult.labeling), std::move(parserResult.stateRewards), std::move(parserResult.transitionRewards), boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>>()); -} - -/*! - * Uses the Function parseDeterministicModel internally to parse the given input files. - * @note This is a Short-Hand for Constructing a Ctmc directly from the data returned by @parseDeterministicModel - * @return A Ctmc Model - */ -storm::models::Ctmc<double> DeterministicModelParserAsCtmc(std::string const & transitionSystemFile, std::string const & labelingFile, - std::string const & stateRewardFile, std::string const & transitionRewardFile) { - DeterministicModelParserResultContainer<double> parserResult(std::move(parseDeterministicModel(transitionSystemFile, labelingFile, stateRewardFile, transitionRewardFile))); - return storm::models::Ctmc<double>(std::move(parserResult.transitionSystem), std::move(parserResult.labeling), std::move(parserResult.stateRewards), std::move(parserResult.transitionRewards), boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>>()); -} + return result; + } + + storm::models::Dtmc<double> DeterministicModelParser::parseDtmc(std::string const & transitionsFilename, std::string const & labelingFilename, std::string const & stateRewardFilename, std::string const & transitionRewardFilename) { + + DeterministicModelParser::Result parserResult(std::move(parseDeterministicModel(transitionsFilename, labelingFilename, stateRewardFilename, transitionRewardFilename))); + return storm::models::Dtmc<double>(std::move(parserResult.transitionSystem), std::move(parserResult.labeling), std::move(parserResult.stateRewards), std::move(parserResult.transitionRewards), boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>>()); + } + + storm::models::Ctmc<double> DeterministicModelParser::parseCtmc(std::string const & transitionsFilename, std::string const & labelingFilename, std::string const & stateRewardFilename, std::string const & transitionRewardFilename) { -} /* namespace parser */ + DeterministicModelParser::Result parserResult(std::move(parseDeterministicModel(transitionsFilename, labelingFilename, stateRewardFilename, transitionRewardFilename))); + return storm::models::Ctmc<double>(std::move(parserResult.transitionSystem), std::move(parserResult.labeling), std::move(parserResult.stateRewards), std::move(parserResult.transitionRewards), boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>>()); + } + } /* namespace parser */ } /* namespace storm */ diff --git a/src/parser/DeterministicModelParser.h b/src/parser/DeterministicModelParser.h index 273c64c49..12aba0470 100644 --- a/src/parser/DeterministicModelParser.h +++ b/src/parser/DeterministicModelParser.h @@ -8,54 +8,123 @@ #ifndef STORM_PARSER_DETERMINISTICMODELPARSER_H_ #define STORM_PARSER_DETERMINISTICMODELPARSER_H_ -#include "src/parser/Parser.h" #include "src/models/Dtmc.h" #include "src/models/Ctmc.h" -#include <boost/optional.hpp> - namespace storm { -namespace parser { + namespace parser { -/*! - * @brief Load label and transition file and returns an initialized dtmc or ctmc object. - * - * @note This class creates a new Dtmc or Ctmc object - * - * @note The labeling representation in the file may use at most as much nodes as are specified in the transition system. - */ + /*! + * Loads a deterministic model (Dtmc or Ctmc) from files. + * + * Given the file paths of the files holding the transitions, the atomic propositions and optionally the state- and transition rewards + * it loads the files, parses them and returns the desired model. + */ + class DeterministicModelParser { + public: -storm::models::Dtmc<double> DeterministicModelParserAsDtmc(std::string const & transitionSystemFile, std::string const & labelingFile, - std::string const & stateRewardFile = "", std::string const & transitionRewardFile = ""); -storm::models::Ctmc<double> DeterministicModelParserAsCtmc(std::string const & transitionSystemFile, std::string const & labelingFile, - std::string const & stateRewardFile = "", std::string const & transitionRewardFile = ""); + /*! + * A structure containing the parsed components of a deterministic model. + */ + struct Result { -/*! - * @brief This Class acts as a container much like std::pair for the four return values of the DeterministicModelParser - */ -template <class T> -class DeterministicModelParserResultContainer { -public: - storm::storage::SparseMatrix<T> transitionSystem; - storm::models::AtomicPropositionsLabeling labeling; - boost::optional<std::vector<T>> stateRewards; - boost::optional<storm::storage::SparseMatrix<T>> transitionRewards; - DeterministicModelParserResultContainer(storm::storage::SparseMatrix<T>& transitionSystem, storm::models::AtomicPropositionsLabeling& labeling) : transitionSystem(transitionSystem), labeling(labeling) { } - DeterministicModelParserResultContainer(storm::storage::SparseMatrix<T>&& transitionSystem, storm::models::AtomicPropositionsLabeling&& labeling) : transitionSystem(std::move(transitionSystem)), labeling(std::move(labeling)) { } - - DeterministicModelParserResultContainer(const DeterministicModelParserResultContainer & other) : transitionSystem(other.transitionSystem), - labeling(other.labeling), stateRewards(other.stateRewards), transitionRewards(other.transitionRewards) {} - DeterministicModelParserResultContainer(DeterministicModelParserResultContainer && other) : transitionSystem(std::move(other.transitionSystem)), - labeling(std::move(other.labeling)), stateRewards(std::move(other.stateRewards)), transitionRewards(std::move(other.transitionRewards)) {} -private: - DeterministicModelParserResultContainer() {} -}; - - -DeterministicModelParserResultContainer<double> parseDeterministicModel(std::string const & transitionSystemFile, std::string const & labelingFile, - std::string const & stateRewardFile = "", std::string const & transitionRewardFile = ""); - -} /* namespace parser */ + /*! + * The copy constructor. + * + * @param transitionSystem The transition system to be contained in the Result. + * @param labeling The the labeling of the transition system to be contained in the Result. + */ + Result(storm::storage::SparseMatrix<double>& transitionSystem, storm::models::AtomicPropositionsLabeling& labeling) : transitionSystem(transitionSystem), labeling(labeling) { + // Intentionally left empty. + } + + /*! + * The move constructor. + * + * @param transitionSystem The transition system to be contained in the Result. + * @param labeling The the labeling of the transition system to be contained in the Result. + */ + Result(storm::storage::SparseMatrix<double>&& transitionSystem, storm::models::AtomicPropositionsLabeling&& labeling) : transitionSystem(std::move(transitionSystem)), labeling(std::move(labeling)) { + // Intentionally left empty. + } + + //! A matrix representing the transitions of the model + storm::storage::SparseMatrix<double> transitionSystem; + + //! The labels of each state. + storm::models::AtomicPropositionsLabeling labeling; + + //! Optional rewards for each state. + boost::optional<std::vector<double>> stateRewards; + + //! Optional rewards for each transition. + boost::optional<storm::storage::SparseMatrix<double>> transitionRewards; + }; + + + /*! + * Parse a Dtmc. + * + * This method is an adapter to the actual parsing function. + * I.e. it uses @parseDeterministicModel internally to parse the given input files, takes its result and compiles it into a Dtmc. + * + * @note The number of states of the model is determined by the transitions file. + * The labeling file may therefore not contain labels of states that are not contained in the transitions file. + * + * @param transitionsFilename The path and name of the file containing the transitions of the model. + * @param labelingFilename The path and name of the file containing the labels for the states of the model. + * @param stateRewardFilename The path and name of the file containing the state reward of the model. This file is optional. + * @param transitionRewardFilename The path and name of the file containing the transition rewards of the model. This file is optional. + * @return The parsed Dtmc. + */ + static storm::models::Dtmc<double> parseDtmc(std::string const & transitionsFilename, + std::string const & labelingFilename, + std::string const & stateRewardFilename = "", + std::string const & transitionRewardFilename = ""); + + /*! + * Parse a Ctmc. + * + * This method is an adapter to the actual parsing function. + * I.e. it uses @parseDeterministicModel internally to parse the given input files, takes its result and compiles it into a Ctmc. + * + * @note The number of states of the model is determined by the transitions file. + * The labeling file may therefore not contain labels of states that are not contained in the transitions file. + * + * @param transitionsFilename The path and name of the file containing the transitions of the model. + * @param labelingFilename The path and name of the file containing the labels for the states of the model. + * @param stateRewardFilename The path and name of the file containing the state reward of the model. This file is optional. + * @param transitionRewardFilename The path and name of the file containing the transition rewards of the model. This file is optional. + * @return The parsed Ctmc. + */ + static storm::models::Ctmc<double> parseCtmc(std::string const & transitionsFilename, + std::string const & labelingFilename, + std::string const & stateRewardFilename = "", + std::string const & transitionRewardFilename = ""); + + private: + + /*! + * Parses a deterministic model from the given files. + * Calls sub-parsers on the given files and fills the container with the results. + * + * @note The number of states of the model is determined by the transitions file. + * The labeling file may therefore not contain labels of states that are not contained in the transitions file. + * + * @param transitionsFilename The path and name of the file containing the transitions of the model. + * @param labelingFilename The path and name of the file containing the labels for the states of the model. + * @param stateRewardFilename The path and name of the file containing the state reward of the model. This file is optional. + * @param transitionRewardFilename The path and name of the file containing the transition rewards of the model. This file is optional. + * @return The parsed model encapsulated in a Result structure. + */ + static Result parseDeterministicModel(std::string const & transitionsFilename, + std::string const & labelingFilename, + std::string const & stateRewardFilename = "", + std::string const & transitionRewardFilename = ""); + }; + + } /* namespace parser */ } /* namespace storm */ + #endif /* STORM_PARSER_DETERMINISTICMODELPARSER_H_ */ diff --git a/src/parser/DeterministicSparseTransitionParser.cpp b/src/parser/DeterministicSparseTransitionParser.cpp index 4645286aa..67e31484f 100644 --- a/src/parser/DeterministicSparseTransitionParser.cpp +++ b/src/parser/DeterministicSparseTransitionParser.cpp @@ -7,22 +7,18 @@ #include "src/parser/DeterministicSparseTransitionParser.h" -#include <errno.h> -#include <time.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <locale.h> - -#include <cstdlib> #include <cstdio> #include <cstring> +#include <cstdint> #include <clocale> #include <iostream> #include <string> +#include "src/utility/constants.h" +#include "src/utility/cstring.h" +#include "src/parser/MappedFile.h" #include "src/exceptions/FileIoException.h" #include "src/exceptions/WrongFormatException.h" -#include <cstdint> #include "src/settings/Settings.h" #include "log4cplus/logger.h" @@ -30,253 +26,250 @@ extern log4cplus::Logger logger; namespace storm { -namespace parser { + namespace parser { -/*! - * @brief Perform first pass through the file and obtain number of - * non-zero cells and maximum node id. - * - * This method does the first pass through the .tra file and computes - * the number of non-zero elements. - * It also calculates the maximum node id and stores it in maxnode. - * - * @return The number of non-zero elements - * @param buf Data to scan. Is expected to be some char array. - * @param maxnode Is set to highest id of all nodes. - */ -uint_fast64_t firstPass(char* buf, SupportedLineEndingsEnum lineEndings, uint_fast64_t& maxnode, RewardMatrixInformationStruct* rewardMatrixInformation) { - bool isRewardMatrix = rewardMatrixInformation != nullptr; - - uint_fast64_t nonZeroEntryCount = 0; - - /* - * Check file header and extract number of transitions. - */ - if (!isRewardMatrix) { - // skip format hint - buf = storm::parser::forwardToNextLine(buf, lineEndings); - } - - /* - * Check all transitions for non-zero diagonal entries and deadlock states. - */ - int_fast64_t lastRow = -1; - uint_fast64_t row, col; - uint_fast64_t readTransitionCount = 0; - bool rowHadDiagonalEntry = false; - double val; - maxnode = 0; - while (buf[0] != '\0') { - /* - * Read row and column. - */ - row = checked_strtol(buf, &buf); - col = checked_strtol(buf, &buf); - - if (!isRewardMatrix) { - if (lastRow != (int_fast64_t)row) { - if ((lastRow != -1) && (!rowHadDiagonalEntry)) { - ++nonZeroEntryCount; - rowHadDiagonalEntry = true; - } - for (uint_fast64_t skippedRow = (uint_fast64_t)(lastRow + 1); skippedRow < row; ++skippedRow) { - ++nonZeroEntryCount; - } - lastRow = row; - rowHadDiagonalEntry = false; - } + using namespace storm::utility::cstring; - if (col == row) { - rowHadDiagonalEntry = true; - } else if (col > row && !rowHadDiagonalEntry) { - rowHadDiagonalEntry = true; - ++nonZeroEntryCount; - } + storm::storage::SparseMatrix<double> DeterministicSparseTransitionParser::parseDeterministicTransitions(std::string const& filename) { + + storm::storage::SparseMatrix<double> emptyMatrix; + + return DeterministicSparseTransitionParser::parse(filename, false, emptyMatrix); } - /* - * Check if one is larger than the current maximum id. - */ - if (row > maxnode) maxnode = row; - if (col > maxnode) maxnode = col; - - /* - * Read probability of this transition. - * Check, if the value is a probability, i.e. if it is between 0 and 1. - */ - val = checked_strtod(buf, &buf); - if ((val < 0.0) || (val > 1.0)) { - LOG4CPLUS_ERROR(logger, "Expected a positive probability but got \"" << val << "\"."); - return 0; + storm::storage::SparseMatrix<double> DeterministicSparseTransitionParser::parseDeterministicTransitionRewards(std::string const& filename, storm::storage::SparseMatrix<double> const & transitionMatrix) { + + return DeterministicSparseTransitionParser::parse(filename, true, transitionMatrix); } - ++nonZeroEntryCount; - ++readTransitionCount; - buf = trimWhitespaces(buf); - } - if (!rowHadDiagonalEntry && !isRewardMatrix) { - ++nonZeroEntryCount; - } + storm::storage::SparseMatrix<double> DeterministicSparseTransitionParser::parse(std::string const& filename, bool isRewardFile, storm::storage::SparseMatrix<double> const & transitionMatrix) { + // Enforce locale where decimal point is '.'. + setlocale(LC_NUMERIC, "C"); - return nonZeroEntryCount; -} + if (!MappedFile::fileExistsAndIsReadable(filename.c_str())) { + LOG4CPLUS_ERROR(logger, "Error while parsing " << filename << ": File does not exist or is not readable."); + throw storm::exceptions::FileIoException() << "The supplied Transition input file \"" << filename << "\" does not exist or is not readable by this process."; + } + // Open file. + MappedFile file(filename.c_str()); + char* buf = file.getData(); + // Perform first pass, i.e. count entries that are not zero. + bool insertDiagonalEntriesIfMissing = !isRewardFile; + DeterministicSparseTransitionParser::FirstPassResult firstPass = DeterministicSparseTransitionParser::firstPass(file.getData(), insertDiagonalEntriesIfMissing); -/*! - * Reads a .tra file and produces a sparse matrix representing the described Markov Chain. - * - * Matrices created with this method have to be freed with the delete operator. - * @param filename input .tra file's name. - * @return a pointer to the created sparse matrix. - */ + LOG4CPLUS_INFO(logger, "First pass on " << filename << " shows " << firstPass.numberOfNonzeroEntries << " NonZeros."); -storm::storage::SparseMatrix<double> DeterministicSparseTransitionParser(std::string const& filename, bool insertDiagonalEntriesIfMissing, RewardMatrixInformationStruct* rewardMatrixInformation) { - /* - * Enforce locale where decimal point is '.'. - */ - setlocale(LC_NUMERIC, "C"); - - bool isRewardMatrix = rewardMatrixInformation != nullptr; - - if (!fileExistsAndIsReadable(filename.c_str())) { - LOG4CPLUS_ERROR(logger, "Error while parsing " << filename << ": File does not exist or is not readable."); - throw storm::exceptions::FileIoException() << "The supplied Transition input file \"" << filename << "\" does not exist or is not readable by this process."; - } - - /* - * Find out about the used line endings. - */ - SupportedLineEndingsEnum lineEndings = findUsedLineEndings(filename, true); - - /* - * Open file. - */ - MappedFile file(filename.c_str()); - char* buf = file.data; - - /* - * Perform first pass, i.e. count entries that are not zero. - */ - uint_fast64_t maxStateId; - uint_fast64_t nonZeroEntryCount = firstPass(file.data, lineEndings, maxStateId, rewardMatrixInformation); - - LOG4CPLUS_INFO(logger, "First pass on " << filename << " shows " << nonZeroEntryCount << " NonZeros."); - - /* - * If first pass returned zero, the file format was wrong. - */ - if (nonZeroEntryCount == 0) { - LOG4CPLUS_ERROR(logger, "Error while parsing " << filename << ": erroneous file format."); - throw storm::exceptions::WrongFormatException(); - } - - /* - * Perform second pass- - * - * From here on, we already know that the file header is correct. - */ - - /* - * Read file header, extract number of states. - */ - if (!isRewardMatrix) { - // skip format hint - buf = storm::parser::forwardToNextLine(buf, lineEndings); - } - - // If the matrix that is being parsed is a reward matrix, it should match the size of the - // transition matrix. - if (isRewardMatrix) { - if (maxStateId + 1 > rewardMatrixInformation->rowCount || maxStateId + 1 > rewardMatrixInformation->columnCount) { - LOG4CPLUS_ERROR(logger, "Reward matrix has more rows or columns than transition matrix."); - throw storm::exceptions::WrongFormatException() << "Reward matrix has more rows or columns than transition matrix."; - } else { - maxStateId = rewardMatrixInformation->rowCount - 1; - } - } - - /* - * Creating matrix here. - * The number of non-zero elements is computed by firstPass(). - */ - LOG4CPLUS_INFO(logger, "Attempting to create matrix of size " << (maxStateId+1) << " x " << (maxStateId+1) << "."); - storm::storage::SparseMatrixBuilder<double> matrixBuilder(maxStateId + 1, maxStateId + 1, nonZeroEntryCount); - - int_fast64_t row, lastRow = -1, col; - double val; - bool fixDeadlocks = storm::settings::Settings::getInstance()->isSet("fixDeadlocks"); - bool hadDeadlocks = false; - bool rowHadDiagonalEntry = false; - - /* - * Read all transitions from file. Note that we assume that the - * transitions are listed in canonical order, otherwise this will not - * work, i.e. the values in the matrix will be at wrong places. - */ - while (buf[0] != '\0') { - /* - * Read row, col and value. - */ - row = checked_strtol(buf, &buf); - col = checked_strtol(buf, &buf); - val = checked_strtod(buf, &buf); - - if (lastRow != row) { - if ((lastRow != -1) && (!rowHadDiagonalEntry)) { - if (insertDiagonalEntriesIfMissing && !isRewardMatrix) { - matrixBuilder.addNextValue(lastRow, lastRow, storm::utility::constantZero<double>()); - // LOG4CPLUS_DEBUG(logger, "While parsing " << filename << ": state " << lastRow << " has no transition to itself. Inserted a 0-transition. (1)"); - } else if (!isRewardMatrix) { - // LOG4CPLUS_WARN(logger, "Warning while parsing " << filename << ": state " << lastRow << " has no transition to itself."); + // If first pass returned zero, the file format was wrong. + if (firstPass.numberOfNonzeroEntries == 0) { + LOG4CPLUS_ERROR(logger, "Error while parsing " << filename << ": empty or erroneous file format."); + throw storm::exceptions::WrongFormatException(); } - // No increment for lastRow - rowHadDiagonalEntry = true; - } - for (int_fast64_t skippedRow = lastRow + 1; skippedRow < row; ++skippedRow) { - hadDeadlocks = true; - if (fixDeadlocks && !isRewardMatrix) { - matrixBuilder.addNextValue(skippedRow, skippedRow, storm::utility::constantOne<double>()); - rowHadDiagonalEntry = true; - // LOG4CPLUS_WARN(logger, "Warning while parsing " << filename << ": state " << skippedRow << " has no outgoing transitions. A self-loop was inserted."); - } else if (!isRewardMatrix) { - // LOG4CPLUS_ERROR(logger, "Error while parsing " << filename << ": state " << skippedRow << " has no outgoing transitions."); - // FIXME Why no exception at this point? This will break the App. + + // Perform second pass. + + // Skip the format hint if it is there. + buf = trimWhitespaces(buf); + if(buf[0] < '0' || buf[0] > '9') { + buf = forwardToLineEnd(buf); + buf = trimWhitespaces(buf); } - } - lastRow = row; - rowHadDiagonalEntry = false; + + if(isRewardFile) { + // The reward matrix should match the size of the transition matrix. + if (firstPass.highestStateIndex + 1 > transitionMatrix.getRowCount() || firstPass.highestStateIndex + 1 > transitionMatrix.getColumnCount()) { + LOG4CPLUS_ERROR(logger, "Reward matrix has more rows or columns than transition matrix."); + throw storm::exceptions::WrongFormatException() << "Reward matrix has more rows or columns than transition matrix."; + } else { + // If we found the right number of states or less, we set it to the number of states represented by the transition matrix. + firstPass.highestStateIndex = transitionMatrix.getRowCount() - 1; + } + } + + // Creating matrix builder here. + // The actual matrix will be build once all contents are inserted. + storm::storage::SparseMatrixBuilder<double> resultMatrix(firstPass.highestStateIndex + 1, firstPass.highestStateIndex + 1, firstPass.numberOfNonzeroEntries); + + uint_fast64_t row, col, lastRow = 0; + double val; + bool fixDeadlocks = storm::settings::Settings::getInstance()->isSet("fixDeadlocks"); + bool hadDeadlocks = false; + bool rowHadDiagonalEntry = false; + + + // Read all transitions from file. Note that we assume that the + // transitions are listed in canonical order, otherwise this will not + // work, i.e. the values in the matrix will be at wrong places. + + // Different parsing routines for transition systems and transition rewards. + if(isRewardFile) { + while (buf[0] != '\0') { + + // Read next transition. + row = checked_strtol(buf, &buf); + col = checked_strtol(buf, &buf); + val = checked_strtod(buf, &buf); + + resultMatrix.addNextValue(row, col, val); + buf = trimWhitespaces(buf); + } + } else { + while (buf[0] != '\0') { + + // Read next transition. + row = checked_strtol(buf, &buf); + col = checked_strtol(buf, &buf); + val = checked_strtod(buf, &buf); + + // Test if we moved to a new row. + // Handle all incomplete or skipped rows. + if (lastRow != row) { + if (!rowHadDiagonalEntry) { + if (insertDiagonalEntriesIfMissing) { + resultMatrix.addNextValue(lastRow, lastRow, storm::utility::constantZero<double>()); + LOG4CPLUS_DEBUG(logger, "While parsing " << filename << ": state " << lastRow << " has no transition to itself. Inserted a 0-transition. (1)"); + } else { + LOG4CPLUS_WARN(logger, "Warning while parsing " << filename << ": state " << lastRow << " has no transition to itself."); + } + // No increment for lastRow. + rowHadDiagonalEntry = true; + } + for (uint_fast64_t skippedRow = lastRow + 1; skippedRow < row; ++skippedRow) { + hadDeadlocks = true; + if (fixDeadlocks) { + resultMatrix.addNextValue(skippedRow, skippedRow, storm::utility::constantOne<double>()); + rowHadDiagonalEntry = true; + LOG4CPLUS_WARN(logger, "Warning while parsing " << filename << ": state " << skippedRow << " has no outgoing transitions. A self-loop was inserted."); + } else { + LOG4CPLUS_ERROR(logger, "Error while parsing " << filename << ": state " << skippedRow << " has no outgoing transitions."); + // Before throwing the appropriate exception we will give notice of all deadlock states. + } + } + lastRow = row; + rowHadDiagonalEntry = false; + } + + if (col == row) { + rowHadDiagonalEntry = true; + } + + if (col > row && !rowHadDiagonalEntry) { + if (insertDiagonalEntriesIfMissing) { + resultMatrix.addNextValue(row, row, storm::utility::constantZero<double>()); + LOG4CPLUS_DEBUG(logger, "While parsing " << filename << ": state " << row << " has no transition to itself. Inserted a 0-transition. (2)"); + } else { + LOG4CPLUS_WARN(logger, "Warning while parsing " << filename << ": state " << row << " has no transition to itself."); + } + rowHadDiagonalEntry = true; + } + + resultMatrix.addNextValue(row, col, val); + buf = trimWhitespaces(buf); + } + + if (!rowHadDiagonalEntry) { + if (insertDiagonalEntriesIfMissing) { + resultMatrix.addNextValue(lastRow, lastRow, storm::utility::constantZero<double>()); + LOG4CPLUS_DEBUG(logger, "While parsing " << filename << ": state " << lastRow << " has no transition to itself. Inserted a 0-transition. (3)"); + } else { + LOG4CPLUS_WARN(logger, "Warning while parsing " << filename << ": state " << lastRow << " has no transition to itself."); + } + } + + // If we encountered deadlock and did not fix them, now is the time to throw the exception. + if (!fixDeadlocks && hadDeadlocks) throw storm::exceptions::WrongFormatException() << "Some of the nodes had deadlocks. You can use --fixDeadlocks to insert self-loops on the fly."; + } + + // Finally, build the actual matrix, test and return it. + storm::storage::SparseMatrix<double> result = resultMatrix.build(); + + // Since we cannot do the testing if each transition for which there is a reward in the reward file also exists in the transition matrix during parsing, we have to do it afterwards. + if(isRewardFile && !result.isSubmatrixOf(transitionMatrix)) { + LOG4CPLUS_ERROR(logger, "There are rewards for non existent transitions given in the reward file."); + throw storm::exceptions::WrongFormatException() << "There are rewards for non existent transitions given in the reward file."; + } + + return result; } - if (col == row) { - rowHadDiagonalEntry = true; - } else if (col > row && !rowHadDiagonalEntry) { - rowHadDiagonalEntry = true; - if (insertDiagonalEntriesIfMissing && !isRewardMatrix) { - matrixBuilder.addNextValue(row, row, storm::utility::constantZero<double>()); - // LOG4CPLUS_DEBUG(logger, "While parsing " << filename << ": state " << row << " has no transition to itself. Inserted a 0-transition. (2)"); - } else if (!isRewardMatrix) { - // LOG4CPLUS_WARN(logger, "Warning while parsing " << filename << ": state " << row << " has no transition to itself."); + DeterministicSparseTransitionParser::FirstPassResult DeterministicSparseTransitionParser::firstPass(char* buf, bool insertDiagonalEntriesIfMissing) { + + DeterministicSparseTransitionParser::FirstPassResult result; + + // Skip the format hint if it is there. + buf = trimWhitespaces(buf); + if(buf[0] < '0' || buf[0] > '9') { + buf = forwardToLineEnd(buf); + buf = trimWhitespaces(buf); } - } - matrixBuilder.addNextValue(row, col, val); - buf = trimWhitespaces(buf); - } + // Check all transitions for non-zero diagonal entries and deadlock states. + uint_fast64_t row, col, lastRow = 0, lastCol = -1; + bool rowHadDiagonalEntry = false; + while (buf[0] != '\0') { + + // Read the transition. + row = checked_strtol(buf, &buf); + col = checked_strtol(buf, &buf); + // The actual read value is not needed here. + checked_strtod(buf, &buf); + + // Compensate for missing diagonal entries if desired. + if (insertDiagonalEntriesIfMissing) { + if (lastRow != row) { + if(!rowHadDiagonalEntry) { + ++result.numberOfNonzeroEntries; + } + + // Compensate for missing rows. + for (uint_fast64_t skippedRow = lastRow + 1; skippedRow < row; ++skippedRow) { + ++result.numberOfNonzeroEntries; + } + rowHadDiagonalEntry = false; + } + + if (col == row) { + rowHadDiagonalEntry = true; + } + + if (col > row && !rowHadDiagonalEntry) { + rowHadDiagonalEntry = true; + ++result.numberOfNonzeroEntries; + } + } - if (!rowHadDiagonalEntry) { - if (insertDiagonalEntriesIfMissing && !isRewardMatrix) { - matrixBuilder.addNextValue(lastRow, lastRow, storm::utility::constantZero<double>()); - // LOG4CPLUS_DEBUG(logger, "While parsing " << filename << ": state " << lastRow << " has no transition to itself. Inserted a 0-transition. (3)"); - } else if (!isRewardMatrix) { - // LOG4CPLUS_WARN(logger, "Warning while parsing " << filename << ": state " << lastRow << " has no transition to itself."); - } - } + // Check if a higher state id was found. + if (row > result.highestStateIndex) result.highestStateIndex = row; + if (col > result.highestStateIndex) result.highestStateIndex = col; - if (!fixDeadlocks && hadDeadlocks) throw storm::exceptions::WrongFormatException() << "Some of the nodes had deadlocks. You can use --fixDeadlocks to insert self-loops on the fly."; + ++result.numberOfNonzeroEntries; - return matrixBuilder.build(); -} + // Have we already seen this transition? + if (row == lastRow && col == lastCol) { + LOG4CPLUS_ERROR(logger, "The same transition (" << row << ", " << col << ") is given twice."); + throw storm::exceptions::InvalidArgumentException() << "The same transition (" << row << ", " << col << ") is given twice."; + } + + lastRow = row; + lastCol = col; + + buf = trimWhitespaces(buf); + } + + if(insertDiagonalEntriesIfMissing) { + if (!rowHadDiagonalEntry) { + ++result.numberOfNonzeroEntries; + } + + //Compensate for missing rows at the end of the file. + for (uint_fast64_t skippedRow = (uint_fast64_t)(lastRow + 1); skippedRow <= result.highestStateIndex; ++skippedRow) { + ++result.numberOfNonzeroEntries; + } + } + + return result; + } -} // namespace parser + } // namespace parser } // namespace storm diff --git a/src/parser/DeterministicSparseTransitionParser.h b/src/parser/DeterministicSparseTransitionParser.h index cca8bf9e0..e31ef2291 100644 --- a/src/parser/DeterministicSparseTransitionParser.h +++ b/src/parser/DeterministicSparseTransitionParser.h @@ -1,23 +1,90 @@ -#ifndef STORM_PARSER_TRAPARSER_H_ -#define STORM_PARSER_TRAPARSER_H_ +#ifndef STORM_PARSER_DETERMINISTICSPARSETRANSITIONPARSER_H_ +#define STORM_PARSER_DETERMINISTICSPARSETRANSITIONPARSER_H_ #include "src/storage/SparseMatrix.h" -#include "src/parser/Parser.h" -#include "src/utility/OsDetection.h" +namespace storm { + namespace parser { -#include <memory> + /*! + * This class can be used to parse a file containing either transitions or transition rewards of a deterministic model. + * + * The file is parsed in two passes. + * The first pass tests the file format and collects statistical data needed for the second pass. + * The second pass then parses the file data and constructs a SparseMatrix representing it. + */ + class DeterministicSparseTransitionParser { + public: -namespace storm { -namespace parser { - -/*! - * @brief Load a deterministic transition system from file and create a - * sparse adjacency matrix whose entries represent the weights of the edges - */ -storm::storage::SparseMatrix<double> DeterministicSparseTransitionParser(std::string const& filename, bool insertDiagonalEntriesIfMissing = true, RewardMatrixInformationStruct* rewardMatrixInformation = nullptr); - -} // namespace parser + /*! + * A structure representing the result of the first pass of this parser. It contains the number of non-zero entries in the model and the highest state index. + */ + struct FirstPassResult { + + /*! + * The default constructor. + * Constructs an empty FirstPassResult. + */ + FirstPassResult() : numberOfNonzeroEntries(0), highestStateIndex(0) { + // Intentionally left empty. + } + + //! The total number of non-zero entries of the model. + uint_fast64_t numberOfNonzeroEntries; + + //! The highest state index that appears in the model. + uint_fast64_t highestStateIndex; + }; + + /*! + * Load a deterministic transition system from file and create a + * sparse adjacency matrix whose entries represent the weights of the edges. + * + * @param filename The path and name of the file to be parsed. + * @param insertDiagonalEntriesIfMissing A flag set iff entries on the primary diagonal of the matrix should be added in case they are missing in the parsed file. + * @return A SparseMatrix containing the parsed transition system. + */ + static storm::storage::SparseMatrix<double> parseDeterministicTransitions(std::string const& filename); + + /*! + * Load the transition rewards for a deterministic transition system from file and create a + * sparse adjacency matrix whose entries represent the rewards of the respective transitions. + * + * @param filename The path and name of the file to be parsed. + * @param transitionMatrix The transition matrix of the model in which the reward matrix is to be used in. + * The dimensions (rows and columns) of the two matrices should match. + * @return A SparseMatrix containing the parsed transition rewards. + */ + static storm::storage::SparseMatrix<double> parseDeterministicTransitionRewards(std::string const& filename, storm::storage::SparseMatrix<double> const & transitionMatrix); + + private: + + /* + * Performs the first pass on the input pointed to by the given buffer to obtain the number of + * transitions and the maximum node id. + * + * @param buffer The buffer that contains the input. + * @param insertDiagonalEntriesIfMissing A flag set iff entries on the primary diagonal of the matrix should be added in case they are missing in the parsed file. + * @return A structure representing the result of the first pass. + */ + static FirstPassResult firstPass(char* buffer, bool insertDiagonalEntriesIfMissing = true); + + /* + * The main parsing routine. + * Opens the given file, calls the first pass and performs the second pass, parsing the content of the file into a SparseMatrix. + * + * @param filename The path and name of the file to be parsed. + * @param rewardFile A flag set iff the file to be parsed contains transition rewards. + * @param insertDiagonalEntriesIfMissing A flag set iff entries on the primary diagonal of the matrix should be added in case they are missing in the parsed file. + * @param transitionMatrix The transition matrix of the model in which the reward matrix is to be used in. + * The dimensions (rows and columns) of the two matrices should match. + * @return A SparseMatrix containing the parsed file contents. + */ + static storm::storage::SparseMatrix<double> parse(std::string const& filename, bool isRewardFile, storm::storage::SparseMatrix<double> const & transitionMatrix); + + }; + + } // namespace parser } // namespace storm -#endif /* STORM_PARSER_TRAPARSER_H_ */ +#endif /* STORM_PARSER_DETERMINISTICSPARSETRANSITIONPARSER_H_ */ diff --git a/src/parser/ExpressionParser.cpp b/src/parser/ExpressionParser.cpp new file mode 100644 index 000000000..6459d7a9d --- /dev/null +++ b/src/parser/ExpressionParser.cpp @@ -0,0 +1,400 @@ +#include "src/parser/ExpressionParser.h" +#include "src/exceptions/InvalidArgumentException.h" +#include "src/exceptions/InvalidTypeException.h" +#include "src/exceptions/WrongFormatException.h" + +namespace storm { + namespace parser { + ExpressionParser::ExpressionParser(qi::symbols<char, uint_fast64_t> const& invalidIdentifiers_) : ExpressionParser::base_type(expression), createExpressions(false), acceptDoubleLiterals(true), identifiers_(nullptr), invalidIdentifiers_(invalidIdentifiers_) { + identifier %= qi::as_string[qi::raw[qi::lexeme[((qi::alpha | qi::char_('_')) >> *(qi::alnum | qi::char_('_')))]]][qi::_pass = phoenix::bind(&ExpressionParser::isValidIdentifier, phoenix::ref(*this), qi::_1)]; + identifier.name("identifier"); + + floorCeilExpression = ((qi::lit("floor")[qi::_a = true] | qi::lit("ceil")[qi::_a = false]) >> qi::lit("(") >> plusExpression >> qi::lit(")"))[phoenix::if_(qi::_a) [qi::_val = phoenix::bind(&ExpressionParser::createFloorExpression, phoenix::ref(*this), qi::_1)] .else_ [qi::_val = phoenix::bind(&ExpressionParser::createCeilExpression, phoenix::ref(*this), qi::_1)]]; + floorCeilExpression.name("floor/ceil expression"); + + minMaxExpression = ((qi::lit("min")[qi::_a = true] | qi::lit("max")[qi::_a = false]) >> qi::lit("(") >> plusExpression >> qi::lit(",") >> plusExpression >> qi::lit(")"))[phoenix::if_(qi::_a) [qi::_val = phoenix::bind(&ExpressionParser::createMinimumExpression, phoenix::ref(*this), qi::_1, qi::_2)] .else_ [qi::_val = phoenix::bind(&ExpressionParser::createMaximumExpression, phoenix::ref(*this), qi::_1, qi::_2)]]; + minMaxExpression.name("min/max expression"); + + identifierExpression = identifier[qi::_val = phoenix::bind(&ExpressionParser::getIdentifierExpression, phoenix::ref(*this), qi::_1)]; + identifierExpression.name("identifier expression"); + + literalExpression = qi::lit("true")[qi::_val = phoenix::bind(&ExpressionParser::createTrueExpression, phoenix::ref(*this))] | qi::lit("false")[qi::_val = phoenix::bind(&ExpressionParser::createFalseExpression, phoenix::ref(*this))] | strict_double[qi::_val = phoenix::bind(&ExpressionParser::createDoubleLiteralExpression, phoenix::ref(*this), qi::_1, qi::_pass)] | qi::int_[qi::_val = phoenix::bind(&ExpressionParser::createIntegerLiteralExpression, phoenix::ref(*this), qi::_1)]; + literalExpression.name("literal expression"); + + atomicExpression = minMaxExpression | floorCeilExpression | qi::lit("(") >> expression >> qi::lit(")") | literalExpression | identifierExpression; + atomicExpression.name("atomic expression"); + + unaryExpression = atomicExpression[qi::_val = qi::_1] | (qi::lit("!") >> atomicExpression)[qi::_val = phoenix::bind(&ExpressionParser::createNotExpression, phoenix::ref(*this), qi::_1)] | (qi::lit("-") >> atomicExpression)[qi::_val = phoenix::bind(&ExpressionParser::createMinusExpression, phoenix::ref(*this), qi::_1)]; + unaryExpression.name("unary expression"); + + powerExpression = unaryExpression[qi::_val = qi::_1] >> -(qi::lit("^") > expression)[qi::_val = phoenix::bind(&ExpressionParser::createPowerExpression, phoenix::ref(*this), qi::_val, qi::_1)]; + powerExpression.name("power expression"); + + multiplicationExpression = powerExpression[qi::_val = qi::_1] >> *((qi::lit("*")[qi::_a = true] | qi::lit("/")[qi::_a = false]) >> powerExpression[phoenix::if_(qi::_a) [qi::_val = phoenix::bind(&ExpressionParser::createMultExpression, phoenix::ref(*this), qi::_val, qi::_1)] .else_ [qi::_val = phoenix::bind(&ExpressionParser::createDivExpression, phoenix::ref(*this), qi::_val, qi::_1)]]); + multiplicationExpression.name("multiplication expression"); + + plusExpression = multiplicationExpression[qi::_val = qi::_1] >> *((qi::lit("+")[qi::_a = true] | qi::lit("-")[qi::_a = false]) >> multiplicationExpression)[phoenix::if_(qi::_a) [qi::_val = phoenix::bind(&ExpressionParser::createPlusExpression, phoenix::ref(*this), qi::_val, qi::_1)] .else_ [qi::_val = phoenix::bind(&ExpressionParser::createMinusExpression, phoenix::ref(*this), qi::_val, qi::_1)]]; + plusExpression.name("plus expression"); + + relativeExpression = (plusExpression >> qi::lit(">=") >> plusExpression)[qi::_val = phoenix::bind(&ExpressionParser::createGreaterOrEqualExpression, phoenix::ref(*this), qi::_1, qi::_2)] | (plusExpression >> qi::lit(">") >> plusExpression)[qi::_val = phoenix::bind(&ExpressionParser::createGreaterExpression, phoenix::ref(*this), qi::_1, qi::_2)] | (plusExpression >> qi::lit("<=") >> plusExpression)[qi::_val = phoenix::bind(&ExpressionParser::createLessOrEqualExpression, phoenix::ref(*this), qi::_1, qi::_2)] | (plusExpression >> qi::lit("<") >> plusExpression)[qi::_val = phoenix::bind(&ExpressionParser::createLessExpression, phoenix::ref(*this), qi::_1, qi::_2)] | plusExpression[qi::_val = qi::_1]; + relativeExpression.name("relative expression"); + + equalityExpression = relativeExpression[qi::_val = qi::_1] >> *((qi::lit("=")[qi::_a = true] | qi::lit("!=")[qi::_a = false]) >> relativeExpression)[phoenix::if_(qi::_a) [ qi::_val = phoenix::bind(&ExpressionParser::createEqualsExpression, phoenix::ref(*this), qi::_val, qi::_1) ] .else_ [ qi::_val = phoenix::bind(&ExpressionParser::createNotEqualsExpression, phoenix::ref(*this), qi::_val, qi::_1) ] ]; + equalityExpression.name("equality expression"); + + andExpression = equalityExpression[qi::_val = qi::_1] >> *(qi::lit("&") >> equalityExpression)[qi::_val = phoenix::bind(&ExpressionParser::createAndExpression, phoenix::ref(*this), qi::_val, qi::_1)]; + andExpression.name("and expression"); + + orExpression = andExpression[qi::_val = qi::_1] >> *((qi::lit("|")[qi::_a = true] | qi::lit("=>")[qi::_a = false]) >> andExpression)[phoenix::if_(qi::_a) [qi::_val = phoenix::bind(&ExpressionParser::createOrExpression, phoenix::ref(*this), qi::_val, qi::_1)] .else_ [qi::_val = phoenix::bind(&ExpressionParser::createImpliesExpression, phoenix::ref(*this), qi::_val, qi::_1)] ]; + orExpression.name("or expression"); + + iteExpression = orExpression[qi::_val = qi::_1] >> -(qi::lit("?") > orExpression > qi::lit(":") > orExpression)[qi::_val = phoenix::bind(&ExpressionParser::createIteExpression, phoenix::ref(*this), qi::_val, qi::_1, qi::_2)]; + iteExpression.name("if-then-else expression"); + + expression %= iteExpression; + expression.name("expression"); + + // Enable error reporting. + qi::on_error<qi::fail>(expression, handler(qi::_1, qi::_2, qi::_3, qi::_4)); + qi::on_error<qi::fail>(iteExpression, handler(qi::_1, qi::_2, qi::_3, qi::_4)); + qi::on_error<qi::fail>(orExpression, handler(qi::_1, qi::_2, qi::_3, qi::_4)); + qi::on_error<qi::fail>(andExpression, handler(qi::_1, qi::_2, qi::_3, qi::_4)); + qi::on_error<qi::fail>(equalityExpression, handler(qi::_1, qi::_2, qi::_3, qi::_4)); + qi::on_error<qi::fail>(relativeExpression, handler(qi::_1, qi::_2, qi::_3, qi::_4)); + qi::on_error<qi::fail>(plusExpression, handler(qi::_1, qi::_2, qi::_3, qi::_4)); + qi::on_error<qi::fail>(multiplicationExpression, handler(qi::_1, qi::_2, qi::_3, qi::_4)); + qi::on_error<qi::fail>(unaryExpression, handler(qi::_1, qi::_2, qi::_3, qi::_4)); + qi::on_error<qi::fail>(atomicExpression, handler(qi::_1, qi::_2, qi::_3, qi::_4)); + qi::on_error<qi::fail>(literalExpression, handler(qi::_1, qi::_2, qi::_3, qi::_4)); + qi::on_error<qi::fail>(identifierExpression, handler(qi::_1, qi::_2, qi::_3, qi::_4)); + qi::on_error<qi::fail>(minMaxExpression, handler(qi::_1, qi::_2, qi::_3, qi::_4)); + qi::on_error<qi::fail>(floorCeilExpression, handler(qi::_1, qi::_2, qi::_3, qi::_4)); + } + + void ExpressionParser::setIdentifierMapping(qi::symbols<char, storm::expressions::Expression> const* identifiers_) { + if (identifiers_ != nullptr) { + this->createExpressions = true; + this->identifiers_ = identifiers_; + } else { + this->createExpressions = false; + this->identifiers_ = nullptr; + } + } + + void ExpressionParser::unsetIdentifierMapping() { + this->createExpressions = false; + this->identifiers_ = nullptr; + } + + void ExpressionParser::setAcceptDoubleLiterals(bool flag) { + this->acceptDoubleLiterals = flag; + } + + storm::expressions::Expression ExpressionParser::createIteExpression(storm::expressions::Expression e1, storm::expressions::Expression e2, storm::expressions::Expression e3) const { + if (this->createExpressions) { + try { + return e1.ite(e2, e3); + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Parsing error in line " << get_line(qi::_3) << ": " << e.what()); + } + } else { + return storm::expressions::Expression::createFalse(); + } + } + + storm::expressions::Expression ExpressionParser::createImpliesExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const { + if (this->createExpressions) { + try { + return e1.implies(e2); + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Parsing error in line " << get_line(qi::_3) << ": " << e.what()); + } + } else { + return storm::expressions::Expression::createFalse(); + } + } + + storm::expressions::Expression ExpressionParser::createOrExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const { + if (this->createExpressions) { + try { + return e1 || e2; + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Parsing error in line " << get_line(qi::_3) << ": " << e.what()); + } + } else { + return storm::expressions::Expression::createFalse(); + } + } + + storm::expressions::Expression ExpressionParser::createAndExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const { + if (this->createExpressions) { + try{ + return e1 && e2; + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Parsing error in line " << get_line(qi::_3) << ": " << e.what()); + } + } else { + return storm::expressions::Expression::createFalse(); + } + } + + storm::expressions::Expression ExpressionParser::createGreaterExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const { + if (this->createExpressions) { + try { + return e1 > e2; + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Parsing error in line " << get_line(qi::_3) << ": " << e.what()); + } + } else { + return storm::expressions::Expression::createFalse(); + } + } + + storm::expressions::Expression ExpressionParser::createGreaterOrEqualExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const { + if (this->createExpressions) { + try { + return e1 >= e2; + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Parsing error in line " << get_line(qi::_3) << ": " << e.what()); + } + } else { + return storm::expressions::Expression::createFalse(); + } + } + + storm::expressions::Expression ExpressionParser::createLessExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const { + if (this->createExpressions) { + try { + return e1 < e2; + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Parsing error in line " << get_line(qi::_3) << ": " << e.what()); + } + } else { + return storm::expressions::Expression::createFalse(); + } + } + + storm::expressions::Expression ExpressionParser::createLessOrEqualExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const { + if (this->createExpressions) { + try { + return e1 <= e2; + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Parsing error in line " << get_line(qi::_3) << ": " << e.what()); + } + } else { + return storm::expressions::Expression::createFalse(); + } + } + + storm::expressions::Expression ExpressionParser::createEqualsExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const { + if (this->createExpressions) { + try { + if (e1.hasBooleanReturnType() && e2.hasBooleanReturnType()) { + return e1.iff(e2); + } else { + return e1 == e2; + } + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Parsing error in line " << get_line(qi::_3) << ": " << e.what()); + } + } else { + return storm::expressions::Expression::createFalse(); + } + } + + storm::expressions::Expression ExpressionParser::createNotEqualsExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const { + if (this->createExpressions) { + try { + return e1 != e2; + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Parsing error in line " << get_line(qi::_3) << ": " << e.what()); + } + } else { + return storm::expressions::Expression::createFalse(); + } + } + + storm::expressions::Expression ExpressionParser::createPlusExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const { + if (this->createExpressions) { + try { + return e1 + e2; + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Parsing error in line " << get_line(qi::_3) << ": " << e.what()); + } + } else { + return storm::expressions::Expression::createFalse(); + } + } + + storm::expressions::Expression ExpressionParser::createMinusExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const { + if (this->createExpressions) { + try { + return e1 - e2; + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Parsing error in line " << get_line(qi::_3) << ": " << e.what()); + } + } else { + return storm::expressions::Expression::createFalse(); + } + } + + storm::expressions::Expression ExpressionParser::createMultExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const { + if (this->createExpressions) { + try { + return e1 * e2; + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Parsing error in line " << get_line(qi::_3) << ": " << e.what()); + } + } else { + return storm::expressions::Expression::createFalse(); + } + } + + storm::expressions::Expression ExpressionParser::createPowerExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const { + if (this->createExpressions) { + try { + return e1 ^ e2; + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Parsing error in line " << get_line(qi::_3) << ": " << e.what()); + } + } else { + return storm::expressions::Expression::createFalse(); + } + } + + storm::expressions::Expression ExpressionParser::createDivExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const { + if (this->createExpressions) { + try { + return e1 / e2; + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Parsing error in line " << get_line(qi::_3) << ": " << e.what()); + } + } else { + return storm::expressions::Expression::createFalse(); + } + } + + storm::expressions::Expression ExpressionParser::createNotExpression(storm::expressions::Expression e1) const { + if (this->createExpressions) { + try { + return !e1; + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Parsing error in line " << get_line(qi::_3) << ": " << e.what()); + } + } else { + return storm::expressions::Expression::createFalse(); + } + } + + storm::expressions::Expression ExpressionParser::createMinusExpression(storm::expressions::Expression e1) const { + if (this->createExpressions) { + try { + return -e1; + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Parsing error in line " << get_line(qi::_3) << ": " << e.what()); + } + } else { + return storm::expressions::Expression::createFalse(); + } + } + + storm::expressions::Expression ExpressionParser::createTrueExpression() const { + if (this->createExpressions) { + return storm::expressions::Expression::createTrue(); + } else { + return storm::expressions::Expression::createFalse(); + } + } + + storm::expressions::Expression ExpressionParser::createFalseExpression() const { + return storm::expressions::Expression::createFalse(); + } + + storm::expressions::Expression ExpressionParser::createDoubleLiteralExpression(double value, bool& pass) const { + // If we are not supposed to accept double expressions, we reject it by setting pass to false. + if (!this->acceptDoubleLiterals) { + pass = false; + } + + if (this->createExpressions) { + return storm::expressions::Expression::createDoubleLiteral(value); + } else { + return storm::expressions::Expression::createFalse(); + } + } + + storm::expressions::Expression ExpressionParser::createIntegerLiteralExpression(int value) const { + if (this->createExpressions) { + return storm::expressions::Expression::createIntegerLiteral(static_cast<int_fast64_t>(value)); + } else { + return storm::expressions::Expression::createFalse(); + } + } + + storm::expressions::Expression ExpressionParser::createMinimumExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const { + if (this->createExpressions) { + try { + return storm::expressions::Expression::minimum(e1, e2); + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Parsing error in line " << get_line(qi::_3) << ": " << e.what()); + } + } else { + return storm::expressions::Expression::createFalse(); + } + } + + storm::expressions::Expression ExpressionParser::createMaximumExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const { + if (this->createExpressions) { + try { + return storm::expressions::Expression::maximum(e1, e2); + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Parsing error in line " << get_line(qi::_3) << ": " << e.what()); + } + } else { + return storm::expressions::Expression::createFalse(); + } + } + + storm::expressions::Expression ExpressionParser::createFloorExpression(storm::expressions::Expression e1) const { + if (this->createExpressions) { + try { + return e1.floor(); + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Parsing error in line " << get_line(qi::_3) << ": " << e.what()); + } + } else { + return storm::expressions::Expression::createFalse(); + } + } + + storm::expressions::Expression ExpressionParser::createCeilExpression(storm::expressions::Expression e1) const { + if (this->createExpressions) { + try { + return e1.ceil(); + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Parsing error in line " << get_line(qi::_3) << ": " << e.what()); + } + } else { + return storm::expressions::Expression::createFalse(); + } + } + + storm::expressions::Expression ExpressionParser::getIdentifierExpression(std::string const& identifier) const { + if (this->createExpressions) { + LOG_THROW(this->identifiers_ != nullptr, storm::exceptions::WrongFormatException, "Unable to substitute identifier expressions without given mapping."); + storm::expressions::Expression const* expression = this->identifiers_->find(identifier); + LOG_THROW(expression != nullptr, storm::exceptions::WrongFormatException, "Parsing error in line " << get_line(qi::_3) << ": Undeclared identifier '" << identifier << "'."); + return *expression; + } else { + return storm::expressions::Expression::createFalse(); + } + } + + bool ExpressionParser::isValidIdentifier(std::string const& identifier) { + if (this->invalidIdentifiers_.find(identifier) != nullptr) { + return false; + } + return true; + } + } +} \ No newline at end of file diff --git a/src/parser/ExpressionParser.h b/src/parser/ExpressionParser.h new file mode 100644 index 000000000..380644caf --- /dev/null +++ b/src/parser/ExpressionParser.h @@ -0,0 +1,128 @@ +#ifndef STORM_PARSER_EXPRESSIONPARSER_H_ +#define STORM_PARSER_EXPRESSIONPARSER_H_ + +#include "src/parser/SpiritParserDefinitions.h" +#include "src/storage/expressions/Expression.h" +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/WrongFormatException.h" + +namespace storm { + namespace parser { + class ExpressionParser : public qi::grammar<Iterator, storm::expressions::Expression(), Skipper> { + public: + /*! + * Creates an expression parser. Initially the parser is set to a mode in which it will not generate the + * actual expressions but only perform a syntax check and return the expression "false". To make the parser + * generate the actual expressions, a mapping of valid identifiers to their expressions need to be provided + * later. + * + * @param invalidIdentifiers_ A symbol table of identifiers that are to be rejected. + */ + ExpressionParser(qi::symbols<char, uint_fast64_t> const& invalidIdentifiers_); + + /*! + * Sets an identifier mapping that is used to determine valid variables in the expression. The mapped-to + * expressions will be substituted wherever the key value appears in the parsed expression. After setting + * this, the parser will generate expressions. + * + * @param identifiers A pointer to a mapping from identifiers to expressions. + */ + void setIdentifierMapping(qi::symbols<char, storm::expressions::Expression> const* identifiers_); + + /*! + * Unsets a previously set identifier mapping. This will make the parser not generate expressions any more + * but merely check for syntactic correctness of an expression. + */ + void unsetIdentifierMapping(); + + /*! + * Sets whether double literals are to be accepted or not. + * + * @param flag If set to true, double literals are accepted. + */ + void setAcceptDoubleLiterals(bool flag); + + private: + // A flag that indicates whether expressions should actually be generated or just a syntax check shall be + // performed. + bool createExpressions; + + // A flag that indicates whether double literals are accepted. + bool acceptDoubleLiterals; + + // The currently used mapping of identifiers to expressions. This is used if the parser is set to create + // expressions. + qi::symbols<char, storm::expressions::Expression> const* identifiers_; + + // The symbol table of invalid identifiers. + qi::symbols<char, uint_fast64_t> const& invalidIdentifiers_; + + // Rules for parsing a composed expression. + qi::rule<Iterator, storm::expressions::Expression(), Skipper> expression; + qi::rule<Iterator, storm::expressions::Expression(), Skipper> iteExpression; + qi::rule<Iterator, storm::expressions::Expression(), qi::locals<bool>, Skipper> orExpression; + qi::rule<Iterator, storm::expressions::Expression(), Skipper> andExpression; + qi::rule<Iterator, storm::expressions::Expression(), Skipper> relativeExpression; + qi::rule<Iterator, storm::expressions::Expression(), qi::locals<bool>, Skipper> equalityExpression; + qi::rule<Iterator, storm::expressions::Expression(), qi::locals<bool>, Skipper> plusExpression; + qi::rule<Iterator, storm::expressions::Expression(), qi::locals<bool>, Skipper> multiplicationExpression; + qi::rule<Iterator, storm::expressions::Expression(), qi::locals<bool>, Skipper> powerExpression; + qi::rule<Iterator, storm::expressions::Expression(), Skipper> unaryExpression; + qi::rule<Iterator, storm::expressions::Expression(), Skipper> atomicExpression; + qi::rule<Iterator, storm::expressions::Expression(), Skipper> literalExpression; + qi::rule<Iterator, storm::expressions::Expression(), Skipper> identifierExpression; + qi::rule<Iterator, storm::expressions::Expression(), qi::locals<bool>, Skipper> minMaxExpression; + qi::rule<Iterator, storm::expressions::Expression(), qi::locals<bool>, Skipper> floorCeilExpression; + qi::rule<Iterator, std::string(), Skipper> identifier; + + // Parser that is used to recognize doubles only (as opposed to Spirit's double_ parser). + boost::spirit::qi::real_parser<double, boost::spirit::qi::strict_real_policies<double>> strict_double; + + // Helper functions to create expressions. + storm::expressions::Expression createIteExpression(storm::expressions::Expression e1, storm::expressions::Expression e2, storm::expressions::Expression e3) const; + storm::expressions::Expression createImpliesExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const; + storm::expressions::Expression createOrExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const; + storm::expressions::Expression createAndExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const; + storm::expressions::Expression createGreaterExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const; + storm::expressions::Expression createGreaterOrEqualExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const; + storm::expressions::Expression createLessExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const; + storm::expressions::Expression createLessOrEqualExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const; + storm::expressions::Expression createEqualsExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const; + storm::expressions::Expression createNotEqualsExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const; + storm::expressions::Expression createPlusExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const; + storm::expressions::Expression createMinusExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const; + storm::expressions::Expression createMultExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const; + storm::expressions::Expression createPowerExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const; + storm::expressions::Expression createDivExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const; + storm::expressions::Expression createNotExpression(storm::expressions::Expression e1) const; + storm::expressions::Expression createMinusExpression(storm::expressions::Expression e1) const; + storm::expressions::Expression createTrueExpression() const; + storm::expressions::Expression createFalseExpression() const; + storm::expressions::Expression createDoubleLiteralExpression(double value, bool& pass) const; + storm::expressions::Expression createIntegerLiteralExpression(int value) const; + storm::expressions::Expression createMinimumExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const; + storm::expressions::Expression createMaximumExpression(storm::expressions::Expression e1, storm::expressions::Expression e2) const; + storm::expressions::Expression createFloorExpression(storm::expressions::Expression e1) const; + storm::expressions::Expression createCeilExpression(storm::expressions::Expression e1) const; + storm::expressions::Expression getIdentifierExpression(std::string const& identifier) const; + + bool isValidIdentifier(std::string const& identifier); + + // Functor used for displaying error information. + struct ErrorHandler { + typedef qi::error_handler_result result_type; + + template<typename T1, typename T2, typename T3, typename T4> + qi::error_handler_result operator()(T1 b, T2 e, T3 where, T4 const& what) const { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Parsing error in line " << get_line(where) << ": " << " expecting " << what << "."); + return qi::fail; + } + }; + + // An error handler function. + phoenix::function<ErrorHandler> handler; + }; + } // namespace parser +} // namespace storm + +#endif /* STORM_PARSER_EXPRESSIONPARSER_H_ */ \ No newline at end of file diff --git a/src/parser/LtlFileParser.cpp b/src/parser/LtlFileParser.cpp index 7f77ff4da..a53557b89 100644 --- a/src/parser/LtlFileParser.cpp +++ b/src/parser/LtlFileParser.cpp @@ -5,9 +5,13 @@ * Author: thomas */ +#include <fstream> + #include "LtlFileParser.h" #include "LtlParser.h" +#include "src/exceptions/FileIoException.h" + namespace storm { namespace parser { diff --git a/src/parser/LtlParser.h b/src/parser/LtlParser.h index 20e82896b..cefcd8f2c 100644 --- a/src/parser/LtlParser.h +++ b/src/parser/LtlParser.h @@ -8,7 +8,6 @@ #ifndef STORM_PARSER_LTLPARSER_H_ #define STORM_PARSER_LTLPARSER_H_ -#include "Parser.h" #include "src/formula/Ltl.h" namespace storm { diff --git a/src/parser/MappedFile.cpp b/src/parser/MappedFile.cpp new file mode 100644 index 000000000..2b7e13a05 --- /dev/null +++ b/src/parser/MappedFile.cpp @@ -0,0 +1,112 @@ +/* + * MappedFile.cpp + * + * Created on: Jan 21, 2014 + * Author: Manuel Sascha Weiand + */ + +#include "src/parser/MappedFile.h" + +#include <fstream> +#include <cstring> +#include <fcntl.h> + +#include <boost/integer/integer_mask.hpp> + +#include "src/exceptions/FileIoException.h" + +#include "log4cplus/logger.h" +#include "log4cplus/loggingmacros.h" +extern log4cplus::Logger logger; + +namespace storm { + namespace parser { + + MappedFile::MappedFile(const char* filename) { + #if defined LINUX || defined MACOSX + + // Do file mapping for reasonable systems. + // stat64(), open(), mmap() + + #ifdef MACOSX + if (stat(filename, &(this->st)) != 0) { + #else + if (stat64(filename, &(this->st)) != 0) { + #endif + LOG4CPLUS_ERROR(logger, "Error in stat(" << filename << "): Probably, this file does not exist."); + throw exceptions::FileIoException() << "MappedFile Error in stat(): Probably, this file does not exist."; + } + this->file = open(filename, O_RDONLY); + + if (this->file < 0) { + LOG4CPLUS_ERROR(logger, "Error in open(" << filename << "): Probably, we may not read this file."); + throw exceptions::FileIoException() << "MappedFile Error in open(): Probably, we may not read this file."; + } + + this->data = reinterpret_cast<char*>(mmap(NULL, this->st.st_size, PROT_READ, MAP_PRIVATE, this->file, 0)); + if (this->data == reinterpret_cast<char*>(-1)) { + close(this->file); + LOG4CPLUS_ERROR(logger, "Error in mmap(" << filename << "): " << std::strerror(errno)); + throw exceptions::FileIoException() << "MappedFile Error in mmap(): " << std::strerror(errno); + } + this->dataEnd = this->data + this->st.st_size; + #elif defined WINDOWS + + // Do file mapping for windows. + // _stat64(), CreateFile(), CreateFileMapping(), MapViewOfFile() + + if (_stat64(filename, &(this->st)) != 0) { + LOG4CPLUS_ERROR(logger, "Error in _stat(" << filename << "): Probably, this file does not exist."); + throw exceptions::FileIoException("MappedFile Error in stat(): Probably, this file does not exist."); + } + + this->file = CreateFileA(filename, GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (this->file == INVALID_HANDLE_VALUE) { + LOG4CPLUS_ERROR(logger, "Error in CreateFileA(" << filename << "): Probably, we may not read this file."); + throw exceptions::FileIoException("MappedFile Error in CreateFileA(): Probably, we may not read this file."); + } + + this->mapping = CreateFileMappingA(this->file, NULL, PAGE_READONLY, (DWORD)(st.st_size >> 32), (DWORD)st.st_size, NULL); + if (this->mapping == NULL) { + CloseHandle(this->file); + LOG4CPLUS_ERROR(logger, "Error in CreateFileMappingA(" << filename << ")."); + throw exceptions::FileIoException("MappedFile Error in CreateFileMappingA()."); + } + + this->data = static_cast<char*>(MapViewOfFile(this->mapping, FILE_MAP_READ, 0, 0, this->st.st_size)); + if (this->data == NULL) { + CloseHandle(this->mapping); + CloseHandle(this->file); + LOG4CPLUS_ERROR(logger, "Error in MapViewOfFile(" << filename << ")."); + throw exceptions::FileIoException("MappedFile Error in MapViewOfFile()."); + } + this->dataEnd = this->data + this->st.st_size; + #endif + } + + MappedFile::~MappedFile() { + #if defined LINUX || defined MACOSX + munmap(this->data, this->st.st_size); + close(this->file); + #elif defined WINDOWS + CloseHandle(this->mapping); + CloseHandle(this->file); + #endif + } + + bool MappedFile::fileExistsAndIsReadable(const char* filename) { + // Test by opening an input file stream and testing the stream flags. + std::ifstream fin(filename); + return fin.good(); + } + + char* MappedFile::getData() { + return data; + } + + char* MappedFile::getDataEnd() { + return dataEnd; + } + + } // namespace parser +} // namespace storm diff --git a/src/parser/MappedFile.h b/src/parser/MappedFile.h new file mode 100644 index 000000000..6de9877d4 --- /dev/null +++ b/src/parser/MappedFile.h @@ -0,0 +1,111 @@ +/* + * MappedFile.h + * + * Created on: Jan 21, 2014 + * Author: Manuel Sascha Weiand + */ + +#ifndef STORM_PARSER_MAPPEDFILE_H_ +#define STORM_PARSER_MAPPEDFILE_H_ + +#include <sys/stat.h> + +#include "src/utility/OsDetection.h" + +namespace storm { + namespace parser { + + #if !defined LINUX && !defined MACOSX && !defined WINDOWS + #error Platform not supported + #endif + + /*! + * Opens a file and maps it to memory providing a char* + * containing the file content. + * + * This class is a very simple interface to read files efficiently. + * The given file is opened and mapped to memory using mmap(). + * The public member data is a pointer to the actual file content. + * Using this method, the kernel will take care of all buffering. + * This is most probably much more efficient than doing this manually. + */ + class MappedFile { + + public: + + /*! + * Constructs a MappedFile. + * This will stat the given file, open it and map it to memory. + * If anything of this fails, an appropriate exception is raised and a log entry is written. + * + * @param filename Path and name of the file to be opened. + */ + MappedFile(const char* filename); + + /*! + * Destructs a MappedFile. + * This will unmap the data and close the file. + */ + ~MappedFile(); + + /*! + * Tests whether the given file exists and is readable. + * + * @param filename Path and name of the file to be tested. + * @return True iff the file exists and is readable. + */ + static bool fileExistsAndIsReadable(const char* filename); + + /*! + * Returns a pointer to the beginning of the mapped file data. + * + * @return A pointer to the first character of the mapped file data. + */ + char* getData(); + + /*! + * Returns a pointer to the end of the mapped file data. + * + * @return A pointer to the first position after the last character of the mapped file data. + */ + char* getDataEnd(); + + private: + + //! A pointer to the mapped file content. + char* data; + + //! A pointer to end of the mapped file content. + char* dataEnd; + + #if defined LINUX || defined MACOSX + + //! The file descriptor obtained by open(). + int file; + #elif defined WINDOWS + //! The file handle obtained by opening the file. + HANDLE file; + + //! The handle referencing the created memory mapping. + HANDLE mapping; + #endif + + #if defined LINUX + + //! Stat information about the file. + struct stat64 st; + #elif defined MACOSX + + //! Stat information about the file. + struct stat st; + #elif defined WINDOWS + + //! Stat information about the file. + struct __stat64 st; + #endif + }; + + } // namespace parser +} // namespace storm + +#endif /* STORM_PARSER_MAPPEDFILE_H_ */ diff --git a/src/parser/MarkovAutomatonParser.cpp b/src/parser/MarkovAutomatonParser.cpp index 2e673da76..707436dab 100644 --- a/src/parser/MarkovAutomatonParser.cpp +++ b/src/parser/MarkovAutomatonParser.cpp @@ -1,28 +1,43 @@ #include "MarkovAutomatonParser.h" #include "AtomicPropositionLabelingParser.h" #include "SparseStateRewardParser.h" +#include "src/exceptions/WrongFormatException.h" + +#include "log4cplus/logger.h" +#include "log4cplus/loggingmacros.h" +extern log4cplus::Logger logger; namespace storm { - namespace parser { - - storm::models::MarkovAutomaton<double> MarkovAutomatonParser::parseMarkovAutomaton(std::string const& transitionsFilename, std::string const& labelingFilename, std::string const& stateRewardFilename, std::string const& transitionRewardFilename) { - storm::parser::MarkovAutomatonSparseTransitionParser::ResultType transitionResult(storm::parser::MarkovAutomatonSparseTransitionParser::parseMarkovAutomatonTransitions(transitionsFilename)); - storm::models::AtomicPropositionsLabeling resultLabeling(storm::parser::AtomicPropositionLabelingParser(transitionResult.transitionMatrix.getColumnCount(), labelingFilename)); - - boost::optional<std::vector<double>> stateRewards; - if (stateRewardFilename != "") { - stateRewards.reset(storm::parser::SparseStateRewardParser(transitionResult.transitionMatrix.getColumnCount(), stateRewardFilename)); - } - - if (transitionRewardFilename != "") { - LOG4CPLUS_ERROR(logger, "Transition rewards are unsupported for Markov automata."); - throw storm::exceptions::WrongFormatException() << "Transition rewards are unsupported for Markov automata."; - } - - storm::models::MarkovAutomaton<double> resultingAutomaton(std::move(transitionResult.transitionMatrix), std::move(resultLabeling), std::move(transitionResult.markovianStates), std::move(transitionResult.exitRates), std::move(stateRewards), boost::optional<storm::storage::SparseMatrix<double>>(), boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>>()); - - return resultingAutomaton; - } - - } // namespace parser -} // namespace storm \ No newline at end of file + namespace parser { + + storm::models::MarkovAutomaton<double> MarkovAutomatonParser::parseMarkovAutomaton(std::string const& transitionsFilename, std::string const& labelingFilename, std::string const& stateRewardFilename, std::string const& transitionRewardFilename) { + + // Parse the transitions of the Markov Automaton. + storm::parser::MarkovAutomatonSparseTransitionParser::Result transitionResult(storm::parser::MarkovAutomatonSparseTransitionParser::parseMarkovAutomatonTransitions(transitionsFilename)); + + // Build the actual transition matrix using the MatrixBuilder provided by the transitionResult. + storm::storage::SparseMatrix<double> transitionMatrix(transitionResult.transitionMatrixBuilder.build()); + + // Parse the state labeling. + storm::models::AtomicPropositionsLabeling resultLabeling(storm::parser::AtomicPropositionLabelingParser::parseAtomicPropositionLabeling(transitionMatrix.getColumnCount(), labelingFilename)); + + // If given, parse the state rewards file. + boost::optional<std::vector<double>> stateRewards; + if (stateRewardFilename != "") { + stateRewards.reset(storm::parser::SparseStateRewardParser::parseSparseStateReward(transitionMatrix.getColumnCount(), stateRewardFilename)); + } + + // Since Markov Automata do not support transition rewards no path should be given here. + if (transitionRewardFilename != "") { + LOG4CPLUS_ERROR(logger, "Transition rewards are unsupported for Markov automata."); + throw storm::exceptions::WrongFormatException() << "Transition rewards are unsupported for Markov automata."; + } + + // Put the pieces together to generate the Markov Automaton. + storm::models::MarkovAutomaton<double> resultingAutomaton(std::move(transitionMatrix), std::move(resultLabeling), std::move(transitionResult.markovianStates), std::move(transitionResult.exitRates), std::move(stateRewards), boost::optional<storm::storage::SparseMatrix<double>>(), boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>>()); + + return resultingAutomaton; + } + + } // namespace parser +} // namespace storm diff --git a/src/parser/MarkovAutomatonParser.h b/src/parser/MarkovAutomatonParser.h index c63274630..19dcbafd0 100644 --- a/src/parser/MarkovAutomatonParser.h +++ b/src/parser/MarkovAutomatonParser.h @@ -5,25 +5,33 @@ #include "src/parser/MarkovAutomatonSparseTransitionParser.h" namespace storm { - namespace parser { - - /*! - * A class providing the functionality to parse a labeled Markov automaton. - */ - class MarkovAutomatonParser { - public: - - /*! - * Parses the given Markov automaton and returns an object representing the automaton. - * - * @param transitionsFilename The name of the file containing the transitions of the Markov automaton. - * @param labelingFilename The name of the file containing the labels for the states of the Markov automaton. - * @param stateRewardFilename The name of the file that contains the state reward of the Markov automaton. - * @param transitionRewardFilename The name of the file that contains the transition rewards of the Markov automaton. - */ - static storm::models::MarkovAutomaton<double> parseMarkovAutomaton(std::string const& transitionsFilename, std::string const& labelingFilename, std::string const& stateRewardFilename, std::string const& transitionRewardFilename); - }; - } // namespace parser + namespace parser { + + /*! + * Loads a labeled Markov automaton from files. + * + * Given the file paths of the files holding the transitions, the atomic propositions and optionally the state rewards + * it loads the files, parses them and returns the desired model. + */ + class MarkovAutomatonParser { + public: + + /*! + * Parses the given Markov automaton and returns an object representing the automaton. + * + * @note The number of states of the model is determined by the transitions file. + * The labeling file may therefore not contain labels of states that are not contained in the transitions file. + * + * @param transitionsFilename The name of the file containing the transitions of the Markov automaton. + * @param labelingFilename The name of the file containing the labels for the states of the Markov automaton. + * @param stateRewardFilename The name of the file that contains the state reward of the Markov automaton. + * @param transitionRewardFilename The name of the file that contains the transition rewards of the Markov automaton. This should be empty as transition rewards are not supported by Markov Automata. + * @return The parsed MarkovAutomaton. + */ + static storm::models::MarkovAutomaton<double> parseMarkovAutomaton(std::string const& transitionsFilename, std::string const& labelingFilename, std::string const& stateRewardFilename = "", std::string const& transitionRewardFilename = ""); + }; + + } // namespace parser } // namespace storm #endif /* STORM_PARSER_MARKOVAUTOMATONPARSER_H_ */ diff --git a/src/parser/MarkovAutomatonSparseTransitionParser.cpp b/src/parser/MarkovAutomatonSparseTransitionParser.cpp index aa7a1406e..21403c3be 100644 --- a/src/parser/MarkovAutomatonSparseTransitionParser.cpp +++ b/src/parser/MarkovAutomatonSparseTransitionParser.cpp @@ -1,264 +1,283 @@ #include "MarkovAutomatonSparseTransitionParser.h" #include "src/settings/Settings.h" +#include "src/exceptions/WrongFormatException.h" +#include "src/exceptions/FileIoException.h" +#include "src/parser/MappedFile.h" +#include "src/utility/cstring.h" + namespace storm { - namespace parser { - - MarkovAutomatonSparseTransitionParser::FirstPassResult MarkovAutomatonSparseTransitionParser::firstPass(char* buf, SupportedLineEndingsEnum lineEndings) { - MarkovAutomatonSparseTransitionParser::FirstPassResult result; - - bool fixDeadlocks = storm::settings::Settings::getInstance()->isSet("fixDeadlocks"); - - // Skip the format hint. - buf = storm::parser::forwardToNextLine(buf, lineEndings); - - // Now read the transitions. - int_fast64_t source, target = -1; - int_fast64_t lastsource = -1; - bool encounteredEOF = false; - bool stateHasMarkovianChoice = false; - bool stateHasProbabilisticChoice = false; - while (buf[0] != '\0' && !encounteredEOF) { - // At the current point, the next thing to read is the source state of the next choice to come. - source = checked_strtol(buf, &buf); - - // Check if we encountered a state index that is bigger than all previously seen ones and record it if necessary. - if (source > result.highestStateIndex) { - result.highestStateIndex = source; - } - - // If we have skipped some states, we need to reserve the space for the self-loop insertion in the second pass. - if (source > lastsource + 1) { - if (fixDeadlocks) { - result.numberOfNonzeroEntries += source - lastsource - 1; - result.numberOfChoices += source - lastsource - 1; - } else { - LOG4CPLUS_ERROR(logger, "Found deadlock states (e.g. " << lastsource + 1 << ") during parsing. Please fix them or set the appropriate flag."); - throw storm::exceptions::WrongFormatException() << "Found deadlock states (e.g. " << lastsource + 1 << ") during parsing. Please fix them or set the appropriate flag."; - } - } else if (source < lastsource) { - LOG4CPLUS_ERROR(logger, "Illegal state choice order. A choice of state " << source << " appears at an illegal position."); - throw storm::exceptions::WrongFormatException() << "Illegal state choice order. A choice of state " << source << " appears at an illegal position."; - } - - ++result.numberOfChoices; - - // If we have moved to the next state, we need to clear the flag that stores whether or not the source has a Markovian or probabilistic choice. - if (source != lastsource) { - stateHasMarkovianChoice = false; - stateHasProbabilisticChoice = false; - } - - // Record that the current source was the last source. - lastsource = source; - - buf = trimWhitespaces(buf); - - // Depending on the action name, the choice is either a probabilitic one or a markovian one. - bool isMarkovianChoice = false; - if (buf[0] == '!') { - isMarkovianChoice = true; - } else { - isMarkovianChoice = false; - } - ++buf; - - if (isMarkovianChoice) { - if (stateHasMarkovianChoice) { - LOG4CPLUS_ERROR(logger, "The state " << source << " has multiple Markovian choices."); - throw storm::exceptions::WrongFormatException() << "The state " << source << " has multiple Markovian choices."; - } - if (stateHasProbabilisticChoice) { - LOG4CPLUS_ERROR(logger, "The state " << source << " has a probabilistic choice preceding a Markovian choice. The Markovian choice must be the first choice listed."); - throw storm::exceptions::WrongFormatException() << "The state " << source << " has a probabilistic choice preceding a Markovian choice. The Markovian choice must be the first choice listed."; - } - stateHasMarkovianChoice = true; - } else { - stateHasProbabilisticChoice = true; - } - - buf = forwardToNextLine(buf, lineEndings); - - // Now that we have the source state and the information whether or not the current choice is probabilistic or Markovian, we need to read the list of successors and the probabilities/rates. - bool hasSuccessorState = false; - bool encounteredNewDistribution = false; - uint_fast64_t lastSuccessorState = 0; - - // At this point, we need to check whether there is an additional successor or we have reached the next choice for the same or a different state. - do { - buf = trimWhitespaces(buf); - // If the end of the file was reached, we need to abort and check whether we are in a legal state. - if (buf[0] == '\0') { - if (!hasSuccessorState) { - LOG4CPLUS_ERROR(logger, "Premature end-of-file. Expected at least one successor state for state " << source << "."); - throw storm::exceptions::WrongFormatException() << "Premature end-of-file. Expected at least one successor state for state " << source << "."; - } else { - // If there was at least one successor for the current choice, this is legal and we need to move on. - encounteredEOF = true; - } - } else if (buf[0] == '*') { - // As we have encountered a "*", we know that there is an additional successor state for the current choice. - ++buf; - - // Now we need to read the successor state and check if we already saw a higher state index. - target = checked_strtol(buf, &buf); - if (target > result.highestStateIndex) { - result.highestStateIndex = target; - } - if (hasSuccessorState && target <= lastSuccessorState) { - LOG4CPLUS_ERROR(logger, "Illegal transition order for source state " << source << "."); - throw storm::exceptions::WrongFormatException() << "Illegal transition order for source state " << source << "."; - } - - // And the corresponding probability/rate. - double val = checked_strtod(buf, &buf); - if (val <= 0.0) { - LOG4CPLUS_ERROR(logger, "Illegal probability/rate value for transition from " << source << " to " << target << ": " << val << "."); - throw storm::exceptions::WrongFormatException() << "Illegal probability/rate value for transition from " << source << " to " << target << ": " << val << "."; - } - - // We need to record that we found at least one successor state for the current choice. - hasSuccessorState = true; - lastSuccessorState = target; - - // As we found a new successor, we need to increase the number of nonzero entries. - ++result.numberOfNonzeroEntries; - - buf = forwardToNextLine(buf, lineEndings); - } else { - // If it was not a "*", we have to assume that we encountered the beginning of a new choice definition. In this case, we don't move the pointer - // to the buffer, because we still need to read the new source state. - encounteredNewDistribution = true; - } - } while (!encounteredEOF && !encounteredNewDistribution); - } - - return result; - } - - MarkovAutomatonSparseTransitionParser::ResultType MarkovAutomatonSparseTransitionParser::secondPass(char* buf, SupportedLineEndingsEnum lineEndings, FirstPassResult const& firstPassResult) { - ResultType result(firstPassResult); - storm::storage::SparseMatrixBuilder<double> matrixBuilder(firstPassResult.numberOfChoices, firstPassResult.highestStateIndex + 1, firstPassResult.numberOfNonzeroEntries, true); - - bool fixDeadlocks = storm::settings::Settings::getInstance()->isSet("fixDeadlocks"); - - // Skip the format hint. - buf = storm::parser::forwardToNextLine(buf, lineEndings); - - // Now read the transitions. - int_fast64_t source, target = -1; - int_fast64_t lastsource = -1; - bool encounteredEOF = false; - uint_fast64_t currentChoice = 0; - while (buf[0] != '\0' && !encounteredEOF) { - // At the current point, the next thing to read is the source state of the next choice to come. - source = checked_strtol(buf, &buf); - - // If we have skipped some states, we need to insert self-loops if requested. - if (source > lastsource + 1) { - if (fixDeadlocks) { - for (uint_fast64_t index = lastsource + 1; index < source; ++index) { - matrixBuilder.newRowGroup(currentChoice); - matrixBuilder.addNextValue(currentChoice, index, 1); - ++currentChoice; - } - } else { - LOG4CPLUS_ERROR(logger, "Found deadlock states (e.g. " << lastsource + 1 << ") during parsing. Please fix them or set the appropriate flag."); - throw storm::exceptions::WrongFormatException() << "Found deadlock states (e.g. " << lastsource + 1 << ") during parsing. Please fix them or set the appropriate flag."; - } - } - - if (source != lastsource) { - // If we skipped to a new state we need to record the beginning of the choices in the nondeterministic choice indices. - matrixBuilder.newRowGroup(currentChoice); - } - - // Record that the current source was the last source. - lastsource = source; - - buf = trimWhitespaces(buf); - - // Depending on the action name, the choice is either a probabilitic one or a markovian one. - bool isMarkovianChoice = false; - if (buf[0] == '!') { - isMarkovianChoice = true; - - // Mark the current state as a Markovian one. - result.markovianStates.set(source, true); - } else { - isMarkovianChoice = false; - } - - buf = forwardToNextLine(buf, lineEndings); - - // Now that we have the source state and the information whether or not the current choice is probabilistic or Markovian, we need to read the list of successors and the probabilities/rates. - bool hasSuccessorState = false; - bool encounteredNewDistribution = false; - - // At this point, we need to check whether there is an additional successor or we have reached the next choice for the same or a different state. - do { - buf = trimWhitespaces(buf); - - // If the end of the file was reached, we need to abort and check whether we are in a legal state. - if (buf[0] == '\0') { - // Under the assumption that the currently open choice has at least one successor (which is given after the first run) - // we may legally stop reading here. - encounteredEOF = true; - } else if (buf[0] == '*') { - // We need to record that we found at least one successor state for the current choice. - hasSuccessorState = true; - - // As we have encountered a "*", we know that there is an additional successor state for the current choice. - ++buf; - - // Now we need to read the successor state and check if we already saw a higher state index. - target = checked_strtol(buf, &buf); - - // And the corresponding probability/rate. - double val = checked_strtod(buf, &buf); - - // Record the value as well as the exit rate in case of a Markovian choice. - matrixBuilder.addNextValue(currentChoice, target, val); - if (isMarkovianChoice) { - result.exitRates[source] += val; - } - - buf = forwardToNextLine(buf, lineEndings); - } else { - // If it was not a "*", we have to assume that we encountered the beginning of a new choice definition. In this case, we don't move the pointer - // to the buffer, because we still need to read the new source state. - encounteredNewDistribution = true; - } - } while (!encounteredEOF && !encounteredNewDistribution); - - ++currentChoice; - } - - // As we have added all entries at this point, we need to finalize the matrix. - result.transitionMatrix = matrixBuilder.build(); - - return result; - } - - MarkovAutomatonSparseTransitionParser::ResultType MarkovAutomatonSparseTransitionParser::parseMarkovAutomatonTransitions(std::string const& filename) { - // Set the locale to correctly recognize floating point numbers. - setlocale(LC_NUMERIC, "C"); - - if (!fileExistsAndIsReadable(filename.c_str())) { - LOG4CPLUS_ERROR(logger, "Error while parsing " << filename << ": File does not exist or is not readable."); - throw storm::exceptions::WrongFormatException() << "Error while parsing " << filename << ": File does not exist or is not readable."; - } - - // Determine used line endings. - SupportedLineEndingsEnum lineEndings = findUsedLineEndings(filename, true); - - // Open file and prepare pointer to buffer. - MappedFile file(filename.c_str()); - char* buf = file.data; - - return secondPass(buf, lineEndings, firstPass(buf, lineEndings)); - } - - } // namespace parser + namespace parser { + + using namespace storm::utility::cstring; + + MarkovAutomatonSparseTransitionParser::FirstPassResult MarkovAutomatonSparseTransitionParser::firstPass(char* buf) { + MarkovAutomatonSparseTransitionParser::FirstPassResult result; + + bool fixDeadlocks = storm::settings::Settings::getInstance()->isSet("fixDeadlocks"); + + // Skip the format hint if it is there. + buf = trimWhitespaces(buf); + if(buf[0] < '0' || buf[0] > '9') { + buf = forwardToLineEnd(buf); + buf = trimWhitespaces(buf); + } + + // Now read the transitions. + uint_fast64_t source, target = 0; + uint_fast64_t lastsource = 0; + bool encounteredEOF = false; + bool stateHasMarkovianChoice = false; + bool stateHasProbabilisticChoice = false; + while (buf[0] != '\0' && !encounteredEOF) { + + // At the current point, the next thing to read is the source state of the next choice to come. + source = checked_strtol(buf, &buf); + + // Check if we encountered a state index that is bigger than all previously seen ones and record it if necessary. + if (source > result.highestStateIndex) { + result.highestStateIndex = source; + } + + // If we have skipped some states, we need to reserve the space for the self-loop insertion in the second pass. + if (source > lastsource + 1) { + if (fixDeadlocks) { + result.numberOfNonzeroEntries += source - lastsource - 1; + result.numberOfChoices += source - lastsource - 1; + } else { + LOG4CPLUS_ERROR(logger, "Found deadlock states (e.g. " << lastsource + 1 << ") during parsing. Please fix them or set the appropriate flag."); + throw storm::exceptions::WrongFormatException() << "Found deadlock states (e.g. " << lastsource + 1 << ") during parsing. Please fix them or set the appropriate flag."; + } + } else if (source < lastsource) { + LOG4CPLUS_ERROR(logger, "Illegal state choice order. A choice of state " << source << " appears at an illegal position."); + throw storm::exceptions::WrongFormatException() << "Illegal state choice order. A choice of state " << source << " appears at an illegal position."; + } + + ++result.numberOfChoices; + + // If we have moved to the next state, we need to clear the flag that stores whether or not the source has a Markovian or probabilistic choice. + if (source != lastsource) { + stateHasMarkovianChoice = false; + stateHasProbabilisticChoice = false; + } + + // Record that the current source was the last source. + lastsource = source; + + buf = trimWhitespaces(buf); + + // Depending on the action name, the choice is either a probabilitic one or a markovian one. + bool isMarkovianChoice = false; + if (buf[0] == '!' && skipWord(buf) - buf == 1) { + isMarkovianChoice = true; + }else { + isMarkovianChoice = false; + } + buf = skipWord(buf); + + if (isMarkovianChoice) { + if (stateHasMarkovianChoice) { + LOG4CPLUS_ERROR(logger, "The state " << source << " has multiple Markovian choices."); + throw storm::exceptions::WrongFormatException() << "The state " << source << " has multiple Markovian choices."; + } + if (stateHasProbabilisticChoice) { + LOG4CPLUS_ERROR(logger, "The state " << source << " has a probabilistic choice preceding a Markovian choice. The Markovian choice must be the first choice listed."); + throw storm::exceptions::WrongFormatException() << "The state " << source << " has a probabilistic choice preceding a Markovian choice. The Markovian choice must be the first choice listed."; + } + stateHasMarkovianChoice = true; + } else { + stateHasProbabilisticChoice = true; + } + + // Go to the next line where the transitions start. + buf = forwardToNextLine(buf); + + // Now that we have the source state and the information whether or not the current choice is probabilistic or Markovian, we need to read the list of successors and the probabilities/rates. + bool hasSuccessorState = false; + bool encounteredNewDistribution = false; + uint_fast64_t lastSuccessorState = 0; + + // At this point, we need to check whether there is an additional successor or we have reached the next choice for the same or a different state. + do { + buf = trimWhitespaces(buf); + // If the end of the file was reached, we need to abort and check whether we are in a legal state. + if (buf[0] == '\0') { + if (!hasSuccessorState) { + LOG4CPLUS_ERROR(logger, "Premature end-of-file. Expected at least one successor state for state " << source << "."); + throw storm::exceptions::WrongFormatException() << "Premature end-of-file. Expected at least one successor state for state " << source << "."; + } else { + // If there was at least one successor for the current choice, this is legal and we need to move on. + encounteredEOF = true; + } + } else if (buf[0] == '*') { + // As we have encountered a "*", we know that there is an additional successor state for the current choice. + buf= skipWord(buf); + + // Now we need to read the successor state and check if we already saw a higher state index. + target = checked_strtol(buf, &buf); + if (target > result.highestStateIndex) { + result.highestStateIndex = target; + } + if (hasSuccessorState && target <= lastSuccessorState) { + LOG4CPLUS_ERROR(logger, "Illegal transition order for source state " << source << "."); + throw storm::exceptions::WrongFormatException() << "Illegal transition order for source state " << source << "."; + } + + // And the corresponding probability/rate. + double val = checked_strtod(buf, &buf); + if (val < 0.0) { + LOG4CPLUS_ERROR(logger, "Illegal negative probability/rate value for transition from " << source << " to " << target << ": " << val << "."); + throw storm::exceptions::WrongFormatException() << "Illegal negative probability/rate value for transition from " << source << " to " << target << ": " << val << "."; + } + if (!isMarkovianChoice && val > 1.0) { + LOG4CPLUS_ERROR(logger, "Illegal probability value for transition from " << source << " to " << target << ": " << val << "."); + throw storm::exceptions::WrongFormatException() << "Illegal probability value for transition from " << source << " to " << target << ": " << val << "."; + } + + // We need to record that we found at least one successor state for the current choice. + hasSuccessorState = true; + lastSuccessorState = target; + + // As we found a new successor, we need to increase the number of nonzero entries. + ++result.numberOfNonzeroEntries; + + buf = forwardToNextLine(buf); + } else { + // If it was not a "*", we have to assume that we encountered the beginning of a new choice definition. In this case, we don't move the pointer + // to the buffer, because we still need to read the new source state. + encounteredNewDistribution = true; + } + } while (!encounteredEOF && !encounteredNewDistribution); + } + + return result; + } + + MarkovAutomatonSparseTransitionParser::Result MarkovAutomatonSparseTransitionParser::secondPass(char* buf, FirstPassResult const& firstPassResult) { + Result result(firstPassResult); + + bool fixDeadlocks = storm::settings::Settings::getInstance()->isSet("fixDeadlocks"); + + // Skip the format hint if it is there. + buf = trimWhitespaces(buf); + if(buf[0] < '0' || buf[0] > '9') { + buf = forwardToLineEnd(buf); + buf = trimWhitespaces(buf); + } + + // Now read the transitions. + uint_fast64_t source, target = 0; + uint_fast64_t lastsource = 0; + bool encounteredEOF = false; + uint_fast64_t currentChoice = 0; + + // The first choice of the first state already starts a new row group of the matrix. + result.transitionMatrixBuilder.newRowGroup(0); + + while (buf[0] != '\0' && !encounteredEOF) { + // At the current point, the next thing to read is the source state of the next choice to come. + source = checked_strtol(buf, &buf); + + // If we have skipped some states, we need to insert self-loops if requested. + if (source > lastsource + 1) { + if (fixDeadlocks) { + for (uint_fast64_t index = lastsource + 1; index < source; ++index) { + result.transitionMatrixBuilder.newRowGroup(currentChoice); + result.transitionMatrixBuilder.addNextValue(currentChoice, index, 1); + ++currentChoice; + } + } else { + LOG4CPLUS_ERROR(logger, "Found deadlock states (e.g. " << lastsource + 1 << ") during parsing. Please fix them or set the appropriate flag."); + throw storm::exceptions::WrongFormatException() << "Found deadlock states (e.g. " << lastsource + 1 << ") during parsing. Please fix them or set the appropriate flag."; + } + } + + if (source != lastsource) { + // If we skipped to a new state we need to create a new row group for the choices of the new state. + result.transitionMatrixBuilder.newRowGroup(currentChoice); + } + + // Record that the current source was the last source. + lastsource = source; + + buf = trimWhitespaces(buf); + + // Depending on the action name, the choice is either a probabilitic one or a markovian one. + bool isMarkovianChoice = false; + if (buf[0] == '!' && skipWord(buf) - buf == 1) { + isMarkovianChoice = true; + + // Mark the current state as a Markovian one. + result.markovianStates.set(source, true); + } else { + isMarkovianChoice = false; + } + + // Go to the next line where the transitions start. + buf = forwardToNextLine(buf); + + // Now that we have the source state and the information whether or not the current choice is probabilistic or Markovian, we need to read the list of successors and the probabilities/rates. + bool encounteredNewDistribution = false; + + // At this point, we need to check whether there is an additional successor or we have reached the next choice for the same or a different state. + do { + buf = trimWhitespaces(buf); + + // If the end of the file was reached, we need to abort and check whether we are in a legal state. + if (buf[0] == '\0') { + // Under the assumption that the currently open choice has at least one successor (which is given after the first run) + // we may legally stop reading here. + encounteredEOF = true; + } else if (buf[0] == '*') { + + // As we have encountered a "*", we know that there is an additional successor state for the current choice. + buf = skipWord(buf); + + // Now we need to read the successor state and check if we already saw a higher state index. + target = checked_strtol(buf, &buf); + + // And the corresponding probability/rate. + double val = checked_strtod(buf, &buf); + + // Record the value as well as the exit rate in case of a Markovian choice. + result.transitionMatrixBuilder.addNextValue(currentChoice, target, val); + if (isMarkovianChoice) { + result.exitRates[source] += val; + } + + buf = forwardToNextLine(buf); + } else { + // If it was not a "*", we have to assume that we encountered the beginning of a new choice definition. In this case, we don't move the pointer + // to the buffer, because we still need to read the new source state. + encounteredNewDistribution = true; + } + } while (!encounteredEOF && !encounteredNewDistribution); + + ++currentChoice; + } + + // Put a sentinel element at the end. + result.transitionMatrixBuilder.newRowGroup(currentChoice); + + return result; + } + + MarkovAutomatonSparseTransitionParser::Result MarkovAutomatonSparseTransitionParser::parseMarkovAutomatonTransitions(std::string const& filename) { + // Set the locale to correctly recognize floating point numbers. + setlocale(LC_NUMERIC, "C"); + + if (!MappedFile::fileExistsAndIsReadable(filename.c_str())) { + LOG4CPLUS_ERROR(logger, "Error while parsing " << filename << ": File does not exist or is not readable."); + throw storm::exceptions::FileIoException() << "Error while parsing " << filename << ": File does not exist or is not readable."; + } + + // Open file and prepare pointer to buffer. + MappedFile file(filename.c_str()); + char* buf = file.getData(); + + return secondPass(buf, firstPass(buf)); + } + + } // namespace parser } // namespace storm diff --git a/src/parser/MarkovAutomatonSparseTransitionParser.h b/src/parser/MarkovAutomatonSparseTransitionParser.h index 48ed619ef..798a9e0e3 100644 --- a/src/parser/MarkovAutomatonSparseTransitionParser.h +++ b/src/parser/MarkovAutomatonSparseTransitionParser.h @@ -3,89 +3,101 @@ #include "src/storage/SparseMatrix.h" #include "src/storage/BitVector.h" -#include "Parser.h" namespace storm { - namespace parser { - - /* - * A class providing the functionality to parse the transitions of a Markov automaton. - */ - class MarkovAutomatonSparseTransitionParser { - public: - /* - * A structure representing the result of the first pass of this parser. It contains the number of non-zero entries in the model, the highest state index - * and the total number of choices. - */ - struct FirstPassResult { - - FirstPassResult() : numberOfNonzeroEntries(0), highestStateIndex(0), numberOfChoices(0) { - // Intentionally left empty. - } - - // The total number of non-zero entries of the model. - uint_fast64_t numberOfNonzeroEntries; - - // The highest state index that appears in the model. - uint_fast64_t highestStateIndex; - - // The total number of choices in the model. - uint_fast64_t numberOfChoices; - }; - - /* - * A structure representing the result of the parser. It contains the sparse matrix that represents the transitions (along with a vector indicating - * at which index the choices of a given state begin) as well as the exit rates for all Markovian choices. - */ - struct ResultType { - - ResultType(FirstPassResult const& firstPassResult) : transitionMatrix(), markovianChoices(firstPassResult.numberOfChoices), markovianStates(firstPassResult.highestStateIndex + 1), exitRates(firstPassResult.highestStateIndex + 1) { - // Intentionally left empty. - } - - // A matrix representing the transitions of the model. - storm::storage::SparseMatrix<double> transitionMatrix; - - // A bit vector indicating which choices are Markovian. By duality, all other choices are probabilitic. - storm::storage::BitVector markovianChoices; - - // A bit vector indicating which states possess a Markovian choice. - storm::storage::BitVector markovianStates; - - // A vector that stores the exit rates for each - std::vector<double> exitRates; - }; - - /*! - * Parses the given file under the assumption that it contains a Markov automaton specified in the appropriate format. - * - * @param filename The name of the file to parse. - * @return A structure representing the result of the parser. - */ - static ResultType parseMarkovAutomatonTransitions(std::string const& filename); - - private: - /* - * Performs the first pass on the input pointed to by the given buffer. - * - * @param buffer The buffer that cointains the input. - * @param lineEndings The line endings that are to be used while parsing. - * @return A structure representing the result of the first pass. - */ - static FirstPassResult firstPass(char* buffer, SupportedLineEndingsEnum lineEndings); - - /* - * Performs the second pass on the input pointed to by the given buffer with the information of the first pass. - * - * @param buffer The buffer that cointains the input. - * @param lineEndings The line endings that are to be used while parsing. - * @param firstPassResult The result of the first pass performed on the same input. - * @return A structure representing the result of the second pass. - */ - static ResultType secondPass(char* buffer, SupportedLineEndingsEnum lineEndings, FirstPassResult const& firstPassResult); - }; - - } // namespace parser + namespace parser { + + /*! + * A class providing the functionality to parse the transitions of a Markov automaton. + * + * The file is parsed in two passes. + * The first pass tests the file format and collects statistical data needed for the second pass. + * The second pass then collects the actual file data and compiles it into a Result. + */ + class MarkovAutomatonSparseTransitionParser { + public: + + /*! + * A structure representing the result of the first pass of this parser. It contains the number of non-zero entries in the model, the highest state index + * and the total number of choices. + */ + struct FirstPassResult { + + /*! + * The default constructor. + * Constructs an empty FirstPassResult. + */ + FirstPassResult() : numberOfNonzeroEntries(0), highestStateIndex(0), numberOfChoices(0) { + // Intentionally left empty. + } + + //! The total number of non-zero entries of the model. + uint_fast64_t numberOfNonzeroEntries; + + //! The highest state index that appears in the model. + uint_fast64_t highestStateIndex; + + //! The total number of nondeterministic choices in the model. + uint_fast64_t numberOfChoices; + }; + + /*! + * A structure representing the result of the parser. It contains the sparse matrix that represents the transitions (along with a vector indicating + * at which index the choices of a given state begin) as well as the exit rates for all Markovian choices. + */ + struct Result { + + /*! + * Creates a new instance of the struct using the result of the first pass to correctly initialize the container. + * + * @param firstPassResult A reference to the result of the first pass. + */ + Result(FirstPassResult const& firstPassResult) : transitionMatrixBuilder(firstPassResult.numberOfChoices, firstPassResult.highestStateIndex + 1, firstPassResult.numberOfNonzeroEntries, true, firstPassResult.highestStateIndex + 1), markovianChoices(firstPassResult.numberOfChoices), markovianStates(firstPassResult.highestStateIndex + 1), exitRates(firstPassResult.highestStateIndex + 1) { + // Intentionally left empty. + } + + //! A matrix representing the transitions of the model. + storm::storage::SparseMatrixBuilder<double> transitionMatrixBuilder; + + //! A bit vector indicating which choices are Markovian. By duality, all other choices are probabilitic. + storm::storage::BitVector markovianChoices; + + //! A bit vector indicating which states possess a Markovian choice. + storm::storage::BitVector markovianStates; + + //! A vector that stores the exit rates for each state. For all states that do not possess Markovian choices this is equal to 0. + std::vector<double> exitRates; + }; + + /*! + * Parses the given file under the assumption that it contains a Markov automaton specified in the appropriate format. + * + * @param filename The name of the file to parse. + * @return A structure representing the result of the parser. + */ + static Result parseMarkovAutomatonTransitions(std::string const& filename); + + private: + + /*! + * Performs the first pass on the input pointed to by the given buffer. + * + * @param buffer The buffer that cointains the input. + * @return A structure representing the result of the first pass. + */ + static FirstPassResult firstPass(char* buffer); + + /*! + * Performs the second pass on the input pointed to by the given buffer with the information of the first pass. + * + * @param buffer The buffer that cointains the input. + * @param firstPassResult The result of the first pass performed on the same input. + * @return A structure representing the result of the second pass. + */ + static Result secondPass(char* buffer, FirstPassResult const& firstPassResult); + }; + + } // namespace parser } // namespace storm #endif /* STORM_PARSER_MARKOVAUTOMATONSPARSETRANSITIONPARSER_H_ */ diff --git a/src/parser/NondeterministicModelParser.cpp b/src/parser/NondeterministicModelParser.cpp index 51aebff56..fc66dacfb 100644 --- a/src/parser/NondeterministicModelParser.cpp +++ b/src/parser/NondeterministicModelParser.cpp @@ -15,63 +15,49 @@ #include "src/parser/SparseStateRewardParser.h" namespace storm { -namespace parser { + namespace parser { -/*! - * Parses a transition file and a labeling file and produces an intermediate Result Container - * Note that the labeling file may have at most as many nodes as the transition file! - * - * @param transitionSystemFile String containing the location of the transition file (....tra) - * @param labelingFile String containing the location of the labeling file (....lab) - * @param stateRewardFile String containing the location of the state reward file (...srew) - * @param transitionRewardFile String containing the location of the transition reward file (...trew) - */ -NondeterministicModelParserResultContainer<double> parseNondeterministicModel(std::string const & transitionSystemFile, std::string const & labelingFile, - std::string const & stateRewardFile, std::string const & transitionRewardFile) { - - NondeterministicSparseTransitionParserResult_t nondeterministicSparseTransitionParserResult(std::move(storm::parser::NondeterministicSparseTransitionParser(transitionSystemFile))); - storm::storage::SparseMatrix<double> resultTransitionSystem(std::move(nondeterministicSparseTransitionParserResult.first)); - - uint_fast64_t stateCount = resultTransitionSystem.getColumnCount(); - uint_fast64_t rowCount = resultTransitionSystem.getRowCount(); - - storm::models::AtomicPropositionsLabeling resultLabeling(std::move(storm::parser::AtomicPropositionLabelingParser(stateCount, labelingFile))); - - NondeterministicModelParserResultContainer<double> result(std::move(resultTransitionSystem), std::move(resultLabeling)); - - if (stateRewardFile != "") { - result.stateRewards.reset(storm::parser::SparseStateRewardParser(stateCount, stateRewardFile)); - } - if (transitionRewardFile != "") { - RewardMatrixInformationStruct* rewardMatrixInfo = new RewardMatrixInformationStruct(rowCount, stateCount, &result.transitionSystem.getRowGroupIndices()); - result.transitionRewards.reset(storm::parser::NondeterministicSparseTransitionParser(transitionRewardFile, rewardMatrixInfo).first); - delete rewardMatrixInfo; - } - return result; -} - -/*! - * Uses the Function parseNondeterministicModel internally to parse the given input files. - * @note This is a Short-Hand for Constructing a Mdp directly from the data returned by @parseNondeterministicModel - * @return A Mdp Model - */ -storm::models::Mdp<double> NondeterministicModelParserAsMdp(std::string const & transitionSystemFile, std::string const & labelingFile, - std::string const & stateRewardFile, std::string const & transitionRewardFile) { - NondeterministicModelParserResultContainer<double> parserResult = parseNondeterministicModel(transitionSystemFile, labelingFile, stateRewardFile, transitionRewardFile); - return storm::models::Mdp<double>(std::move(parserResult.transitionSystem), std::move(parserResult.labeling), std::move(parserResult.stateRewards), std::move(parserResult.transitionRewards), boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>>()); -} - -/*! - * Uses the Function parseNondeterministicModel internally to parse the given input files. - * @note This is a Short-Hand for Constructing a Ctmdp directly from the data returned by @parseNondeterministicModel - * @return A Ctmdp Model - */ -storm::models::Ctmdp<double> NondeterministicModelParserAsCtmdp(std::string const & transitionSystemFile, std::string const & labelingFile, - std::string const & stateRewardFile, std::string const & transitionRewardFile) { - NondeterministicModelParserResultContainer<double> parserResult = parseNondeterministicModel(transitionSystemFile, labelingFile, stateRewardFile, transitionRewardFile); - return storm::models::Ctmdp<double>(std::move(parserResult.transitionSystem), std::move(parserResult.labeling), std::move(parserResult.stateRewards), std::move(parserResult.transitionRewards), boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>>()); -} + NondeterministicModelParser::Result NondeterministicModelParser::parseNondeterministicModel(std::string const & transitionsFilename, std::string const & labelingFilename, std::string const & stateRewardFilename, std::string const & transitionRewardFilename) { + + // Parse the transitions. + storm::storage::SparseMatrix<double> transitions(std::move(storm::parser::NondeterministicSparseTransitionParser::parseNondeterministicTransitions(transitionsFilename))); + + uint_fast64_t stateCount = transitions.getColumnCount(); + + // Parse the state labeling. + storm::models::AtomicPropositionsLabeling labeling(std::move(storm::parser::AtomicPropositionLabelingParser::parseAtomicPropositionLabeling(stateCount, labelingFilename))); + + // Only parse state rewards if a file is given. + boost::optional<std::vector<double>> stateRewards; + if (stateRewardFilename != "") { + stateRewards = storm::parser::SparseStateRewardParser::parseSparseStateReward(stateCount, stateRewardFilename); + } + + // Only parse transition rewards if a file is given. + boost::optional<storm::storage::SparseMatrix<double>> transitionRewards; + if (transitionRewardFilename != "") { + transitionRewards = storm::parser::NondeterministicSparseTransitionParser::parseNondeterministicTransitionRewards(transitionRewardFilename, transitions); + } + + // Construct the result. + Result result(std::move(transitions), std::move(labeling)); + result.stateRewards = stateRewards; + result.transitionRewards = transitionRewards; + + return result; + } + + storm::models::Mdp<double> NondeterministicModelParser::parseMdp(std::string const & transitionsFilename, std::string const & labelingFilename, std::string const & stateRewardFilename, std::string const & transitionRewardFilename) { + + Result parserResult = parseNondeterministicModel(transitionsFilename, labelingFilename, stateRewardFilename, transitionRewardFilename); + return storm::models::Mdp<double>(std::move(parserResult.transitionSystem), std::move(parserResult.labeling), std::move(parserResult.stateRewards), std::move(parserResult.transitionRewards), boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>>()); + } + + storm::models::Ctmdp<double> NondeterministicModelParser::parseCtmdp(std::string const & transitionsFilename, std::string const & labelingFilename, std::string const & stateRewardFilename, std::string const & transitionRewardFilename) { -} /* namespace parser */ + Result parserResult = parseNondeterministicModel(transitionsFilename, labelingFilename, stateRewardFilename, transitionRewardFilename); + return storm::models::Ctmdp<double>(std::move(parserResult.transitionSystem), std::move(parserResult.labeling), std::move(parserResult.stateRewards), std::move(parserResult.transitionRewards), boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>>()); + } + } /* namespace parser */ } /* namespace storm */ diff --git a/src/parser/NondeterministicModelParser.h b/src/parser/NondeterministicModelParser.h index 6c49dad0c..5de438fde 100644 --- a/src/parser/NondeterministicModelParser.h +++ b/src/parser/NondeterministicModelParser.h @@ -8,56 +8,123 @@ #ifndef STORM_PARSER_NONDETERMINISTICMODELPARSER_H_ #define STORM_PARSER_NONDETERMINISTICMODELPARSER_H_ -#include "src/parser/Parser.h" #include "src/models/Mdp.h" #include "src/models/Ctmdp.h" namespace storm { + namespace parser { -namespace parser { + /*! + * Loads a nondeterministic model (Mdp or Ctmdp) from files. + * + * Given the file paths of the files holding the transitions, the atomic propositions and optionally the state- and transition rewards + * it loads the files, parses them and returns the desired model. + */ + class NondeterministicModelParser { -/*! - * @brief Load label and transition file and return initialized mdp object - * - * @note This class creates a new Mdp object that can - * be accessed via getMdp(). However, it will not delete this object! - * - * @note The labeling representation in the file may use at most as much nodes as are specified in the mdp. - */ + public: -storm::models::Mdp<double> NondeterministicModelParserAsMdp(std::string const & transitionSystemFile, std::string const & labelingFile, - std::string const & stateRewardFile = "", std::string const & transitionRewardFile = ""); -storm::models::Ctmdp<double> NondeterministicModelParserAsCtmdp(std::string const & transitionSystemFile, std::string const & labelingFile, - std::string const & stateRewardFile = "", std::string const & transitionRewardFile = ""); + /*! + * A structure containing the parsed components of a nondeterministic model. + */ + struct Result { + /*! + * The copy constructor. + * + * @param transitionSystem The transition system to be contained in the Result. + * @param labeling The the labeling of the transition system to be contained in the Result. + */ + Result(storm::storage::SparseMatrix<double>& transitionSystem, storm::models::AtomicPropositionsLabeling& labeling) : transitionSystem(transitionSystem), labeling(labeling) { + // Intentionally left empty. + } -/*! - * @brief This Class acts as a container much like std::pair for the five return values of the NondeterministicModelParser - */ -template <class T> -class NondeterministicModelParserResultContainer { -public: - storm::storage::SparseMatrix<T> transitionSystem; - storm::models::AtomicPropositionsLabeling labeling; - boost::optional<std::vector<T>> stateRewards; - boost::optional<storm::storage::SparseMatrix<T>> transitionRewards; - NondeterministicModelParserResultContainer(storm::storage::SparseMatrix<T>& transitionSystem, storm::models::AtomicPropositionsLabeling& labeling) : transitionSystem(transitionSystem), labeling(labeling) { } - NondeterministicModelParserResultContainer(storm::storage::SparseMatrix<T>&& transitionSystem, storm::models::AtomicPropositionsLabeling&& labeling) : transitionSystem(std::move(transitionSystem)), labeling(std::move(labeling)) { } - - NondeterministicModelParserResultContainer(const NondeterministicModelParserResultContainer & other) : transitionSystem(other.transitionSystem), - labeling(other.labeling), stateRewards(other.stateRewards), transitionRewards(other.transitionRewards) {} - NondeterministicModelParserResultContainer(NondeterministicModelParserResultContainer && other) : transitionSystem(std::move(other.transitionSystem)), - labeling(std::move(other.labeling)), stateRewards(std::move(other.stateRewards)), transitionRewards(std::move(other.transitionRewards)) {} -private: - NondeterministicModelParserResultContainer() {} -}; - - -NondeterministicModelParserResultContainer<double> parseNondeterministicModel(std::string const & transitionSystemFile, std::string const & labelingFile, - std::string const & stateRewardFile = "", std::string const & transitionRewardFile = ""); - -} /* namespace parser */ + /*! + * The move constructor. + * + * @param transitionSystem The transition system to be contained in the Result. + * @param labeling The the labeling of the transition system to be contained in the Result. + */ + Result(storm::storage::SparseMatrix<double>&& transitionSystem, storm::models::AtomicPropositionsLabeling&& labeling) : transitionSystem(std::move(transitionSystem)), labeling(std::move(labeling)) { + // Intentionally left empty. + } + + /*! + * A matrix representing the transitions of the model + */ + storm::storage::SparseMatrix<double> transitionSystem; + + /*! + * The labels of each state. + */ + storm::models::AtomicPropositionsLabeling labeling; + + /*! + * Optional rewards for each state. + */ + boost::optional<std::vector<double>> stateRewards; + + /*! + * Optional rewards for each transition. + */ + boost::optional<storm::storage::SparseMatrix<double>> transitionRewards; + }; + + /*! + * Parse a Mdp. + * + * This method is an adapter to the actual parsing function. + * I.e. it uses @parseNondeterministicModel internally to parse the given input files, takes its result and compiles it into a Dtmc. + * + * @note The number of states of the model is determined by the transitions file. + * The labeling file may therefore not contain labels of states that are not contained in the transitions file. + * + * @param transitionsFilename The path and name of the file containing the transitions of the model. + * @param labelingFilename The path and name of the file containing the labels for the states of the model. + * @param stateRewardFilename The path and name of the file containing the state reward of the model. This file is optional. + * @param transitionRewardFilename The path and name of the file containing the transition rewards of the model. This file is optional. + * @return The parsed Mdp. + */ + static storm::models::Mdp<double> parseMdp(std::string const & transitionsFilename, std::string const & labelingFilename, std::string const & stateRewardFilename = "", std::string const & transitionRewardFilename = ""); + + + /*! + * Parse a Ctmdp. + * + * This method is an adapter to the actual parsing function. + * I.e. it uses @parseNondeterministicModel internally to parse the given input files, takes its result and compiles it into a Dtmc. + * + * @note The number of states of the model is determined by the transitions file. + * The labeling file may therefore not contain labels of states that are not contained in the transitions file. + * + * @param transitionsFilename The path and name of the file containing the transitions of the model. + * @param labelingFilename The path and name of the file containing the labels for the states of the model. + * @param stateRewardFilename The path and name of the file containing the state reward of the model. This file is optional. + * @param transitionRewardFilename The path and name of the file containing the transition rewards of the model. This file is optional. + * @return The parsed Ctmdp. + */ + static storm::models::Ctmdp<double> parseCtmdp(std::string const & transitionsFilename, std::string const & labelingFilename, std::string const & stateRewardFilename = "", std::string const & transitionRewardFilename = ""); + + private: + + /*! + * Parses a nondeterministic model from the given files. + * Calls sub-parsers on the given files and fills the container with the results. + * + * @note The number of states of the model is determined by the transitions file. + * The labeling file may therefore not contain labels of states that are not contained in the transitions file. + * + * @param transitionsFilename The path and name of the file containing the transitions of the model. + * @param labelingFilename The path and name of the file containing the labels for the states of the model. + * @param stateRewardFilename The path and name of the file containing the state reward of the model. This file is optional. + * @param transitionRewardFilename The path and name of the file containing the transition rewards of the model. This file is optional. + * @return The parsed model encapsulated in a Result structure. + */ + static Result parseNondeterministicModel(std::string const & transitionsFilename, std::string const & labelingFilename, std::string const & stateRewardFilename = "", std::string const & transitionRewardFilename = ""); + + }; + } /* namespace parser */ } /* namespace storm */ #endif /* STORM_PARSER_NONDETERMINISTICMODELPARSER_H_ */ diff --git a/src/parser/NondeterministicSparseTransitionParser.cpp b/src/parser/NondeterministicSparseTransitionParser.cpp index cc7d99bfa..32557dbb2 100644 --- a/src/parser/NondeterministicSparseTransitionParser.cpp +++ b/src/parser/NondeterministicSparseTransitionParser.cpp @@ -1,5 +1,5 @@ /*! - * TraParser.cpp + * NondeterministicSparseTransitionParser.cpp * * Created on: 20.11.2012 * Author: Gereon Kremer @@ -7,396 +7,333 @@ #include "src/parser/NondeterministicSparseTransitionParser.h" -#include <errno.h> -#include <time.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <locale.h> - -#include <cstdlib> -#include <cstdio> -#include <cstring> -#include <clocale> -#include <iostream> -#include <utility> #include <string> +#include "src/parser/MappedFile.h" #include "src/settings/Settings.h" #include "src/exceptions/FileIoException.h" +#include "src/exceptions/OutOfRangeException.h" #include "src/exceptions/WrongFormatException.h" -#include <cstdint> + +#include "src/utility/cstring.h" + #include "log4cplus/logger.h" #include "log4cplus/loggingmacros.h" extern log4cplus::Logger logger; namespace storm { -namespace parser { + namespace parser { -/*! - * @brief Perform first pass through the file and obtain overall number of - * choices, number of non-zero cells and maximum node id. - * - * This method does the first pass through the transition file. - * - * It computes the overall number of nondeterministic choices, i.e. the - * number of rows in the matrix that should be created. - * It also calculates the overall number of non-zero cells, i.e. the number - * of elements the matrix has to hold, and the maximum node id, i.e. the - * number of columns of the matrix. - * - * @param buf Data to scan. Is expected to be some char array. - * @param choices Overall number of choices. - * @param maxnode Is set to highest id of all nodes. - * @return The number of non-zero elements. - */ -uint_fast64_t firstPass(char* buf, SupportedLineEndingsEnum lineEndings, uint_fast64_t& choices, int_fast64_t& maxnode, RewardMatrixInformationStruct* rewardMatrixInformation) { - bool isRewardFile = rewardMatrixInformation != nullptr; - - /* - * Check file header and extract number of transitions. - */ - if (!isRewardFile) { - // skip format hint - buf = storm::parser::forwardToNextLine(buf, lineEndings); - } - - /* - * Read all transitions. - */ - int_fast64_t source, target, choice, lastchoice = -1; - int_fast64_t lastsource = -1; - uint_fast64_t nonzero = 0; - double val; - choices = 0; - maxnode = 0; - while (buf[0] != '\0') { - /* - * Read source state and choice. - */ - source = checked_strtol(buf, &buf); - - // Read the name of the nondeterministic choice. - choice = checked_strtol(buf, &buf); - - // Check if we encountered a state index that is bigger than all previously seen. - if (source > maxnode) { - maxnode = source; + using namespace storm::utility::cstring; + + storm::storage::SparseMatrix<double> NondeterministicSparseTransitionParser::parseNondeterministicTransitions(std::string const& filename) { + + storm::storage::SparseMatrix<double> emptyMatrix; + + return NondeterministicSparseTransitionParser::parse(filename, false, emptyMatrix); } - if (isRewardFile) { - // If we have switched the source state, we possibly need to insert the rows of the last - // last source state. - if (source != lastsource && lastsource != -1) { - choices += lastchoice - ((*rewardMatrixInformation->nondeterministicChoiceIndices)[lastsource + 1] - (*rewardMatrixInformation->nondeterministicChoiceIndices)[lastsource] - 1); + storm::storage::SparseMatrix<double> NondeterministicSparseTransitionParser::parseNondeterministicTransitionRewards(std::string const& filename, storm::storage::SparseMatrix<double> const & modelInformation) { + + return NondeterministicSparseTransitionParser::parse(filename, true, modelInformation); + } + + storm::storage::SparseMatrix<double> NondeterministicSparseTransitionParser::parse(std::string const &filename, bool isRewardFile, storm::storage::SparseMatrix<double> const & modelInformation) { + + // Enforce locale where decimal point is '.'. + setlocale(LC_NUMERIC, "C"); + + if (!MappedFile::fileExistsAndIsReadable(filename.c_str())) { + LOG4CPLUS_ERROR(logger, "Error while parsing " << filename << ": File does not exist or is not readable."); + throw storm::exceptions::FileIoException() << "Error while parsing " << filename << ": File does not exist or is not readable."; } - // If we skipped some states, we need to reserve empty rows for all their nondeterministic - // choices. - for (int_fast64_t i = lastsource + 1; i < source; ++i) { - choices += ((*rewardMatrixInformation->nondeterministicChoiceIndices)[i + 1] - (*rewardMatrixInformation->nondeterministicChoiceIndices)[i]); + // Open file. + MappedFile file(filename.c_str()); + char* buf = file.getData(); + + // Perform first pass, i.e. obtain number of columns, rows and non-zero elements. + NondeterministicSparseTransitionParser::FirstPassResult firstPass = NondeterministicSparseTransitionParser::firstPass(file.getData(), isRewardFile, modelInformation); + + // If first pass returned zero, the file format was wrong. + if (firstPass.numberOfNonzeroEntries == 0) { + LOG4CPLUS_ERROR(logger, "Error while parsing " << filename << ": erroneous file format."); + throw storm::exceptions::WrongFormatException() << "Error while parsing " << filename << ": erroneous file format."; } - // If we advanced to the next state, but skipped some choices, we have to reserve rows - // for them - if (source != lastsource) { - choices += choice + 1; - } else if (choice != lastchoice) { - choices += choice - lastchoice; + // Perform second pass. + + // Skip the format hint if it is there. + buf = trimWhitespaces(buf); + if(buf[0] < '0' || buf[0] > '9') { + buf = forwardToLineEnd(buf); + buf = trimWhitespaces(buf); } - } else { - // If we have skipped some states, we need to reserve the space for the self-loop insertion - // in the second pass. - if (source > lastsource + 1) { - nonzero += source - lastsource - 1; - choices += source - lastsource - 1; - } else if (source != lastsource || choice != lastchoice) { - // If we have switched the source state or the nondeterministic choice, we need to - // reserve one row more. - ++choices; + + if (isRewardFile) { + // The reward matrix should match the size of the transition matrix. + if (firstPass.choices > modelInformation.getRowCount() || (uint_fast64_t)(firstPass.highestStateIndex + 1) > modelInformation.getColumnCount()) { + LOG4CPLUS_ERROR(logger, "Reward matrix size exceeds transition matrix size."); + throw storm::exceptions::OutOfRangeException() << "Reward matrix size exceeds transition matrix size."; + } else if (firstPass.choices != modelInformation.getRowCount()) { + LOG4CPLUS_ERROR(logger, "Reward matrix row count does not match transition matrix row count."); + throw storm::exceptions::OutOfRangeException() << "Reward matrix row count does not match transition matrix row count."; + } else if(firstPass.numberOfNonzeroEntries > modelInformation.getEntryCount()) { + LOG4CPLUS_ERROR(logger, "The reward matrix has more entries than the transition matrix. There must be a reward for a non existent transition"); + throw storm::exceptions::OutOfRangeException() << "The reward matrix has more entries than the transition matrix."; + } else { + firstPass.highestStateIndex = modelInformation.getColumnCount() - 1; + } } - } - // Read target and check if we encountered a state index that is bigger than all previously - // seen. - target = checked_strtol(buf, &buf); - if (target > maxnode) { - maxnode = target; - } + // Create the matrix builder. + // The matrix to be build should have as many columns as we have nodes and as many rows as we have choices. + // Those two values, as well as the number of nonzero elements, was been calculated in the first run. + LOG4CPLUS_INFO(logger, "Attempting to create matrix of size " << firstPass.choices << " x " << (firstPass.highestStateIndex+1) << " with " << firstPass.numberOfNonzeroEntries << " entries."); + storm::storage::SparseMatrixBuilder<double> matrixBuilder; + if(!isRewardFile) { + matrixBuilder = storm::storage::SparseMatrixBuilder<double>(firstPass.choices, firstPass.highestStateIndex + 1, firstPass.numberOfNonzeroEntries, true, firstPass.highestStateIndex + 1); + } else { + matrixBuilder = storm::storage::SparseMatrixBuilder<double>(firstPass.choices, firstPass.highestStateIndex + 1, firstPass.numberOfNonzeroEntries, true, modelInformation.getRowGroupCount()); + } - // Read value and check whether it's positive. - val = checked_strtod(buf, &buf); - if ((val < 0.0) || (val > 1.0)) { - LOG4CPLUS_ERROR(logger, "Expected a positive probability but got \"" << std::string(buf, 0, 16) << "\"."); - return 0; - } + // Initialize variables for the parsing run. + uint_fast64_t source = 0, target = 0, lastSource = 0, choice = 0, lastChoice = 0, curRow = 0; + double val = 0.0; + bool fixDeadlocks = storm::settings::Settings::getInstance()->isSet("fixDeadlocks"); + bool hadDeadlocks = false; + + // The first state already starts a new row group of the matrix. + matrixBuilder.newRowGroup(0); + + // Read all transitions from file. + while (buf[0] != '\0') { + + // Read source state and choice. + source = checked_strtol(buf, &buf); + choice = checked_strtol(buf, &buf); + + if (isRewardFile) { + // If we have switched the source state, we possibly need to insert the rows of the last + // source state. + if (source != lastSource) { + curRow += ((modelInformation.getRowGroupIndices())[lastSource + 1] - (modelInformation.getRowGroupIndices())[lastSource]) -(lastChoice + 1); + } + + // If we skipped some states, we need to reserve empty rows for all their nondeterministic + // choices. + for (uint_fast64_t i = lastSource + 1; i < source; ++i) { + curRow += ((modelInformation.getRowGroupIndices())[i + 1] - (modelInformation.getRowGroupIndices())[i]); + } + + // If we advanced to the next state, but skipped some choices, we have to reserve rows + // for them + if (source != lastSource) { + curRow += choice + 1; + } else if (choice != lastChoice) { + curRow += choice - lastChoice; + } + } else { + // Increase line count if we have either finished reading the transitions of a certain state + // or we have finished reading one nondeterministic choice of a state. + if ((source != lastSource || choice != lastChoice)) { + ++curRow; + } + + // Check if we have skipped any source node, i.e. if any node has no + // outgoing transitions. If so, insert a self-loop. + // Also begin a new rowGroup for the skipped state. + for (uint_fast64_t node = lastSource + 1; node < source; node++) { + hadDeadlocks = true; + if (fixDeadlocks) { + matrixBuilder.newRowGroup(curRow); + matrixBuilder.addNextValue(curRow, node, 1); + ++curRow; + LOG4CPLUS_WARN(logger, "Warning while parsing " << filename << ": node " << node << " has no outgoing transitions. A self-loop was inserted."); + } else { + LOG4CPLUS_ERROR(logger, "Error while parsing " << filename << ": node " << node << " has no outgoing transitions."); + } + } + if (source != lastSource) { + // Add create a new rowGroup for the source, if this is the first choice we encounter for this state. + matrixBuilder.newRowGroup(curRow); + } + } - lastchoice = choice; - lastsource = source; - - /* - * Increase number of non-zero values. - */ - nonzero++; - - // The PRISM output format lists the name of the transition in the fourth column, - // but omits the fourth column if it is an internal action. In either case, however, the third column - // is followed by a space. We need to skip over that space first (instead of trimming whitespaces), - // before we can skip to the line end, because trimming the white spaces will proceed to the next line - // in case there is no action label in the fourth column. - if (buf[0] == ' ') { - ++buf; - } + // Read target and value and write it to the matrix. + target = checked_strtol(buf, &buf); + val = checked_strtod(buf, &buf); + matrixBuilder.addNextValue(curRow, target, val); - /* - * Proceed to beginning of next line. - */ - switch (lineEndings) { - case SupportedLineEndingsEnum::SlashN: - buf += strcspn(buf, " \t\n"); - break; - case SupportedLineEndingsEnum::SlashR: - buf += strcspn(buf, " \t\r"); - break; - case SupportedLineEndingsEnum::SlashRN: - buf += strcspn(buf, " \t\r\n"); - break; - default: - case storm::parser::SupportedLineEndingsEnum::Unsupported: - // This Line will never be reached as the Parser would have thrown already. - throw; - break; - } - buf = trimWhitespaces(buf); - } + lastSource = source; + lastChoice = choice; - if (isRewardFile) { - // If not all rows were filled for the last state, we need to insert them. - choices += lastchoice - ((*rewardMatrixInformation->nondeterministicChoiceIndices)[lastsource + 1] - (*rewardMatrixInformation->nondeterministicChoiceIndices)[lastsource] - 1); + // Proceed to beginning of next line in file and next row in matrix. + buf = forwardToLineEnd(buf); - // If we skipped some states, we need to reserve empty rows for all their nondeterministic - // choices. - for (uint_fast64_t i = lastsource + 1; i < rewardMatrixInformation->nondeterministicChoiceIndices->size() - 1; ++i) { - choices += ((*rewardMatrixInformation->nondeterministicChoiceIndices)[i + 1] - (*rewardMatrixInformation->nondeterministicChoiceIndices)[i]); - } - } + buf = trimWhitespaces(buf); + } - return nonzero; -} + if (!fixDeadlocks && hadDeadlocks && !isRewardFile) throw storm::exceptions::WrongFormatException() << "Some of the nodes had deadlocks. You can use --fixDeadlocks to insert self-loops on the fly."; + // Since we assume the transition rewards are for the transitions of the model, we copy the rowGroupIndices. + if(isRewardFile) { + // We already have rowGroup 0. + for(uint_fast64_t index = 1; index < modelInformation.getRowGroupIndices().size(); index++) { + matrixBuilder.newRowGroup(modelInformation.getRowGroupIndices()[index]); + } + } else { + for (uint_fast64_t node = lastSource + 1; node <= firstPass.highestStateIndex; node++) { + matrixBuilder.newRowGroup(curRow + 1); + } + } + // Finally, build the actual matrix, test and return it. + storm::storage::SparseMatrix<double> resultMatrix = matrixBuilder.build(); -/*! - * Reads a .tra file and produces a sparse matrix representing the described Markov Chain. - * - * Matrices created with this method have to be freed with the delete operator. - * @param filename input .tra file's name. - * @return a pointer to the created sparse matrix. - */ + // Since we cannot do the testing if each transition for which there is a reward in the reward file also exists in the transition matrix during parsing, we have to do it afterwards. + if(isRewardFile && !resultMatrix.isSubmatrixOf(modelInformation)) { + LOG4CPLUS_ERROR(logger, "There are rewards for non existent transitions given in the reward file."); + throw storm::exceptions::WrongFormatException() << "There are rewards for non existent transitions given in the reward file."; + } -NondeterministicSparseTransitionParserResult_t NondeterministicSparseTransitionParser(std::string const &filename, RewardMatrixInformationStruct* rewardMatrixInformation) { - /* - * Enforce locale where decimal point is '.'. - */ - setlocale(LC_NUMERIC, "C"); - - if (!fileExistsAndIsReadable(filename.c_str())) { - LOG4CPLUS_ERROR(logger, "Error while parsing " << filename << ": File does not exist or is not readable."); - throw storm::exceptions::WrongFormatException(); - } - - bool isRewardFile = rewardMatrixInformation != nullptr; - - /* - * Find out about the used line endings. - */ - SupportedLineEndingsEnum lineEndings = findUsedLineEndings(filename, true); - - /* - * Open file. - */ - MappedFile file(filename.c_str()); - char* buf = file.data; - - /* - * Perform first pass, i.e. obtain number of columns, rows and non-zero elements. - */ - int_fast64_t maxnode; - uint_fast64_t choices; - uint_fast64_t nonzero = firstPass(file.data, lineEndings, choices, maxnode, rewardMatrixInformation); - - /* - * If first pass returned zero, the file format was wrong. - */ - if (nonzero == 0) { - LOG4CPLUS_ERROR(logger, "Error while parsing " << filename << ": erroneous file format."); - throw storm::exceptions::WrongFormatException(); - } - - /* - * Perform second pass. - * - * From here on, we already know that the file header is correct. - */ - - /* - * Skip file header. - */ - if (!isRewardFile) { - // skip format hint - buf = storm::parser::forwardToNextLine(buf, lineEndings); - } - - if (isRewardFile) { - if (choices > rewardMatrixInformation->rowCount || (uint_fast64_t)(maxnode + 1) > rewardMatrixInformation->columnCount) { - LOG4CPLUS_ERROR(logger, "Reward matrix size exceeds transition matrix size."); - throw storm::exceptions::WrongFormatException() << "Reward matrix size exceeds transition matrix size."; - } else if (choices != rewardMatrixInformation->rowCount) { - LOG4CPLUS_ERROR(logger, "Reward matrix row count does not match transition matrix row count."); - throw storm::exceptions::WrongFormatException() << "Reward matrix row count does not match transition matrix row count."; - } else { - maxnode = rewardMatrixInformation->columnCount - 1; + return resultMatrix; } - } - - /* - * Create and initialize matrix. - * The matrix should have as many columns as we have nodes and as many rows as we have choices. - * Those two values, as well as the number of nonzero elements, was been calculated in the first run. - */ - LOG4CPLUS_INFO(logger, "Attempting to create matrix of size " << choices << " x " << (maxnode+1) << " with " << nonzero << " entries."); - storm::storage::SparseMatrixBuilder<double> matrixBuilder(choices, maxnode + 1, nonzero, true); - - /* - * Create row mapping. - */ - std::vector<uint_fast64_t> rowMapping(maxnode + 2, 0); - - /* - * Parse file content. - */ - int_fast64_t source, target, lastsource = -1, choice, lastchoice = -1; - int_fast64_t curRow = -1; - double val; - bool fixDeadlocks = storm::settings::Settings::getInstance()->isSet("fixDeadlocks"); - bool hadDeadlocks = false; - - /* - * Read all transitions from file. - */ - while (buf[0] != '\0') { - /* - * Read source state and choice. - */ - source = checked_strtol(buf, &buf); - choice = checked_strtol(buf, &buf); - - if (isRewardFile) { - // If we have switched the source state, we possibly need to insert the rows of the last - // last source state. - if (source != lastsource && lastsource != -1) { - curRow += lastchoice - ((*rewardMatrixInformation->nondeterministicChoiceIndices)[lastsource + 1] - (*rewardMatrixInformation->nondeterministicChoiceIndices)[lastsource] - 1); - } - // If we skipped some states, we need to reserve empty rows for all their nondeterministic - // choices. - for (int_fast64_t i = lastsource + 1; i < source; ++i) { - matrixBuilder.newRowGroup(curRow + 1); - curRow += ((*rewardMatrixInformation->nondeterministicChoiceIndices)[i + 1] - (*rewardMatrixInformation->nondeterministicChoiceIndices)[i]); - } + NondeterministicSparseTransitionParser::FirstPassResult NondeterministicSparseTransitionParser::firstPass(char* buf, bool isRewardFile, storm::storage::SparseMatrix<double> const & modelInformation) { - // If we advanced to the next state, but skipped some choices, we have to reserve rows - // for them - if (source != lastsource) { - matrixBuilder.newRowGroup(curRow + 1); - curRow += choice + 1; - } else if (choice != lastchoice) { - curRow += choice - lastchoice; - } - } else { - // Increase line count if we have either finished reading the transitions of a certain state - // or we have finished reading one nondeterministic choice of a state. - if ((source != lastsource || choice != lastchoice)) { - ++curRow; + // Check file header and extract number of transitions. + + // Skip the format hint if it is there. + buf = trimWhitespaces(buf); + if(buf[0] < '0' || buf[0] > '9') { + buf = forwardToLineEnd(buf); + buf = trimWhitespaces(buf); } - /* - * Check if we have skipped any source node, i.e. if any node has no - * outgoing transitions. If so, insert a self-loop. - * Also add self-loops to rowMapping. - */ - for (int_fast64_t node = lastsource + 1; node < source; node++) { - hadDeadlocks = true; - if (fixDeadlocks) { - rowMapping.at(node) = curRow; - matrixBuilder.newRowGroup(curRow); - matrixBuilder.addNextValue(curRow, node, 1); - ++curRow; - LOG4CPLUS_WARN(logger, "Warning while parsing " << filename << ": node " << node << " has no outgoing transitions. A self-loop was inserted."); + + // Read all transitions. + uint_fast64_t source = 0, target = 0, choice = 0, lastChoice = 0, lastSource = 0, lastTarget = -1; + double val = 0.0; + NondeterministicSparseTransitionParser::FirstPassResult result; + + // Since the first line is already a new choice but is not covered below, that has to be covered here. + result.choices = 1; + + while (buf[0] != '\0') { + + // Read source state and choice. + source = checked_strtol(buf, &buf); + + // Read the name of the nondeterministic choice. + choice = checked_strtol(buf, &buf); + + if (source < lastSource) { + LOG4CPLUS_ERROR(logger, "The current source state " << source << " is smaller than the last one " << lastSource << "."); + throw storm::exceptions::InvalidArgumentException() << "The current source state " << source << " is smaller than the last one " << lastSource << "."; + } + + // Check if we encountered a state index that is bigger than all previously seen. + if (source > result.highestStateIndex) { + result.highestStateIndex = source; + } + + if (isRewardFile) { + + // Make sure that the highest state index of the reward file is not higher than the highest state index of the corresponding model. + if(result.highestStateIndex > modelInformation.getColumnCount() - 1) { + LOG4CPLUS_ERROR(logger, "State index " << result.highestStateIndex << " found. This exceeds the highest state index of the model, which is " << modelInformation.getColumnCount() - 1 << " ."); + throw storm::exceptions::OutOfRangeException() << "State index " << result.highestStateIndex << " found. This exceeds the highest state index of the model, which is " << modelInformation.getColumnCount() - 1 << " ."; + } + + // If we have switched the source state, we possibly need to insert rows for skipped choices of the last + // source state. + if (source != lastSource) { + // number of choices skipped = number of choices of last state - number of choices read + result.choices += ((modelInformation.getRowGroupIndices())[lastSource + 1] - (modelInformation.getRowGroupIndices())[lastSource]) - (lastChoice + 1); + } + + // If we skipped some states, we need to reserve empty rows for all their nondeterministic + // choices. + for (uint_fast64_t i = lastSource + 1; i < source; ++i) { + result.choices += ((modelInformation.getRowGroupIndices())[i + 1] - (modelInformation.getRowGroupIndices())[i]); + } + + // If we advanced to the next state, but skipped some choices, we have to reserve rows + // for them. + if (source != lastSource) { + result.choices += choice + 1; + } else if (choice != lastChoice) { + result.choices += choice - lastChoice; + } } else { - LOG4CPLUS_ERROR(logger, "Error while parsing " << filename << ": node " << node << " has no outgoing transitions."); + + // If we have skipped some states, we need to reserve the space for the self-loop insertion + // in the second pass. + if (source > lastSource + 1) { + result.numberOfNonzeroEntries += source - lastSource - 1; + result.choices += source - lastSource - 1; + } + + if (source != lastSource || choice != lastChoice) { + // If we have switched the source state or the nondeterministic choice, we need to + // reserve one row more. + ++result.choices; + } } + + // Read target and check if we encountered a state index that is bigger than all previously seen. + target = checked_strtol(buf, &buf); + + if (target > result.highestStateIndex) { + result.highestStateIndex = target; + } + + // Also, have we already seen this transition? + if (target == lastTarget && choice == lastChoice && source == lastSource) { + LOG4CPLUS_ERROR(logger, "The same transition (" << source << ", " << choice << ", " << target << ") is given twice."); + throw storm::exceptions::InvalidArgumentException() << "The same transition (" << source << ", " << choice << ", " << target << ") is given twice."; + } + + // Read value and check whether it's positive. + val = checked_strtod(buf, &buf); + if (!isRewardFile && (val < 0.0 || val > 1.0 )) { + LOG4CPLUS_ERROR(logger, "Expected a positive probability but got \"" << std::string(buf, 0, 16) << "\"."); + NondeterministicSparseTransitionParser::FirstPassResult nullResult; + return nullResult; + } + else if (val < 0.0) { + LOG4CPLUS_ERROR(logger, "Expected a positive reward value but got \"" << std::string(buf, 0, 16) << "\"."); + NondeterministicSparseTransitionParser::FirstPassResult nullResult; + return nullResult; + } + + lastChoice = choice; + lastSource = source; + lastTarget = target; + + // Increase number of non-zero values. + result.numberOfNonzeroEntries++; + + // The PRISM output format lists the name of the transition in the fourth column, + // but omits the fourth column if it is an internal action. In either case we can skip to the end of the line. + buf = forwardToLineEnd(buf); + + buf = trimWhitespaces(buf); } - if (source != lastsource) { - /* - * Add this source to rowMapping, if this is the first choice we encounter for this state. - */ - rowMapping.at(source) = curRow; - matrixBuilder.newRowGroup(curRow); - } - } - // Read target and value and write it to the matrix. - target = checked_strtol(buf, &buf); - val = checked_strtod(buf, &buf); - matrixBuilder.addNextValue(curRow, target, val); + if (isRewardFile) { + // If not all rows were filled for the last state, we need to insert them. + result.choices += ((modelInformation.getRowGroupIndices())[lastSource + 1] - (modelInformation.getRowGroupIndices())[lastSource] ) - (lastChoice + 1); - lastsource = source; - lastchoice = choice; + // If we skipped some states, we need to reserve empty rows for all their nondeterministic + // choices. + for (uint_fast64_t i = lastSource + 1; i < modelInformation.getRowGroupIndices().size() - 1; ++i) { + result.choices += ((modelInformation.getRowGroupIndices())[i + 1] - (modelInformation.getRowGroupIndices())[i]); + } + } - /* - * Proceed to beginning of next line in file and next row in matrix. - */ - if (buf[0] == ' ') { - ++buf; + return result; } - switch (lineEndings) { - case SupportedLineEndingsEnum::SlashN: - buf += strcspn(buf, " \t\n"); - break; - case SupportedLineEndingsEnum::SlashR: - buf += strcspn(buf, " \t\r"); - break; - case SupportedLineEndingsEnum::SlashRN: - buf += strcspn(buf, " \t\r\n"); - break; - default: - case storm::parser::SupportedLineEndingsEnum::Unsupported: - // This Line will never be reached as the Parser would have thrown already. - throw; - break; - } - buf = trimWhitespaces(buf); - } - - if (isRewardFile) { - for (int_fast64_t node = lastsource + 1; node <= maxnode + 1; ++node) { - rowMapping.at(node) = curRow + 1; - if (node <= maxnode) { - matrixBuilder.newRowGroup((*rewardMatrixInformation->nondeterministicChoiceIndices)[node]); - } - } - } else { - for (int_fast64_t node = lastsource + 1; node <= maxnode + 1; ++node) { - rowMapping.at(node) = curRow + 1; - if (node <= maxnode) { - matrixBuilder.newRowGroup(curRow + 1); - } - } - } - - if (!fixDeadlocks && hadDeadlocks && !isRewardFile) throw storm::exceptions::WrongFormatException() << "Some of the nodes had deadlocks. You can use --fixDeadlocks to insert self-loops on the fly."; - - return std::make_pair(matrixBuilder.build(), std::move(rowMapping)); -} - -} // namespace parser + + } // namespace parser } // namespace storm diff --git a/src/parser/NondeterministicSparseTransitionParser.h b/src/parser/NondeterministicSparseTransitionParser.h index 900bc088e..f75babbb7 100644 --- a/src/parser/NondeterministicSparseTransitionParser.h +++ b/src/parser/NondeterministicSparseTransitionParser.h @@ -1,30 +1,96 @@ -#ifndef STORM_PARSER_NONDETTRAPARSER_H_ -#define STORM_PARSER_NONDETTRAPARSER_H_ +#ifndef STORM_PARSER_NONDETERMINISTICSPARSETRANSITIONPARSER_H_ +#define STORM_PARSER_NONDETERMINISTICSPARSETRANSITIONPARSER_H_ #include "src/storage/SparseMatrix.h" -#include "src/parser/Parser.h" -#include "src/utility/OsDetection.h" - -#include <utility> -#include <memory> #include <vector> namespace storm { -namespace parser { + namespace parser { + + /*! + * A class providing the functionality to parse the transitions of a nondeterministic model. + * + * The file is parsed in two passes. + * The first pass tests the file format and collects statistical data needed for the second pass. + * The second pass then collects the actual file data and compiles it into a Result. + */ + class NondeterministicSparseTransitionParser { + + public: + + /*! + * A structure representing the result of the first pass of this parser. + * It contains the number of non-zero entries in the model, the highest state index and the total number if nondeterministic choices. + */ + struct FirstPassResult { + + /*! + * The default constructor. + * Constructs an empty FirstPassResult. + */ + FirstPassResult() : numberOfNonzeroEntries(0), highestStateIndex(0), choices(0) { + // Intentionally left empty. + } + + //! The total number of non-zero entries of the model. + uint_fast64_t numberOfNonzeroEntries; + + //! The highest state index that appears in the model. + uint_fast64_t highestStateIndex; + + //! The total number of nondeterministic choices within the transition system. + uint_fast64_t choices; + }; + + /*! + * Load a nondeterministic transition system from file and create a sparse adjacency matrix whose entries represent the weights of the edges + * + * @param filename The path and name of file to be parsed. + */ + static storm::storage::SparseMatrix<double> parseNondeterministicTransitions(std::string const & filename); + + /*! + * Load a nondeterministic transition system from file and create a sparse adjacency matrix whose entries represent the weights of the edges + * + * @param filename The path and name of file to be parsed. + * @param modelInformation The information about the transition structure of nondeterministic model in which the transition rewards shall be used. + * @return A struct containing the parsed file contents, i.e. the transition reward matrix and the mapping between its rows and the states of the model. + */ + static storm::storage::SparseMatrix<double> parseNondeterministicTransitionRewards(std::string const & filename, storm::storage::SparseMatrix<double> const & modelInformation); + + private: + + /*! + * This method does the first pass through the buffer containing the content of some transition file. + * + * It computes the overall number of nondeterministic choices, i.e. the + * number of rows in the matrix that should be created. + * It also calculates the overall number of non-zero cells, i.e. the number + * of elements the matrix has to hold, and the maximum node id, i.e. the + * number of columns of the matrix. + * + * @param buffer Buffer containing the data to scan. This is expected to be some char array. + * @param insertDiagonalEntriesIfMissing A flag set iff entries on the primary diagonal of the matrix should be added in case they are missing in the parsed file. + * @return A structure representing the result of the first pass. + */ + static FirstPassResult firstPass(char* buffer, bool isRewardFile, storm::storage::SparseMatrix<double> const & modelInformation); + + /*! + * The main parsing routine. + * Opens the given file, calls the first pass and performs the second pass, parsing the content of the file into a SparseMatrix. + * + * @param filename The path and name of file to be parsed. + * @param rewardFile A flag set iff the file to be parsed contains transition rewards. + * @param insertDiagonalEntriesIfMissing A flag set iff entries on the primary diagonal of the matrix should be added in case they are missing in the parsed file. + * @param modelInformation A struct containing information that is used to check if the transition reward matrix fits to the rest of the model. + * @return A SparseMatrix containing the parsed file contents. + */ + static storm::storage::SparseMatrix<double> parse(std::string const& filename, bool isRewardFile, storm::storage::SparseMatrix<double> const & modelInformation); + + }; -/*! - * @brief Contains the Result of a call to the NondeterministicSparseTransitionParser function. The first part is the resulting matrix. The second part is the row mapping. - */ -typedef std::pair<storm::storage::SparseMatrix<double>, std::vector<uint_fast64_t>> NondeterministicSparseTransitionParserResult_t; - -/*! - * @brief Load a nondeterministic transition system from file and create a - * sparse adjacency matrix whose entries represent the weights of the edges - */ -NondeterministicSparseTransitionParserResult_t NondeterministicSparseTransitionParser(std::string const &filename, RewardMatrixInformationStruct* rewardMatrixInformation = nullptr); - -} // namespace parser + } // namespace parser } // namespace storm -#endif /* STORM_PARSER_NONDETTRAPARSER_H_ */ +#endif /* STORM_PARSER_NONDETERMINISTICSPARSETRANSITIONPARSER_H__H_ */ diff --git a/src/parser/Parser.cpp b/src/parser/Parser.cpp deleted file mode 100644 index 5ed957c17..000000000 --- a/src/parser/Parser.cpp +++ /dev/null @@ -1,289 +0,0 @@ -#include "src/parser/Parser.h" - -#include <iostream> -#include <cstring> -#include <string> -#include <cerrno> - -#include "src/exceptions/FileIoException.h" -#include "src/exceptions/WrongFormatException.h" -#include "src/utility/OsDetection.h" - -#include "log4cplus/logger.h" -#include "log4cplus/loggingmacros.h" -extern log4cplus::Logger logger; - -/*! - * Calls strtol() internally and checks if the new pointer is different - * from the original one, i.e. if str != *end. If they are the same, a - * storm::exceptions::WrongFormatException will be thrown. - * @param str String to parse - * @param end New pointer will be written there - * @return Result of strtol() - */ -uint_fast64_t storm::parser::checked_strtol(const char* str, char** end) { - uint_fast64_t res = strtol(str, end, 10); - if (str == *end) { - LOG4CPLUS_ERROR(logger, "Error while parsing integer. Next input token is not a number."); - LOG4CPLUS_ERROR(logger, "\tUpcoming input is: \"" << std::string(str, 0, 16) << "\""); - throw storm::exceptions::WrongFormatException("Error while parsing integer. Next input token is not a number."); - } - return res; -} - -/*! - * Calls strtod() internally and checks if the new pointer is different - * from the original one, i.e. if str != *end. If they are the same, a - * storm::exceptions::WrongFormatException will be thrown. - * @param str String to parse - * @param end New pointer will be written there - * @return Result of strtod() - */ -double storm::parser::checked_strtod(const char* str, char** end) { - double res = strtod(str, end); - if (str == *end) { - LOG4CPLUS_ERROR(logger, "Error while parsing floating point. Next input token is not a number."); - LOG4CPLUS_ERROR(logger, "\tUpcoming input is: \"" << std::string(str, 0, 16) << "\""); - throw storm::exceptions::WrongFormatException("Error while parsing floating point. Next input token is not a number."); - } - return res; -} - -/*! - * @brief Tests whether the given file exists and is readable. - * @return True iff the file exists and is readable. - */ -bool storm::parser::fileExistsAndIsReadable(const char* fileName) { - std::ifstream fin(fileName); - bool returnValue = !fin.fail(); - return returnValue; -} - -/*! - * Skips spaces, tabs, newlines and carriage returns. Returns pointer - * to first char that is not a whitespace. - * @param buf String buffer - * @return pointer to first non-whitespace character - */ -char* storm::parser::trimWhitespaces(char* buf) { - while ((*buf == ' ') || (*buf == '\t') || (*buf == '\n') || (*buf == '\r')) buf++; - return buf; -} - -/*! - * @briefs Analyzes the given file and tries to find out the used file endings. - */ -storm::parser::SupportedLineEndingsEnum storm::parser::findUsedLineEndings(std::string const& fileName, bool throwOnUnsupported) { - MappedFile fileMap(fileName.c_str()); - char* buf = nullptr; - char* const bufferEnd = fileMap.dataend; - - bool sawR = false; - for (buf = fileMap.data; buf != bufferEnd; ++buf) { - if (*buf == '\r') { - // check for following \n - if (((buf + sizeof(char)) < bufferEnd) && (*(buf + sizeof(char)) == '\n')) { - return storm::parser::SupportedLineEndingsEnum::SlashRN; - } - return storm::parser::SupportedLineEndingsEnum::SlashR; - } else if (*buf == '\n') { - return storm::parser::SupportedLineEndingsEnum::SlashN; - } - } - - if (throwOnUnsupported) { - LOG4CPLUS_ERROR(logger, "Error while parsing \"" << fileName << "\": Unsupported or unknown line-endings. Please use either of \\r, \\n or \\r\\n"); - throw storm::exceptions::WrongFormatException() << "Error while parsing \"" << fileName << "\": Unsupported or unknown line-endings. Please use either of \\r, \\n or \\r\\n"; - } - LOG4CPLUS_WARN(logger, "Error while parsing \"" << fileName << "\": Unsupported or unknown line-endings. Please use either of \\r, \\n or \\r\\n"); - - return storm::parser::SupportedLineEndingsEnum::Unsupported; -} - -/*! - * @brief Encapsulates the usage of function @strchr to forward to the next line - */ -char* storm::parser::forwardToNextLine(char* buffer, storm::parser::SupportedLineEndingsEnum lineEndings) { - switch (lineEndings) { - case storm::parser::SupportedLineEndingsEnum::SlashN: - return strchr(buffer, '\n') + 1; - break; - case storm::parser::SupportedLineEndingsEnum::SlashR: - return strchr(buffer, '\r') + 1; - break; - case storm::parser::SupportedLineEndingsEnum::SlashRN: - return strchr(buffer, '\r') + 2; - break; - default: - case storm::parser::SupportedLineEndingsEnum::Unsupported: - // This Line will never be reached as the Parser would have thrown already. - throw; - break; - } - return nullptr; -} - -/*! - * @brief Encapsulates the usage of function @sscanf to scan for the model type hint - * @param targetBuffer The Target for the hint, must be at least 64 bytes long - * @param buffer The Source Buffer from which the Model Hint will be read - */ -void storm::parser::scanForModelHint(char* targetBuffer, uint_fast64_t targetBufferSize, char const* buffer, storm::parser::SupportedLineEndingsEnum lineEndings) { - if (targetBufferSize <= 4) { - throw; - } - switch (lineEndings) { - case storm::parser::SupportedLineEndingsEnum::SlashN: -#ifdef WINDOWS - sscanf_s(buffer, "%60s\n", targetBuffer, targetBufferSize); -#else - sscanf(buffer, "%60s\n", targetBuffer); -#endif - break; - case storm::parser::SupportedLineEndingsEnum::SlashR: -#ifdef WINDOWS - sscanf_s(buffer, "%60s\r", targetBuffer, sizeof(targetBufferSize)); -#else - sscanf(buffer, "%60s\r", targetBuffer); -#endif - break; - case storm::parser::SupportedLineEndingsEnum::SlashRN: -#ifdef WINDOWS - sscanf_s(buffer, "%60s\r\n", targetBuffer, sizeof(targetBufferSize)); -#else - sscanf(buffer, "%60s\r\n", targetBuffer); -#endif - break; - default: - case storm::parser::SupportedLineEndingsEnum::Unsupported: - // This Line will never be reached as the Parser would have thrown already. - throw; - break; - } -} - -/*! - * @brief Returns the matching Separator-String in the format of "BLANK\t\NEWLINESYMBOL(S)\0 - */ -void storm::parser::getMatchingSeparatorString(char* targetBuffer, uint_fast64_t targetBufferSize, storm::parser::SupportedLineEndingsEnum lineEndings) { - if (targetBufferSize < 5) { - LOG4CPLUS_ERROR(logger, "storm::parser::getMatchingSeparatorString: The passed Target Buffer is too small."); - throw; - } - switch (lineEndings) { - case SupportedLineEndingsEnum::SlashN: { - char source[] = " \n\t"; -#ifdef WINDOWS - strncpy(targetBuffer, targetBufferSize, source, sizeof(source)); -#else - strncpy(targetBuffer, source, targetBufferSize); -#endif - break; - } - case SupportedLineEndingsEnum::SlashR: { - char source[] = " \r\t"; -#ifdef WINDOWS - strncpy(targetBuffer, targetBufferSize, source, sizeof(source)); -#else - strncpy(targetBuffer, source, targetBufferSize); -#endif - break; - } - case SupportedLineEndingsEnum::SlashRN: { - char source[] = " \r\n\t"; -#ifdef WINDOWS - strncpy(targetBuffer, targetBufferSize, source, sizeof(source)); -#else - strncpy(targetBuffer, source, targetBufferSize); -#endif - break; - } - default: - case SupportedLineEndingsEnum::Unsupported: - // This Line will never be reached as the Parser would have thrown already. - LOG4CPLUS_ERROR(logger, "storm::parser::getMatchingSeparatorString: The passed lineEndings were Unsupported. Check your input file."); - throw; - break; - } -} - -/*! - * Will stat the given file, open it and map it to memory. - * If anything of this fails, an appropriate exception is raised - * and a log entry is written. - * @param filename file to be opened - */ -storm::parser::MappedFile::MappedFile(const char* filename) { -#if defined LINUX || defined MACOSX - /* - * Do file mapping for reasonable systems. - * stat64(), open(), mmap() - */ -#ifdef MACOSX - if (stat(filename, &(this->st)) != 0) { -#else - if (stat64(filename, &(this->st)) != 0) { -#endif - LOG4CPLUS_ERROR(logger, "Error in stat(" << filename << "): Probably, this file does not exist."); - throw exceptions::FileIoException() << "storm::parser::MappedFile Error in stat(): Probably, this file does not exist."; - } - this->file = open(filename, O_RDONLY); - - if (this->file < 0) { - LOG4CPLUS_ERROR(logger, "Error in open(" << filename << "): Probably, we may not read this file."); - throw exceptions::FileIoException() << "storm::parser::MappedFile Error in open(): Probably, we may not read this file."; - } - - this->data = reinterpret_cast<char*>(mmap(NULL, this->st.st_size, PROT_READ, MAP_PRIVATE, this->file, 0)); - if (this->data == reinterpret_cast<char*>(-1)) { - close(this->file); - LOG4CPLUS_ERROR(logger, "Error in mmap(" << filename << "): " << std::strerror(errno)); - throw exceptions::FileIoException() << "storm::parser::MappedFile Error in mmap(): " << std::strerror(errno); - } - this->dataend = this->data + this->st.st_size; -#elif defined WINDOWS - /* - * Do file mapping for windows. - * _stat64(), CreateFile(), CreateFileMapping(), MapViewOfFile() - */ - if (_stat64(filename, &(this->st)) != 0) { - LOG4CPLUS_ERROR(logger, "Error in _stat(" << filename << "): Probably, this file does not exist."); - throw exceptions::FileIoException("storm::parser::MappedFile Error in stat(): Probably, this file does not exist."); - } - - this->file = CreateFileA(filename, GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (this->file == INVALID_HANDLE_VALUE) { - LOG4CPLUS_ERROR(logger, "Error in CreateFileA(" << filename << "): Probably, we may not read this file."); - throw exceptions::FileIoException("storm::parser::MappedFile Error in CreateFileA(): Probably, we may not read this file."); - } - - this->mapping = CreateFileMappingA(this->file, NULL, PAGE_READONLY, (DWORD)(st.st_size >> 32), (DWORD)st.st_size, NULL); - if (this->mapping == NULL) { - CloseHandle(this->file); - LOG4CPLUS_ERROR(logger, "Error in CreateFileMappingA(" << filename << ")."); - throw exceptions::FileIoException("storm::parser::MappedFile Error in CreateFileMappingA()."); - } - - this->data = static_cast<char*>(MapViewOfFile(this->mapping, FILE_MAP_READ, 0, 0, this->st.st_size)); - if (this->data == NULL) { - CloseHandle(this->mapping); - CloseHandle(this->file); - LOG4CPLUS_ERROR(logger, "Error in MapViewOfFile(" << filename << ")."); - throw exceptions::FileIoException("storm::parser::MappedFile Error in MapViewOfFile()."); - } - this->dataend = this->data + this->st.st_size; -#endif -} - -/*! - * Will unmap the data and close the file. - */ -storm::parser::MappedFile::~MappedFile() { -#if defined LINUX || defined MACOSX - munmap(this->data, this->st.st_size); - close(this->file); -#elif defined WINDOWS - CloseHandle(this->mapping); - CloseHandle(this->file); -#endif -} diff --git a/src/parser/Parser.h b/src/parser/Parser.h deleted file mode 100644 index f934ec50a..000000000 --- a/src/parser/Parser.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Parser.h - * - * Created on: 21.11.2012 - * Author: Gereon Kremer - */ - -#ifndef STORM_PARSER_PARSER_H_ -#define STORM_PARSER_PARSER_H_ - -#include "src/utility/OsDetection.h" - -#include <sys/stat.h> -#include <fcntl.h> -#include <errno.h> -#include <iostream> -#include <fstream> -#include <memory> -#include <vector> -#include <string> - -#include <boost/integer/integer_mask.hpp> -#include "src/exceptions/FileIoException.h" -#include "src/exceptions/WrongFormatException.h" - -namespace storm { - -/*! - * @brief Contains all file parser and helper classes. - * - * This namespace contains everything needed to load data files (like - * atomic propositions, transition systems, formulas, ...) including - * methods for efficient file access (see MappedFile). - */ -namespace parser { - - struct RewardMatrixInformationStruct { - RewardMatrixInformationStruct() : rowCount(0), columnCount(0), nondeterministicChoiceIndices(nullptr) { - // Intentionally left empty. - } - - RewardMatrixInformationStruct(uint_fast64_t rowCount, uint_fast64_t columnCount, std::vector<uint_fast64_t> const * const nondeterministicChoiceIndices) - : rowCount(rowCount), columnCount(columnCount), nondeterministicChoiceIndices(nondeterministicChoiceIndices) { - // Intentionally left empty. - } - - uint_fast64_t rowCount; - uint_fast64_t columnCount; - std::vector<uint_fast64_t> const * const nondeterministicChoiceIndices; - }; - - /*! - * @brief Opens a file and maps it to memory providing a char* - * containing the file content. - * - * This class is a very simple interface to read files efficiently. - * The given file is opened and mapped to memory using mmap(). - * The public member data is a pointer to the actual file content. - * Using this method, the kernel will take care of all buffering. This is - * most probably much more efficient than doing this manually. - */ - -#if !defined LINUX && !defined MACOSX && !defined WINDOWS - #error Platform not supported -#endif - - class MappedFile { - private: -#if defined LINUX || defined MACOSX - /*! - * @brief file descriptor obtained by open(). - */ - int file; -#elif defined WINDOWS - HANDLE file; - HANDLE mapping; -#endif - -#if defined LINUX - /*! - * @brief stat information about the file. - */ - struct stat64 st; -#elif defined MACOSX - /*! - * @brief stat information about the file. - */ - struct stat st; -#elif defined WINDOWS - /*! - * @brief stat information about the file. - */ - struct __stat64 st; -#endif - - public: - /*! - * @brief pointer to actual file content. - */ - char* data; - - /*! - * @brief pointer to end of file content. - */ - char* dataend; - - /*! - * @brief Constructor of MappedFile. - */ - MappedFile(const char* filename); - - /*! - * @brief Destructor of MappedFile. - */ - ~MappedFile(); - }; - - /*! - * @brief Parses integer and checks, if something has been parsed. - */ - uint_fast64_t checked_strtol(const char* str, char** end); - - /*! - * @brief Parses floating point and checks, if something has been parsed. - */ - double checked_strtod(const char* str, char** end); - - /*! - * @brief Skips common whitespaces in a string. - */ - char* trimWhitespaces(char* buf); - - /*! - * @brief Tests whether the given file exists and is readable. - */ - bool fileExistsAndIsReadable(const char* fileName); - - /*! - * @brief Enum Class Type containing all supported file endings. - */ - enum class SupportedLineEndingsEnum : unsigned short { - Unsupported = 0, - SlashR, - SlashN, - SlashRN - }; - - /*! - * @briefs Analyzes the given file and tries to find out the used line endings. - */ - storm::parser::SupportedLineEndingsEnum findUsedLineEndings(std::string const& fileName, bool throwOnUnsupported = false); - - /*! - * @brief Encapsulates the usage of function @strchr to forward to the next line - */ - char* forwardToNextLine(char* buffer, storm::parser::SupportedLineEndingsEnum lineEndings); - - /*! - * @brief Encapsulates the usage of function @sscanf to scan for the model type hint - * @param targetBuffer The Target for the hint, should be at least 64 bytes long - * @param buffer The Source Buffer from which the Model Hint will be read - - */ - void scanForModelHint(char* targetBuffer, uint_fast64_t targetBufferSize, char const* buffer, storm::parser::SupportedLineEndingsEnum lineEndings); - - /*! - * @brief Returns the matching Separator-String in the format of "BLANK\t\NEWLINESYMBOL(S)\0 - */ - void getMatchingSeparatorString(char* targetBuffer, uint_fast64_t targetBufferSize, storm::parser::SupportedLineEndingsEnum lineEndings); - -} // namespace parser -} // namespace storm - -#endif /* STORM_PARSER_PARSER_H_ */ diff --git a/src/parser/PrctlFileParser.cpp b/src/parser/PrctlFileParser.cpp index ccf4c62b1..b4f2b3f2e 100644 --- a/src/parser/PrctlFileParser.cpp +++ b/src/parser/PrctlFileParser.cpp @@ -9,6 +9,7 @@ #include "PrctlFileParser.h" #include "PrctlParser.h" +#include "src/exceptions/FileIoException.h" namespace storm { namespace parser { diff --git a/src/parser/PrctlParser.cpp b/src/parser/PrctlParser.cpp index fb58d7ee9..d929c5ac7 100644 --- a/src/parser/PrctlParser.cpp +++ b/src/parser/PrctlParser.cpp @@ -140,7 +140,7 @@ struct PrctlParser::PrctlGrammar : qi::grammar<Iterator, storm::property::prctl: reachabilityReward = (qi::lit("F") > stateFormula)[qi::_val = phoenix::new_<storm::property::prctl::ReachabilityReward<double>>(qi::_1)]; reachabilityReward.name("path formula (for reward operator)"); - instantaneousReward = (qi::lit("I") > qi::lit("=") > qi::double_) + instantaneousReward = (qi::lit("I") > qi::lit("=") > qi::uint_) [qi::_val = phoenix::new_<storm::property::prctl::InstantaneousReward<double>>(qi::_1)]; instantaneousReward.name("path formula (for reward operator)"); steadyStateReward = (qi::lit("S"))[qi::_val = phoenix::new_<storm::property::prctl::SteadyStateReward<double>>()]; diff --git a/src/parser/PrctlParser.h b/src/parser/PrctlParser.h index 848d1de99..3ba8f1998 100644 --- a/src/parser/PrctlParser.h +++ b/src/parser/PrctlParser.h @@ -1,8 +1,6 @@ #ifndef STORM_PARSER_PRCTLPARSER_H_ #define STORM_PARSER_PRCTLPARSER_H_ -#include "src/parser/Parser.h" - #include "src/formula/Prctl.h" //#include <memory> diff --git a/src/parser/PrismParser.cpp b/src/parser/PrismParser.cpp index 587c91dc8..b026880d3 100644 --- a/src/parser/PrismParser.cpp +++ b/src/parser/PrismParser.cpp @@ -1,128 +1,416 @@ -/* - * PrismParser.cpp - * - * Created on: 11.01.2013 - * Author: chris - */ - -#include "PrismParser.h" - -#include "src/utility/OsDetection.h" - -#include "src/parser/prismparser/PrismGrammar.h" - -// If the parser fails due to ill-formed data, this exception is thrown. +#include "src/parser/PrismParser.h" +#include "src/exceptions/InvalidArgumentException.h" +#include "src/exceptions/InvalidTypeException.h" #include "src/exceptions/WrongFormatException.h" -// Needed for file IO. -#include <fstream> -#include <iomanip> -#include <limits> - -#include "log4cplus/logger.h" -#include "log4cplus/loggingmacros.h" -extern log4cplus::Logger logger; - - namespace storm { -namespace parser { - -/*! - * Opens the given file for parsing, invokes the helper function to parse the actual content and - * closes the file properly, even if an exception is thrown in the parser. In this case, the - * exception is passed on to the caller. - */ -storm::ir::Program PrismParserFromFile(std::string const& filename) { - // Open file and initialize result. - std::ifstream inputFileStream(filename, std::ios::in); - storm::ir::Program result; - - // Now try to parse the contents of the file. - try { - result = PrismParser(inputFileStream, filename); - } catch(std::exception& e) { - // In case of an exception properly close the file before passing exception. - inputFileStream.close(); - throw e; - } - - // Close the stream in case everything went smoothly and return result. - inputFileStream.close(); - return result; -} - -/*! - * Passes iterators to the input stream to the Boost spirit parser and thereby parses the input. - * If the parser throws an expectation failure exception, i.e. expected input different than the one - * provided, this is caught and displayed properly before the exception is passed on. - */ -storm::ir::Program PrismParser(std::istream& inputStream, std::string const& filename) { - // Prepare iterators to input. - // TODO: Right now, this parses the whole contents of the file into a string first. - // While this is usually not necessary, because there exist adapters that make an input stream - // iterable in both directions without storing it into a string, using the corresponding - // Boost classes gives an awful output under valgrind and is thus disabled for the time being. - std::string fileContent((std::istreambuf_iterator<char>(inputStream)), (std::istreambuf_iterator<char>())); - BaseIteratorType stringIteratorBegin = fileContent.begin(); - BaseIteratorType stringIteratorEnd = fileContent.end(); - PositionIteratorType positionIteratorBegin(stringIteratorBegin, stringIteratorEnd, filename); - PositionIteratorType positionIteratorBegin2(stringIteratorBegin, stringIteratorEnd, filename); - PositionIteratorType positionIteratorEnd; - - // Prepare resulting intermediate representation of input. - storm::ir::Program result; - - // In order to instantiate the grammar, we have to pass the type of the skipping parser. - // As this is more complex, we let Boost figure out the actual type for us. - prism::PrismGrammar grammar; - try { - // Now parse the content using phrase_parse in order to be able to supply a skipping parser. - // First run. - LOG4CPLUS_INFO(logger, "Start parsing..."); - qi::phrase_parse(positionIteratorBegin, positionIteratorEnd, grammar, boost::spirit::ascii::space | qi::lit("//") >> *(qi::char_ - qi::eol) >> qi::eol, result); - grammar.prepareForSecondRun(); - result = storm::ir::Program(); - LOG4CPLUS_INFO(logger, "Start second parsing run..."); - // Second run. - qi::phrase_parse(positionIteratorBegin2, positionIteratorEnd, grammar, boost::spirit::ascii::space | qi::lit("//") >> *(qi::char_ - qi::eol) >> qi::eol, result); - LOG4CPLUS_INFO(logger, "Finished parsing, here is the parsed program:" << std::endl << result.toString()); - // Reset grammars. - grammar.resetGrammars(); - } catch(const qi::expectation_failure<PositionIteratorType>& e) { - // If the parser expected content different than the one provided, display information - // about the location of the error. - const boost::spirit::classic::file_position_base<std::string>& pos = e.first.get_position(); - - // Construct the error message including a caret display of the position in the - // erroneous line. - std::stringstream msg; - std::string line = e.first.get_currentline(); - while (line.find('\t') != std::string::npos) line.replace(line.find('\t'),1," "); - msg << pos.file << ", line " << pos.line << ", column " << pos.column - << ": parse error: expected " << e.what_ << std::endl << "\t" - << line << std::endl << "\t"; - int i = 0; - for (i = 1; i < pos.column; ++i) { - msg << "-"; - } - msg << "^"; - for (; i < 80; ++i) { - msg << "-"; - } - msg << std::endl; - - std::cerr << msg.str(); - - // Reset grammars in any case. - grammar.resetGrammars(); - - // Now propagate exception. - throw storm::exceptions::WrongFormatException() << msg.str(); - } - - return result; -} - -} // namespace parser - + namespace parser { + storm::prism::Program PrismParser::parse(std::string const& filename) { + // Open file and initialize result. + std::ifstream inputFileStream(filename, std::ios::in); + LOG_THROW(inputFileStream.good(), storm::exceptions::WrongFormatException, "Unable to read from file '" << filename << "'."); + + storm::prism::Program result; + + // Now try to parse the contents of the file. + try { + std::string fileContent((std::istreambuf_iterator<char>(inputFileStream)), (std::istreambuf_iterator<char>())); + result = parseFromString(fileContent, filename); + } catch(std::exception& e) { + // In case of an exception properly close the file before passing exception. + inputFileStream.close(); + throw e; + } + + // Close the stream in case everything went smoothly and return result. + inputFileStream.close(); + return result; + } + + storm::prism::Program PrismParser::parseFromString(std::string const& input, std::string const& filename) { + PositionIteratorType first(input.begin()); + PositionIteratorType iter = first; + PositionIteratorType last(input.end()); + + // Create empty result; + storm::prism::Program result; + + // Create grammar. + storm::parser::PrismParser grammar(filename, first); + try { + // Start first run. + bool succeeded = qi::phrase_parse(iter, last, grammar, boost::spirit::ascii::space | qi::lit("//") >> *(qi::char_ - qi::eol) >> qi::eol, result); + LOG_THROW(succeeded, storm::exceptions::WrongFormatException, "Parsing failed in first pass."); + + // Start second run. + first = PositionIteratorType(input.begin()); + iter = first; + last = PositionIteratorType(input.end()); + grammar.moveToSecondRun(); + succeeded = qi::phrase_parse(iter, last, grammar, boost::spirit::ascii::space | qi::lit("//") >> *(qi::char_ - qi::eol) >> qi::eol, result); + LOG_THROW(succeeded, storm::exceptions::WrongFormatException, "Parsing failed in second pass."); + } catch (qi::expectation_failure<PositionIteratorType> const& e) { + // If the parser expected content different than the one provided, display information about the location of the error. + std::size_t lineNumber = boost::spirit::get_line(e.first); + + // Now propagate exception. + LOG_THROW(false, storm::exceptions::WrongFormatException, "Parsing error in line " << lineNumber << " of file " << filename << "."); + } + + return result; + } + + PrismParser::PrismParser(std::string const& filename, Iterator first) : PrismParser::base_type(start), secondRun(false), filename(filename), annotate(first), expressionParser(keywords_) { + // Parse simple identifier. + identifier %= qi::as_string[qi::raw[qi::lexeme[((qi::alpha | qi::char_('_')) >> *(qi::alnum | qi::char_('_')))]]][qi::_pass = phoenix::bind(&PrismParser::isValidIdentifier, phoenix::ref(*this), qi::_1)]; + identifier.name("identifier"); + + modelTypeDefinition %= modelType_; + modelTypeDefinition.name("model type"); + + undefinedBooleanConstantDefinition = ((qi::lit("const") >> qi::lit("bool")) > identifier > qi::lit(";"))[qi::_val = phoenix::bind(&PrismParser::createUndefinedBooleanConstant, phoenix::ref(*this), qi::_1)]; + undefinedBooleanConstantDefinition.name("undefined boolean constant declaration"); + + undefinedIntegerConstantDefinition = ((qi::lit("const") >> qi::lit("int")) > identifier > qi::lit(";"))[qi::_val = phoenix::bind(&PrismParser::createUndefinedIntegerConstant, phoenix::ref(*this), qi::_1)]; + undefinedIntegerConstantDefinition.name("undefined integer constant declaration"); + + undefinedDoubleConstantDefinition = ((qi::lit("const") >> qi::lit("double")) > identifier > qi::lit(";"))[qi::_val = phoenix::bind(&PrismParser::createUndefinedDoubleConstant, phoenix::ref(*this), qi::_1)]; + undefinedDoubleConstantDefinition.name("undefined double constant definition"); + + undefinedConstantDefinition = (undefinedBooleanConstantDefinition | undefinedIntegerConstantDefinition | undefinedDoubleConstantDefinition); + undefinedConstantDefinition.name("undefined constant definition"); + + definedBooleanConstantDefinition = ((qi::lit("const") >> qi::lit("bool") >> identifier >> qi::lit("=")) > expressionParser > qi::lit(";"))[qi::_val = phoenix::bind(&PrismParser::createDefinedBooleanConstant, phoenix::ref(*this), qi::_1, qi::_2)]; + definedBooleanConstantDefinition.name("defined boolean constant declaration"); + + definedIntegerConstantDefinition = ((qi::lit("const") >> qi::lit("int") >> identifier >> qi::lit("=")) > expressionParser >> qi::lit(";"))[qi::_val = phoenix::bind(&PrismParser::createDefinedIntegerConstant, phoenix::ref(*this), qi::_1, qi::_2)]; + definedIntegerConstantDefinition.name("defined integer constant declaration"); + + definedDoubleConstantDefinition = ((qi::lit("const") >> qi::lit("double") >> identifier >> qi::lit("=")) > expressionParser > qi::lit(";"))[qi::_val = phoenix::bind(&PrismParser::createDefinedDoubleConstant, phoenix::ref(*this), qi::_1, qi::_2)]; + definedDoubleConstantDefinition.name("defined double constant declaration"); + + definedConstantDefinition %= (definedBooleanConstantDefinition | definedIntegerConstantDefinition | definedDoubleConstantDefinition); + definedConstantDefinition.name("defined constant definition"); + + formulaDefinition = (qi::lit("formula") > identifier > qi::lit("=") > expressionParser > qi::lit(";"))[qi::_val = phoenix::bind(&PrismParser::createFormula, phoenix::ref(*this), qi::_1, qi::_2)]; + formulaDefinition.name("formula definition"); + + booleanVariableDefinition = ((identifier >> qi::lit(":") >> qi::lit("bool")) > ((qi::lit("init") > expressionParser) | qi::attr(storm::expressions::Expression::createFalse())) > qi::lit(";"))[qi::_val = phoenix::bind(&PrismParser::createBooleanVariable, phoenix::ref(*this), qi::_1, qi::_2)]; + booleanVariableDefinition.name("boolean variable definition"); + + integerVariableDefinition = ((identifier >> qi::lit(":") >> qi::lit("[")[phoenix::bind(&PrismParser::allowDoubleLiterals, phoenix::ref(*this), false)]) > expressionParser[qi::_a = qi::_1] > qi::lit("..") > expressionParser > qi::lit("]")[phoenix::bind(&PrismParser::allowDoubleLiterals, phoenix::ref(*this), true)] > -(qi::lit("init") > expressionParser[qi::_a = qi::_1]) > qi::lit(";"))[qi::_val = phoenix::bind(&PrismParser::createIntegerVariable, phoenix::ref(*this), qi::_1, qi::_2, qi::_3, qi::_a)]; + integerVariableDefinition.name("integer variable definition"); + + variableDefinition = (booleanVariableDefinition[phoenix::push_back(qi::_r1, qi::_1)] | integerVariableDefinition[phoenix::push_back(qi::_r2, qi::_1)]); + variableDefinition.name("variable declaration"); + + globalVariableDefinition = (qi::lit("global") > (booleanVariableDefinition[phoenix::push_back(phoenix::bind(&GlobalProgramInformation::globalBooleanVariables, qi::_r1), qi::_1)] | integerVariableDefinition[phoenix::push_back(phoenix::bind(&GlobalProgramInformation::globalIntegerVariables, qi::_r1), qi::_1)])); + globalVariableDefinition.name("global variable declaration list"); + + stateRewardDefinition = (expressionParser > qi::lit(":") > expressionParser >> qi::lit(";"))[qi::_val = phoenix::bind(&PrismParser::createStateReward, phoenix::ref(*this), qi::_1, qi::_2)]; + stateRewardDefinition.name("state reward definition"); + + transitionRewardDefinition = (qi::lit("[") > -(identifier[qi::_a = qi::_1]) > qi::lit("]") > expressionParser > qi::lit(":") > expressionParser > qi::lit(";"))[qi::_val = phoenix::bind(&PrismParser::createTransitionReward, phoenix::ref(*this), qi::_a, qi::_2, qi::_3)]; + transitionRewardDefinition.name("transition reward definition"); + + rewardModelDefinition = (qi::lit("rewards") > -(qi::lit("\"") > identifier[qi::_a = qi::_1] > qi::lit("\"")) + > +( stateRewardDefinition[phoenix::push_back(qi::_b, qi::_1)] + | transitionRewardDefinition[phoenix::push_back(qi::_c, qi::_1)] + ) + >> qi::lit("endrewards"))[qi::_val = phoenix::bind(&PrismParser::createRewardModel, phoenix::ref(*this), qi::_a, qi::_b, qi::_c)]; + rewardModelDefinition.name("reward model definition"); + + initialStatesConstruct = (qi::lit("init") > expressionParser > qi::lit("endinit"))[qi::_pass = phoenix::bind(&PrismParser::addInitialStatesConstruct, phoenix::ref(*this), qi::_1, qi::_r1)]; + initialStatesConstruct.name("initial construct"); + + labelDefinition = (qi::lit("label") > -qi::lit("\"") > identifier > -qi::lit("\"") > qi::lit("=") > expressionParser >> qi::lit(";"))[qi::_val = phoenix::bind(&PrismParser::createLabel, phoenix::ref(*this), qi::_1, qi::_2)]; + labelDefinition.name("label definition"); + + assignmentDefinition = (qi::lit("(") > identifier > qi::lit("'") > qi::lit("=") > expressionParser > qi::lit(")"))[qi::_val = phoenix::bind(&PrismParser::createAssignment, phoenix::ref(*this), qi::_1, qi::_2)]; + assignmentDefinition.name("assignment"); + + assignmentDefinitionList %= +assignmentDefinition % "&"; + assignmentDefinitionList.name("assignment list"); + + updateDefinition = (((expressionParser > qi::lit(":")) | qi::attr(storm::expressions::Expression::createDoubleLiteral(1))) >> assignmentDefinitionList)[qi::_val = phoenix::bind(&PrismParser::createUpdate, phoenix::ref(*this), qi::_1, qi::_2, qi::_r1)]; + updateDefinition.name("update"); + + updateListDefinition %= +updateDefinition(qi::_r1) % "+"; + updateListDefinition.name("update list"); + + commandDefinition = (qi::lit("[") > -(identifier[qi::_a = qi::_1]) > qi::lit("]") > expressionParser > qi::lit("->") > updateListDefinition(qi::_r1) > qi::lit(";"))[qi::_val = phoenix::bind(&PrismParser::createCommand, phoenix::ref(*this), qi::_a, qi::_2, qi::_3, qi::_r1)]; + commandDefinition.name("command definition"); + + moduleDefinition = ((qi::lit("module") >> identifier >> *(variableDefinition(qi::_a, qi::_b))) > +commandDefinition(qi::_r1) > qi::lit("endmodule"))[qi::_val = phoenix::bind(&PrismParser::createModule, phoenix::ref(*this), qi::_1, qi::_a, qi::_b, qi::_2, qi::_r1)]; + moduleDefinition.name("module definition"); + + moduleRenaming = ((qi::lit("module") >> identifier >> qi::lit("=")) > identifier > qi::lit("[") + > ((identifier > qi::lit("=") > identifier)[phoenix::insert(qi::_a, phoenix::construct<std::pair<std::string,std::string>>(qi::_1, qi::_2))] % ",") > qi::lit("]") + > qi::lit("endmodule"))[qi::_val = phoenix::bind(&PrismParser::createRenamedModule, phoenix::ref(*this), qi::_1, qi::_2, qi::_a, qi::_r1)]; + moduleRenaming.name("module definition via renaming"); + + moduleDefinitionList %= +(moduleRenaming(qi::_r1) | moduleDefinition(qi::_r1))[phoenix::push_back(phoenix::bind(&GlobalProgramInformation::modules, qi::_r1), qi::_1)]; + moduleDefinitionList.name("module list"); + + start = (qi::eps + > modelTypeDefinition[phoenix::bind(&GlobalProgramInformation::modelType, qi::_a) = qi::_1] + > *(definedConstantDefinition[phoenix::push_back(phoenix::bind(&GlobalProgramInformation::constants, qi::_a), qi::_1)] + | undefinedConstantDefinition[phoenix::push_back(phoenix::bind(&GlobalProgramInformation::constants, qi::_a), qi::_1)] + | formulaDefinition[phoenix::push_back(phoenix::bind(&GlobalProgramInformation::formulas, qi::_a), qi::_1)] + | globalVariableDefinition(qi::_a) + | (moduleRenaming(qi::_a) | moduleDefinition(qi::_a))[phoenix::push_back(phoenix::bind(&GlobalProgramInformation::modules, qi::_a), qi::_1)] + | initialStatesConstruct(qi::_a) + | rewardModelDefinition[phoenix::push_back(phoenix::bind(&GlobalProgramInformation::rewardModels, qi::_a), qi::_1)] + | labelDefinition[phoenix::push_back(phoenix::bind(&GlobalProgramInformation::labels, qi::_a), qi::_1)] + | formulaDefinition[phoenix::push_back(phoenix::bind(&GlobalProgramInformation::formulas, qi::_a), qi::_1)] + ) + > qi::eoi)[qi::_val = phoenix::bind(&PrismParser::createProgram, phoenix::ref(*this), qi::_a)]; + start.name("probabilistic program"); + + // Enable location tracking for important entities. + auto setLocationInfoFunction = this->annotate(qi::_val, qi::_1, qi::_3); + qi::on_success(undefinedBooleanConstantDefinition, setLocationInfoFunction); + qi::on_success(undefinedIntegerConstantDefinition, setLocationInfoFunction); + qi::on_success(undefinedDoubleConstantDefinition, setLocationInfoFunction); + qi::on_success(definedBooleanConstantDefinition, setLocationInfoFunction); + qi::on_success(definedIntegerConstantDefinition, setLocationInfoFunction); + qi::on_success(definedDoubleConstantDefinition, setLocationInfoFunction); + qi::on_success(booleanVariableDefinition, setLocationInfoFunction); + qi::on_success(integerVariableDefinition, setLocationInfoFunction); + qi::on_success(moduleDefinition, setLocationInfoFunction); + qi::on_success(moduleRenaming, setLocationInfoFunction); + qi::on_success(formulaDefinition, setLocationInfoFunction); + qi::on_success(rewardModelDefinition, setLocationInfoFunction); + qi::on_success(labelDefinition, setLocationInfoFunction); + qi::on_success(commandDefinition, setLocationInfoFunction); + qi::on_success(updateDefinition, setLocationInfoFunction); + qi::on_success(assignmentDefinition, setLocationInfoFunction); + } + + void PrismParser::moveToSecondRun() { + this->secondRun = true; + this->expressionParser.setIdentifierMapping(&this->identifiers_); + } + + void PrismParser::allowDoubleLiterals(bool flag) { + this->expressionParser.setAcceptDoubleLiterals(flag); + } + + std::string const& PrismParser::getFilename() const { + return this->filename; + } + + bool PrismParser::isValidIdentifier(std::string const& identifier) { + if (this->keywords_.find(identifier) != nullptr) { + return false; + } + return true; + } + + bool PrismParser::addInitialStatesConstruct(storm::expressions::Expression initialStatesExpression, GlobalProgramInformation& globalProgramInformation) { + LOG_THROW(!globalProgramInformation.hasInitialConstruct, storm::exceptions::WrongFormatException, "Parsing error in " << this->getFilename() << ", line " << get_line(qi::_3) << ": Program must not define two initial constructs."); + if (globalProgramInformation.hasInitialConstruct) { + return false; + } + globalProgramInformation.hasInitialConstruct = true; + globalProgramInformation.initialConstruct = storm::prism::InitialConstruct(initialStatesExpression, this->getFilename(), get_line(qi::_3)); + return true; + } + + storm::prism::Constant PrismParser::createUndefinedBooleanConstant(std::string const& newConstant) const { + if (!this->secondRun) { + LOG_THROW(this->identifiers_.find(newConstant) == nullptr, storm::exceptions::WrongFormatException, "Parsing error in " << this->getFilename() << ", line " << get_line(qi::_3) << ": Duplicate identifier '" << newConstant << "'."); + this->identifiers_.add(newConstant, storm::expressions::Expression::createBooleanVariable(newConstant)); + } + return storm::prism::Constant(storm::expressions::ExpressionReturnType::Bool, newConstant, this->getFilename()); + } + + storm::prism::Constant PrismParser::createUndefinedIntegerConstant(std::string const& newConstant) const { + if (!this->secondRun) { + LOG_THROW(this->identifiers_.find(newConstant) == nullptr, storm::exceptions::WrongFormatException, "Parsing error in " << this->getFilename() << ", line " << get_line(qi::_3) << ": Duplicate identifier '" << newConstant << "'."); + this->identifiers_.add(newConstant, storm::expressions::Expression::createIntegerVariable(newConstant)); + } + return storm::prism::Constant(storm::expressions::ExpressionReturnType::Int, newConstant, this->getFilename()); + } + + storm::prism::Constant PrismParser::createUndefinedDoubleConstant(std::string const& newConstant) const { + if (!this->secondRun) { + LOG_THROW(this->identifiers_.find(newConstant) == nullptr, storm::exceptions::WrongFormatException, "Parsing error in " << this->getFilename() << ", line " << get_line(qi::_3) << ": Duplicate identifier '" << newConstant << "'."); + this->identifiers_.add(newConstant, storm::expressions::Expression::createDoubleVariable(newConstant)); + } + return storm::prism::Constant(storm::expressions::ExpressionReturnType::Double, newConstant, this->getFilename()); + } + + storm::prism::Constant PrismParser::createDefinedBooleanConstant(std::string const& newConstant, storm::expressions::Expression expression) const { + if (!this->secondRun) { + LOG_THROW(this->identifiers_.find(newConstant) == nullptr, storm::exceptions::WrongFormatException, "Parsing error in " << this->getFilename() << ", line " << get_line(qi::_3) << ": Duplicate identifier '" << newConstant << "'."); + this->identifiers_.add(newConstant, storm::expressions::Expression::createBooleanVariable(newConstant)); + } + return storm::prism::Constant(storm::expressions::ExpressionReturnType::Bool, newConstant, expression, this->getFilename()); + } + + storm::prism::Constant PrismParser::createDefinedIntegerConstant(std::string const& newConstant, storm::expressions::Expression expression) const { + if (!this->secondRun) { + LOG_THROW(this->identifiers_.find(newConstant) == nullptr, storm::exceptions::WrongFormatException, "Parsing error in " << this->getFilename() << ", line " << get_line(qi::_3) << ": Duplicate identifier '" << newConstant << "'."); + this->identifiers_.add(newConstant, storm::expressions::Expression::createIntegerVariable(newConstant)); + } + return storm::prism::Constant(storm::expressions::ExpressionReturnType::Int, newConstant, expression, this->getFilename()); + } + + storm::prism::Constant PrismParser::createDefinedDoubleConstant(std::string const& newConstant, storm::expressions::Expression expression) const { + if (!this->secondRun) { + LOG_THROW(this->identifiers_.find(newConstant) == nullptr, storm::exceptions::WrongFormatException, "Parsing error in " << this->getFilename() << ", line " << get_line(qi::_3) << ": Duplicate identifier '" << newConstant << "'."); + this->identifiers_.add(newConstant, storm::expressions::Expression::createDoubleVariable(newConstant)); + } + return storm::prism::Constant(storm::expressions::ExpressionReturnType::Double, newConstant, expression, this->getFilename()); + } + + storm::prism::Formula PrismParser::createFormula(std::string const& formulaName, storm::expressions::Expression expression) const { + if (!this->secondRun) { + LOG_THROW(this->identifiers_.find(formulaName) == nullptr, storm::exceptions::WrongFormatException, "Parsing error in " << this->getFilename() << ", line " << get_line(qi::_3) << ": Duplicate identifier '" << formulaName << "'."); + this->identifiers_.add(formulaName, expression); + } + return storm::prism::Formula(formulaName, expression, this->getFilename()); + } + + storm::prism::Label PrismParser::createLabel(std::string const& labelName, storm::expressions::Expression expression) const { + return storm::prism::Label(labelName, expression, this->getFilename()); + } + + storm::prism::RewardModel PrismParser::createRewardModel(std::string const& rewardModelName, std::vector<storm::prism::StateReward> const& stateRewards, std::vector<storm::prism::TransitionReward> const& transitionRewards) const { + return storm::prism::RewardModel(rewardModelName, stateRewards, transitionRewards, this->getFilename()); + } + + storm::prism::StateReward PrismParser::createStateReward(storm::expressions::Expression statePredicateExpression, storm::expressions::Expression rewardValueExpression) const { + return storm::prism::StateReward(statePredicateExpression, rewardValueExpression, this->getFilename()); + } + + storm::prism::TransitionReward PrismParser::createTransitionReward(std::string const& actionName, storm::expressions::Expression statePredicateExpression, storm::expressions::Expression rewardValueExpression) const { + return storm::prism::TransitionReward(actionName, statePredicateExpression, rewardValueExpression, this->getFilename()); + } + + storm::prism::Assignment PrismParser::createAssignment(std::string const& variableName, storm::expressions::Expression assignedExpression) const { + return storm::prism::Assignment(variableName, assignedExpression, this->getFilename()); + } + + storm::prism::Update PrismParser::createUpdate(storm::expressions::Expression likelihoodExpression, std::vector<storm::prism::Assignment> const& assignments, GlobalProgramInformation& globalProgramInformation) const { + ++globalProgramInformation.currentUpdateIndex; + return storm::prism::Update(globalProgramInformation.currentUpdateIndex - 1, likelihoodExpression, assignments, this->getFilename()); + } + + storm::prism::Command PrismParser::createCommand(std::string const& actionName, storm::expressions::Expression guardExpression, std::vector<storm::prism::Update> const& updates, GlobalProgramInformation& globalProgramInformation) const { + ++globalProgramInformation.currentCommandIndex; + return storm::prism::Command(globalProgramInformation.currentCommandIndex - 1, actionName, guardExpression, updates, this->getFilename()); + } + + storm::prism::BooleanVariable PrismParser::createBooleanVariable(std::string const& variableName, storm::expressions::Expression initialValueExpression) const { + if (!this->secondRun) { + LOG_THROW(this->identifiers_.find(variableName) == nullptr, storm::exceptions::WrongFormatException, "Parsing error in " << this->getFilename() << ", line " << get_line(qi::_3) << ": Duplicate identifier '" << variableName << "'."); + this->identifiers_.add(variableName, storm::expressions::Expression::createBooleanVariable(variableName)); + } + return storm::prism::BooleanVariable(variableName, initialValueExpression, this->getFilename()); + } + + storm::prism::IntegerVariable PrismParser::createIntegerVariable(std::string const& variableName, storm::expressions::Expression lowerBoundExpression, storm::expressions::Expression upperBoundExpression, storm::expressions::Expression initialValueExpression) const { + if (!this->secondRun) { + LOG_THROW(this->identifiers_.find(variableName) == nullptr, storm::exceptions::WrongFormatException, "Parsing error in " << this->getFilename() << ", line " << get_line(qi::_3) << ": Duplicate identifier '" << variableName << "'."); + this->identifiers_.add(variableName, storm::expressions::Expression::createIntegerVariable(variableName)); + } + return storm::prism::IntegerVariable(variableName, lowerBoundExpression, upperBoundExpression, initialValueExpression, this->getFilename()); + } + + storm::prism::Module PrismParser::createModule(std::string const& moduleName, std::vector<storm::prism::BooleanVariable> const& booleanVariables, std::vector<storm::prism::IntegerVariable> const& integerVariables, std::vector<storm::prism::Command> const& commands, GlobalProgramInformation& globalProgramInformation) const { + globalProgramInformation.moduleToIndexMap[moduleName] = globalProgramInformation.modules.size(); + return storm::prism::Module(moduleName, booleanVariables, integerVariables, commands, this->getFilename()); + } + + storm::prism::Module PrismParser::createRenamedModule(std::string const& newModuleName, std::string const& oldModuleName, std::map<std::string, std::string> const& renaming, GlobalProgramInformation& globalProgramInformation) const { + // Check whether the module to rename actually exists. + auto const& moduleIndexPair = globalProgramInformation.moduleToIndexMap.find(oldModuleName); + LOG_THROW(moduleIndexPair != globalProgramInformation.moduleToIndexMap.end(), storm::exceptions::WrongFormatException, "Parsing error in " << this->getFilename() << ", line " << get_line(qi::_3) << ": No module named '" << oldModuleName << "' to rename."); + storm::prism::Module const& moduleToRename = globalProgramInformation.modules[moduleIndexPair->second]; + + if (!this->secondRun) { + // Register all (renamed) variables for later use. + for (auto const& variable : moduleToRename.getBooleanVariables()) { + auto const& renamingPair = renaming.find(variable.getName()); + LOG_THROW(renamingPair != renaming.end(), storm::exceptions::WrongFormatException, "Parsing error in " << this->getFilename() << ", line " << get_line(qi::_3) << ": Boolean variable '" << variable.getName() << " was not renamed."); + this->identifiers_.add(renamingPair->second, storm::expressions::Expression::createBooleanVariable(renamingPair->second)); + } + for (auto const& variable : moduleToRename.getIntegerVariables()) { + auto const& renamingPair = renaming.find(variable.getName()); + LOG_THROW(renamingPair != renaming.end(), storm::exceptions::WrongFormatException, "Parsing error in " << this->getFilename() << ", line " << get_line(qi::_3) << ": Integer variable '" << variable.getName() << " was not renamed."); + this->identifiers_.add(renamingPair->second, storm::expressions::Expression::createIntegerVariable(renamingPair->second)); + } + + // Return a dummy module in the first pass. + return storm::prism::Module(); + } else { + // Add a mapping from the new module name to its (future) index. + globalProgramInformation.moduleToIndexMap[newModuleName] = globalProgramInformation.modules.size(); + + // Create a mapping from identifiers to the expressions they need to be replaced with. + std::map<std::string, storm::expressions::Expression> expressionRenaming; + for (auto const& namePair : renaming) { + storm::expressions::Expression const* substitutedExpression = this->identifiers_.find(namePair.second); + // If the mapped-to-value is an expression, we need to replace it. + if (substitutedExpression != nullptr) { + expressionRenaming.emplace(namePair.first, *substitutedExpression); + } + } + + // Rename the boolean variables. + std::vector<storm::prism::BooleanVariable> booleanVariables; + for (auto const& variable : moduleToRename.getBooleanVariables()) { + auto const& renamingPair = renaming.find(variable.getName()); + LOG_THROW(renamingPair != renaming.end(), storm::exceptions::WrongFormatException, "Parsing error in " << this->getFilename() << ", line " << get_line(qi::_3) << ": Boolean variable '" << variable.getName() << " was not renamed."); + + booleanVariables.push_back(storm::prism::BooleanVariable(renamingPair->second, variable.getInitialValueExpression().substitute(expressionRenaming), this->getFilename(), get_line(qi::_1))); + } + + // Rename the integer variables. + std::vector<storm::prism::IntegerVariable> integerVariables; + for (auto const& variable : moduleToRename.getIntegerVariables()) { + auto const& renamingPair = renaming.find(variable.getName()); + LOG_THROW(renamingPair != renaming.end(), storm::exceptions::WrongFormatException, "Parsing error in " << this->getFilename() << ", line " << get_line(qi::_3) << ": Integer variable '" << variable.getName() << " was not renamed."); + + integerVariables.push_back(storm::prism::IntegerVariable(renamingPair->second, variable.getLowerBoundExpression().substitute(expressionRenaming), variable.getUpperBoundExpression().substitute(expressionRenaming), variable.getInitialValueExpression().substitute(expressionRenaming), this->getFilename(), get_line(qi::_1))); + } + + // Rename commands. + std::vector<storm::prism::Command> commands; + for (auto const& command : moduleToRename.getCommands()) { + std::vector<storm::prism::Update> updates; + for (auto const& update : command.getUpdates()) { + std::vector<storm::prism::Assignment> assignments; + for (auto const& assignment : update.getAssignments()) { + auto const& renamingPair = renaming.find(assignment.getVariableName()); + if (renamingPair != renaming.end()) { + assignments.emplace_back(renamingPair->second, assignment.getExpression().substitute(expressionRenaming), this->getFilename(), get_line(qi::_1)); + } else { + assignments.emplace_back(assignment.getVariableName(), assignment.getExpression().substitute(expressionRenaming), this->getFilename(), get_line(qi::_1)); + } + } + updates.emplace_back(globalProgramInformation.currentUpdateIndex, update.getLikelihoodExpression().substitute(expressionRenaming), assignments, this->getFilename(), get_line(qi::_1)); + ++globalProgramInformation.currentUpdateIndex; + } + + std::string newActionName = command.getActionName(); + auto const& renamingPair = renaming.find(command.getActionName()); + if (renamingPair != renaming.end()) { + newActionName = renamingPair->second; + } + + commands.emplace_back(globalProgramInformation.currentCommandIndex, newActionName, command.getGuardExpression().substitute(expressionRenaming), updates, this->getFilename(), get_line(qi::_1)); + ++globalProgramInformation.currentCommandIndex; + } + + return storm::prism::Module(newModuleName, booleanVariables, integerVariables, commands, oldModuleName, renaming); + } + } + + storm::prism::Program PrismParser::createProgram(GlobalProgramInformation const& globalProgramInformation) const { + return storm::prism::Program(globalProgramInformation.modelType, globalProgramInformation.constants, globalProgramInformation.globalBooleanVariables, globalProgramInformation.globalIntegerVariables, globalProgramInformation.formulas, globalProgramInformation.modules, globalProgramInformation.rewardModels, this->secondRun && !globalProgramInformation.hasInitialConstruct, globalProgramInformation.initialConstruct, globalProgramInformation.labels, this->getFilename(), 1, this->secondRun); + } + } // namespace parser } // namespace storm diff --git a/src/parser/PrismParser.h b/src/parser/PrismParser.h index da8ad0941..bdf84af24 100644 --- a/src/parser/PrismParser.h +++ b/src/parser/PrismParser.h @@ -1,49 +1,246 @@ -/* * PrismParser.h - * - * Created on: Jan 3, 2013 - * Author: Christian Dehnert - */ - #ifndef STORM_PARSER_PRISMPARSER_H_ -#define STORM_PARSER_PRISMPARSER_H_ - -// All classes of the intermediate representation are used. -#include "src/ir/IR.h" +#define STORM_PARSER_PRISMPARSER_H_ -// Used for file input. -#include <istream> +// Include files for file input. +#include <fstream> #include <memory> +#include <iomanip> -namespace storm { - -namespace parser { - -using namespace storm::ir; -using namespace storm::ir::expressions; +#include "src/parser/SpiritParserDefinitions.h" +#include "src/parser/ExpressionParser.h" +#include "src/storage/prism/Program.h" +#include "src/storage/expressions/Expression.h" +#include "src/storage/expressions/Expressions.h" +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/WrongFormatException.h" -/* - * This functions parse the format of the PRISM model checker into an intermediate representation. - */ - -/*! - * Parses the given file into the intermediate representation assuming it complies with the - * PRISM syntax. - * @param filename the name of the file to parse. - * @return a shared pointer to the intermediate representation of the PRISM file. - */ -storm::ir::Program PrismParserFromFile(std::string const& filename); - -/*! - * Parses the given input stream into the intermediate representation assuming it complies with - * the PRISM syntax. - * @param inputStream the input stream to parse. - * @param filename the name of the file the input stream belongs to. Used for diagnostics. - * @return a shared pointer to the intermediate representation of the PRISM file. - */ -storm::ir::Program PrismParser(std::istream& inputStream, std::string const& filename); - -} // namespace parser +namespace storm { + namespace parser { + // A class that stores information about the parsed program. + class GlobalProgramInformation { + public: + // Default construct the header information. + GlobalProgramInformation() : modelType(), constants(), formulas(), globalBooleanVariables(), globalIntegerVariables(), moduleToIndexMap(), modules(), rewardModels(), labels(),hasInitialConstruct(false), initialConstruct(storm::expressions::Expression::createFalse()), currentCommandIndex(0), currentUpdateIndex(0) { + // Intentionally left empty. + } + + // Members for all essential information that needs to be collected. + storm::prism::Program::ModelType modelType; + std::vector<storm::prism::Constant> constants; + std::vector<storm::prism::Formula> formulas; + std::vector<storm::prism::BooleanVariable> globalBooleanVariables; + std::vector<storm::prism::IntegerVariable> globalIntegerVariables; + std::map<std::string, uint_fast64_t> moduleToIndexMap; + std::vector<storm::prism::Module> modules; + std::vector<storm::prism::RewardModel> rewardModels; + std::vector<storm::prism::Label> labels; + bool hasInitialConstruct; + storm::prism::InitialConstruct initialConstruct; + + // Counters to provide unique indexing for commands and updates. + uint_fast64_t currentCommandIndex; + uint_fast64_t currentUpdateIndex; + }; + class PrismParser : public qi::grammar<Iterator, storm::prism::Program(), qi::locals<GlobalProgramInformation>, Skipper> { + public: + /*! + * Parses the given file into the PRISM storage classes assuming it complies with the PRISM syntax. + * + * @param filename the name of the file to parse. + * @return The resulting PRISM program. + */ + static storm::prism::Program parse(std::string const& filename); + + /*! + * Parses the given input stream into the PRISM storage classes assuming it complies with the PRISM syntax. + * + * @param input The input string to parse. + * @param filename The name of the file from which the input was read. + * @return The resulting PRISM program. + */ + static storm::prism::Program parseFromString(std::string const& input, std::string const& filename); + + private: + struct modelTypeStruct : qi::symbols<char, storm::prism::Program::ModelType> { + modelTypeStruct() { + add + ("dtmc", storm::prism::Program::ModelType::DTMC) + ("ctmc", storm::prism::Program::ModelType::CTMC) + ("mdp", storm::prism::Program::ModelType::MDP) + ("ctmdp", storm::prism::Program::ModelType::CTMDP) + ("ma", storm::prism::Program::ModelType::MA); + } + }; + + struct keywordsStruct : qi::symbols<char, uint_fast64_t> { + keywordsStruct() { + add + ("dtmc", 1) + ("ctmc", 2) + ("mdp", 3) + ("ctmdp", 4) + ("ma", 5) + ("const", 6) + ("int", 7) + ("bool", 8) + ("module", 9) + ("endmodule", 10) + ("rewards", 11) + ("endrewards", 12) + ("true", 13) + ("min", 14) + ("max", 15) + ("floor", 16) + ("ceil", 17) + ("init", 18) + ("endinit", 19); + } + }; + + // Functor used for annotating entities with line number information. + class PositionAnnotation { + public: + typedef void result_type; + + PositionAnnotation(Iterator first) : first(first) { + // Intentionally left empty. + } + + template<typename Entity, typename First, typename Last> + result_type operator()(Entity& entity, First f, Last l) const { + entity.setLineNumber(get_line(f)); + } + private: + std::string filename; + Iterator const first; + }; + + /*! + * Creates a grammar for the given filename and the iterator to the first input to parse. + * + * @param filename The filename that is to be read. This is used for proper error reporting. + * @param first The iterator to the beginning of the input. + */ + PrismParser(std::string const& filename, Iterator first); + + /*! + * Sets an internal flag that indicates the second run is now taking place. + */ + void moveToSecondRun(); + + // A flag that stores whether the grammar is currently doing the second run. + bool secondRun; + + /*! + * Sets whether doubles literals are allowed in the parsed expression. + * + * @param flag Indicates whether to allow or forbid double literals in the parsed expression. + */ + void allowDoubleLiterals(bool flag); + + // The name of the file being parsed. + std::string filename; + + /*! + * Retrieves the name of the file currently being parsed. + * + * @return The name of the file currently being parsed. + */ + std::string const& getFilename() const; + + // A function used for annotating the entities with their position. + phoenix::function<PositionAnnotation> annotate; + + // The starting point of the grammar. + qi::rule<Iterator, storm::prism::Program(), qi::locals<GlobalProgramInformation>, Skipper> start; + + // Rules for model type. + qi::rule<Iterator, storm::prism::Program::ModelType(), Skipper> modelTypeDefinition; + + // Rules for parsing the program header. + qi::rule<Iterator, storm::prism::Constant(), Skipper> undefinedConstantDefinition; + qi::rule<Iterator, storm::prism::Constant(), Skipper> undefinedBooleanConstantDefinition; + qi::rule<Iterator, storm::prism::Constant(), Skipper> undefinedIntegerConstantDefinition; + qi::rule<Iterator, storm::prism::Constant(), Skipper> undefinedDoubleConstantDefinition; + qi::rule<Iterator, storm::prism::Constant(), Skipper> definedConstantDefinition; + qi::rule<Iterator, storm::prism::Constant(), Skipper> definedBooleanConstantDefinition; + qi::rule<Iterator, storm::prism::Constant(), Skipper> definedIntegerConstantDefinition; + qi::rule<Iterator, storm::prism::Constant(), Skipper> definedDoubleConstantDefinition; + + // Rules for global variable definitions. + qi::rule<Iterator, qi::unused_type(GlobalProgramInformation&), Skipper> globalVariableDefinition; + qi::rule<Iterator, qi::unused_type(GlobalProgramInformation&), Skipper> globalBooleanVariableDefinition; + qi::rule<Iterator, qi::unused_type(GlobalProgramInformation&), Skipper> globalIntegerVariableDefinition; + + // Rules for modules definition. + qi::rule<Iterator, std::vector<storm::prism::Module>(GlobalProgramInformation&), Skipper> moduleDefinitionList; + qi::rule<Iterator, storm::prism::Module(GlobalProgramInformation&), qi::locals<std::vector<storm::prism::BooleanVariable>, std::vector<storm::prism::IntegerVariable>>, Skipper> moduleDefinition; + qi::rule<Iterator, storm::prism::Module(GlobalProgramInformation&), qi::locals<std::map<std::string, std::string>>, Skipper> moduleRenaming; + + // Rules for variable definitions. + qi::rule<Iterator, qi::unused_type(std::vector<storm::prism::BooleanVariable>&, std::vector<storm::prism::IntegerVariable>&), Skipper> variableDefinition; + qi::rule<Iterator, storm::prism::BooleanVariable(), Skipper> booleanVariableDefinition; + qi::rule<Iterator, storm::prism::IntegerVariable(), qi::locals<storm::expressions::Expression>, Skipper> integerVariableDefinition; + + // Rules for command definitions. + qi::rule<Iterator, storm::prism::Command(GlobalProgramInformation&), qi::locals<std::string>, Skipper> commandDefinition; + qi::rule<Iterator, std::vector<storm::prism::Update>(GlobalProgramInformation&), Skipper> updateListDefinition; + qi::rule<Iterator, storm::prism::Update(GlobalProgramInformation&), Skipper> updateDefinition; + qi::rule<Iterator, std::vector<storm::prism::Assignment>(), Skipper> assignmentDefinitionList; + qi::rule<Iterator, storm::prism::Assignment(), Skipper> assignmentDefinition; + + // Rules for reward definitions. + qi::rule<Iterator, storm::prism::RewardModel(), qi::locals<std::string, std::vector<storm::prism::StateReward>, std::vector<storm::prism::TransitionReward>>, Skipper> rewardModelDefinition; + qi::rule<Iterator, storm::prism::StateReward(), Skipper> stateRewardDefinition; + qi::rule<Iterator, storm::prism::TransitionReward(), qi::locals<std::string>, Skipper> transitionRewardDefinition; + + // Rules for initial states expression. + qi::rule<Iterator, qi::unused_type(GlobalProgramInformation&), Skipper> initialStatesConstruct; + + // Rules for label definitions. + qi::rule<Iterator, storm::prism::Label(), Skipper> labelDefinition; + + // Rules for formula definitions. + qi::rule<Iterator, storm::prism::Formula(), Skipper> formulaDefinition; + + // Rules for identifier parsing. + qi::rule<Iterator, std::string(), Skipper> identifier; + + // Parsers that recognize special keywords and model types. + storm::parser::PrismParser::keywordsStruct keywords_; + storm::parser::PrismParser::modelTypeStruct modelType_; + qi::symbols<char, storm::expressions::Expression> identifiers_; + + // Parser used for recognizing expressions. + storm::parser::ExpressionParser expressionParser; + + // Helper methods used in the grammar. + bool isValidIdentifier(std::string const& identifier); + bool addInitialStatesConstruct(storm::expressions::Expression initialStatesExpression, GlobalProgramInformation& globalProgramInformation); + + storm::prism::Constant createUndefinedBooleanConstant(std::string const& newConstant) const; + storm::prism::Constant createUndefinedIntegerConstant(std::string const& newConstant) const; + storm::prism::Constant createUndefinedDoubleConstant(std::string const& newConstant) const; + storm::prism::Constant createDefinedBooleanConstant(std::string const& newConstant, storm::expressions::Expression expression) const; + storm::prism::Constant createDefinedIntegerConstant(std::string const& newConstant, storm::expressions::Expression expression) const; + storm::prism::Constant createDefinedDoubleConstant(std::string const& newConstant, storm::expressions::Expression expression) const; + storm::prism::Formula createFormula(std::string const& formulaName, storm::expressions::Expression expression) const; + storm::prism::Label createLabel(std::string const& labelName, storm::expressions::Expression expression) const; + storm::prism::RewardModel createRewardModel(std::string const& rewardModelName, std::vector<storm::prism::StateReward> const& stateRewards, std::vector<storm::prism::TransitionReward> const& transitionRewards) const; + storm::prism::StateReward createStateReward(storm::expressions::Expression statePredicateExpression, storm::expressions::Expression rewardValueExpression) const; + storm::prism::TransitionReward createTransitionReward(std::string const& actionName, storm::expressions::Expression statePredicateExpression, storm::expressions::Expression rewardValueExpression) const; + storm::prism::Assignment createAssignment(std::string const& variableName, storm::expressions::Expression assignedExpression) const; + storm::prism::Update createUpdate(storm::expressions::Expression likelihoodExpression, std::vector<storm::prism::Assignment> const& assignments, GlobalProgramInformation& globalProgramInformation) const; + storm::prism::Command createCommand(std::string const& actionName, storm::expressions::Expression guardExpression, std::vector<storm::prism::Update> const& updates, GlobalProgramInformation& globalProgramInformation) const; + storm::prism::BooleanVariable createBooleanVariable(std::string const& variableName, storm::expressions::Expression initialValueExpression) const; + storm::prism::IntegerVariable createIntegerVariable(std::string const& variableName, storm::expressions::Expression lowerBoundExpression, storm::expressions::Expression upperBoundExpression, storm::expressions::Expression initialValueExpression) const; + storm::prism::Module createModule(std::string const& moduleName, std::vector<storm::prism::BooleanVariable> const& booleanVariables, std::vector<storm::prism::IntegerVariable> const& integerVariables, std::vector<storm::prism::Command> const& commands, GlobalProgramInformation& globalProgramInformation) const; + storm::prism::Module createRenamedModule(std::string const& newModuleName, std::string const& oldModuleName, std::map<std::string, std::string> const& renaming, GlobalProgramInformation& globalProgramInformation) const; + storm::prism::Program createProgram(GlobalProgramInformation const& globalProgramInformation) const; + }; + } // namespace parser } // namespace storm -#endif /* STORM_PARSER_PRISMPARSER_H_ */ +#endif /* STORM_PARSER_PRISMPARSER_H_ */ + diff --git a/src/parser/SparseStateRewardParser.cpp b/src/parser/SparseStateRewardParser.cpp index a5d43e024..679bb5e7e 100644 --- a/src/parser/SparseStateRewardParser.cpp +++ b/src/parser/SparseStateRewardParser.cpp @@ -7,72 +7,70 @@ #include "src/parser/SparseStateRewardParser.h" -#include <errno.h> -#include <time.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <locale.h> -#include <iostream> -#include <cstdlib> -#include <cstdio> -#include <cstring> -#include <string> -#include <vector> -#include <clocale> - #include "src/exceptions/WrongFormatException.h" +#include "src/exceptions/OutOfRangeException.h" #include "src/exceptions/FileIoException.h" -#include "src/utility/OsDetection.h" +#include "src/utility/cstring.h" +#include "src/parser/MappedFile.h" #include "log4cplus/logger.h" #include "log4cplus/loggingmacros.h" extern log4cplus::Logger logger; namespace storm { -namespace parser { + namespace parser { + using namespace storm::utility::cstring; -/*! - * Reads a state reward file and puts the result in a state reward vector. - * - * @param stateCount The number of states. - * @param filename The filename of the state reward file. - * @return The created state reward vector. - */ -std::vector<double> SparseStateRewardParser(uint_fast64_t stateCount, std::string const & filename) { - // Open file. - if (!fileExistsAndIsReadable(filename.c_str())) { - LOG4CPLUS_ERROR(logger, "Error while parsing " << filename << ": File does not exist or is not readable."); - throw storm::exceptions::WrongFormatException(); - } - - MappedFile file(filename.c_str()); - char* buf = file.data; - - // Create state reward vector with given state count. - std::vector<double> stateRewards(stateCount); - - { - // Now parse state reward assignments. - uint_fast64_t state; - double reward; - - // Iterate over states. - while (buf[0] != '\0') { - // Parse state number and reward value. - state = checked_strtol(buf, &buf); - reward = checked_strtod(buf, &buf); - if (reward < 0.0) { - LOG4CPLUS_ERROR(logger, "Expected positive reward value but got \"" << reward << "\"."); - throw storm::exceptions::WrongFormatException() << "State reward file specifies illegal reward value."; + std::vector<double> SparseStateRewardParser::parseSparseStateReward(uint_fast64_t stateCount, std::string const & filename) { + // Open file. + if (!MappedFile::fileExistsAndIsReadable(filename.c_str())) { + LOG4CPLUS_ERROR(logger, "Error while parsing " << filename << ": File does not exist or is not readable."); + throw storm::exceptions::FileIoException() << "Error while parsing " << filename << ": File does not exist or is not readable."; } - stateRewards[state] = reward; + MappedFile file(filename.c_str()); + char* buf = file.getData(); + + // Create state reward vector with given state count. + std::vector<double> stateRewards(stateCount); + + // Now parse state reward assignments. + uint_fast64_t state = 0; + uint_fast64_t lastState = (uint_fast64_t)-1; + double reward; + + // Iterate over states. + while (buf[0] != '\0') { - buf = trimWhitespaces(buf); + // Parse state number and reward value. + state = checked_strtol(buf, &buf); + + // If the state has already been read or skipped once there might be a problem with the file (doubled lines, or blocks). + // Note: The value -1 shows that lastState has not yet been set, i.e. this is the first run of the loop (state index (2^64)-1 is a really bad starting index). + if(state <= lastState && lastState != (uint_fast64_t)-1) { + LOG4CPLUS_ERROR(logger, "State " << state << " was found but has already been read or skipped previously."); + throw storm::exceptions::WrongFormatException() << "State " << state << " was found but has already been read or skipped previously."; + } + + if(stateCount <= state) { + LOG4CPLUS_ERROR(logger, "Found reward for a state of an invalid index \"" << state << "\". The model has only " << stateCount << " states."); + throw storm::exceptions::OutOfRangeException() << "Found reward for a state of an invalid index \"" << state << "\""; + } + + reward = checked_strtod(buf, &buf); + + if (reward < 0.0) { + LOG4CPLUS_ERROR(logger, "Expected positive reward value but got \"" << reward << "\"."); + throw storm::exceptions::WrongFormatException() << "State reward file specifies illegal reward value."; + } + + stateRewards[state] = reward; + + buf = trimWhitespaces(buf); + lastState = state; + } + return stateRewards; } - } - return stateRewards; -} -} // namespace parser + } // namespace parser } // namespace storm diff --git a/src/parser/SparseStateRewardParser.h b/src/parser/SparseStateRewardParser.h index 03f26c902..f9e149e83 100644 --- a/src/parser/SparseStateRewardParser.h +++ b/src/parser/SparseStateRewardParser.h @@ -2,21 +2,30 @@ #define STORM_PARSER_SPARSESTATEREWARDPARSER_H_ #include <cstdint> -#include "src/parser/Parser.h" -#include <memory> #include <vector> +#include <string> namespace storm { + namespace parser { -namespace parser { + /*! + * A class providing the functionality to parse a the state rewards of a model. + */ + class SparseStateRewardParser { + public: -/*! - * @brief Load state reward file and return vector of state rewards. - */ -std::vector<double> SparseStateRewardParser(uint_fast64_t stateCount, std::string const &filename); + /*! + * Reads a state reward file and puts the result in a state reward vector. + * + * @param stateCount The number of states. + * @param filename The path and name of the state reward file. + * @return The created state reward vector. + */ + static std::vector<double> parseSparseStateReward(uint_fast64_t stateCount, std::string const &filename); -} // namespace parser + }; + } // namespace parser } // namespace storm #endif /* STORM_PARSER_SPARSESTATEREWARDPARSER_H_ */ diff --git a/src/parser/SpiritParserDefinitions.h b/src/parser/SpiritParserDefinitions.h new file mode 100644 index 000000000..feb78f77d --- /dev/null +++ b/src/parser/SpiritParserDefinitions.h @@ -0,0 +1,21 @@ +#ifndef STORM_PARSER_SPIRITPARSERDEFINITIONS_H_ +#define STORM_PARSER_SPIRITPARSERDEFINITIONS_H_ + +// Include boost spirit. +#define BOOST_SPIRIT_USE_PHOENIX_V3 +#include <boost/typeof/typeof.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/phoenix.hpp> +#include <boost/spirit/include/support_line_pos_iterator.hpp> +#include <boost/spirit/home/classic/iterator/position_iterator.hpp> + +namespace qi = boost::spirit::qi; +namespace phoenix = boost::phoenix; + +typedef std::string::const_iterator BaseIteratorType; +typedef boost::spirit::line_pos_iterator<BaseIteratorType> PositionIteratorType; +typedef PositionIteratorType Iterator; + +typedef BOOST_TYPEOF(boost::spirit::ascii::space | qi::lit("//") >> *(qi::char_ - qi::eol) >> qi::eol) Skipper; + +#endif /* STORM_PARSER_SPIRITPARSERDEFINITIONS_H_ */ \ No newline at end of file diff --git a/src/parser/prismparser/BaseGrammar.h b/src/parser/prismparser/BaseGrammar.h deleted file mode 100644 index 7cac493c3..000000000 --- a/src/parser/prismparser/BaseGrammar.h +++ /dev/null @@ -1,253 +0,0 @@ -/* - * File: Keywords.h - * Author: nafur - * - * Created on April 10, 2013, 6:03 PM - */ - -#ifndef BASEGRAMMAR_H -#define BASEGRAMMAR_H - -#include "Includes.h" - -#include "VariableState.h" - -namespace storm { -namespace parser { -namespace prism { - - /*! - * This is the base class for all expression grammars. - * It takes care of implementing a singleton, stores a VariableState and implements some common helper routines. - */ - template <typename T> - class BaseGrammar { - public: - /*! - * Constructor. - */ - BaseGrammar(std::shared_ptr<VariableState> const& state) : state(state) {} - - /*! - * Create and return a new instance of class T, usually the subclass. - * @param state VariableState to be given to the constructor. - * @returns Instance of class T. - */ - static T& instance(std::shared_ptr<VariableState> const& state = nullptr) { - if (BaseGrammar::instanceObject == nullptr) { - BaseGrammar::instanceObject = std::shared_ptr<T>(new T(state)); - if (!state->firstRun) BaseGrammar::instanceObject->secondRun(); - } - return *BaseGrammar::instanceObject; - } - - /*! - * Clear the cached instance. - */ - static void resetInstance() { - BaseGrammar::instanceObject = nullptr; - } - - /*! - * Notify the cached object, that we will begin with the second parsing run. - */ - static void secondRun() { - if (BaseGrammar::instanceObject != nullptr) { - BaseGrammar::instanceObject->prepareSecondRun(); - } - } - - /*! - * Create a new boolean literal with the given value. - * @param value Value of the literal. - * @returns Boolean literal. - */ - std::shared_ptr<BaseExpression> createBoolLiteral(bool value) { - return std::shared_ptr<BaseExpression>(new BooleanLiteralExpression(value)); - } - /*! - * Create a new double literal with the given value. - * @param value Value of the literal. - * @returns Double literal. - */ - std::shared_ptr<BaseExpression> createDoubleLiteral(double value) { - return std::shared_ptr<BaseExpression>(new DoubleLiteralExpression(value)); - } - /*! - * Create a new integer literal with the given value. - * @param value Value of the literal. - * @returns Integer literal. - */ - std::shared_ptr<BaseExpression> createIntLiteral(int_fast64_t value) { - return std::shared_ptr<BaseExpression>(new IntegerLiteralExpression(value)); - } - - /*! - * Create a new plus expression. If addition is true, it will be an addition, otherwise a subtraction. - * @param left Left operand. - * @param addition Flag for addition or subtraction. - * @param right Right operand. - * @param type Return type. - * @returns Plus expression. - */ - std::shared_ptr<BaseExpression> createPlus(std::shared_ptr<BaseExpression> const& left, bool addition, std::shared_ptr<BaseExpression> const& right, BaseExpression::ReturnType type) { - if (addition) { - return std::shared_ptr<BaseExpression>(new BinaryNumericalFunctionExpression(type, left->clone(), right->clone(), BinaryNumericalFunctionExpression::PLUS)); - } else { - return std::shared_ptr<BaseExpression>(new BinaryNumericalFunctionExpression(type, left->clone(), right->clone(), BinaryNumericalFunctionExpression::MINUS)); - } - } - /*! - * Create a new double plus expression. If addition is true, it will be an addition, otherwise a subtraction. - * @param left Left operand. - * @param addition Flag for addition or subtraction. - * @param right Right operand. - * @returns Double plus expression. - */ - std::shared_ptr<BaseExpression> createDoublePlus(std::shared_ptr<BaseExpression> const& left, bool addition, std::shared_ptr<BaseExpression> const& right) { - return this->createPlus(left, addition, right, BaseExpression::double_); - } - /*! - * Create a new integer plus expression. If addition is true, it will be an addition, otherwise a subtraction. - * @param left Left operand. - * @param addition Flag for addition or subtraction. - * @param right Right operand. - * @returns Integer plus expression. - */ - std::shared_ptr<BaseExpression> createIntPlus(std::shared_ptr<BaseExpression> const& left, bool addition, std::shared_ptr<BaseExpression> const& right) { - return this->createPlus(left, addition, right, BaseExpression::int_); - } - - /*! - * Create a new integer multiplication expression. - * @param left Left operand. - * @param right Right operand. - * @returns Integer multiplication expression. - */ - std::shared_ptr<BaseExpression> createIntMult(std::shared_ptr<BaseExpression> const& left, std::shared_ptr<BaseExpression> const& right) { - return std::shared_ptr<BaseExpression>(new BinaryNumericalFunctionExpression(BaseExpression::int_, left->clone(), right->clone(), BinaryNumericalFunctionExpression::TIMES)); - } - - /*! - * Create a new integer multiplication expression. If multiplication is true, it will be an multiplication, otherwise a division. - * @param left Left operand. - * @param addition Flag for multiplication or division. - * @param right Right operand. - * @returns Integer multiplication expression. - */ - std::shared_ptr<BaseExpression> createDoubleMult(std::shared_ptr<BaseExpression> const& left, bool multiplication, std::shared_ptr<BaseExpression> const& right) { - if (multiplication) { - return std::shared_ptr<BaseExpression>(new BinaryNumericalFunctionExpression(BaseExpression::double_, left->clone(), right->clone(), BinaryNumericalFunctionExpression::TIMES)); - } else { - return std::shared_ptr<BaseExpression>(new BinaryNumericalFunctionExpression(BaseExpression::double_, left->clone(), right->clone(), BinaryNumericalFunctionExpression::DIVIDE)); - } - } - - /*! - * Creates an integer min/max expression. - * - * @param min Indicates whether the expression is min or max. - * @param left The left operand. - * @param right The right operand. - * @return An integer min/max expression. - */ - std::shared_ptr<BaseExpression> createIntMinMax(bool min, std::shared_ptr<BaseExpression> const& left, std::shared_ptr<BaseExpression> const& right) { - if (min) { - return std::shared_ptr<BaseExpression>(new BinaryNumericalFunctionExpression(BaseExpression::int_, left->clone(), right->clone(), BinaryNumericalFunctionExpression::MIN)); - } else { - return std::shared_ptr<BaseExpression>(new BinaryNumericalFunctionExpression(BaseExpression::int_, left->clone(), right->clone(), BinaryNumericalFunctionExpression::MAX)); - } - } - - /*! - * Creates an integer floor/ceil expression. - * - * @param floor Indicates whether the expression is a floor expression. - * @param operand The argument of the floor/ceil operation. - * @return An integer floor/ceil expression. - */ - std::shared_ptr<BaseExpression> createIntFloorCeil(bool floor, std::shared_ptr<BaseExpression> const& operand) { - if (floor) { - return std::shared_ptr<BaseExpression>(new UnaryNumericalFunctionExpression(BaseExpression::int_, operand->clone(), UnaryNumericalFunctionExpression::FLOOR)); - } else { - return std::shared_ptr<BaseExpression>(new UnaryNumericalFunctionExpression(BaseExpression::int_, operand->clone(), UnaryNumericalFunctionExpression::CEIL)); - } - } - - /*! - * Create a new binary relation expression. - * @param left Left operand. - * @param relationType Type of binary relation. - * @param right Right operand. - * @returns Binary relation expression. - */ - std::shared_ptr<BaseExpression> createRelation(std::shared_ptr<BaseExpression> const& left, BinaryRelationExpression::RelationType relationType, std::shared_ptr<BaseExpression> const& right) { - return std::shared_ptr<BaseExpression>(new BinaryRelationExpression(left->clone(), right->clone(), relationType)); - } - /*! - * Create a new negation expression. - * @param child Expression to be negated. - * @returns Negation expression. - */ - std::shared_ptr<BaseExpression> createNot(std::shared_ptr<BaseExpression> const& child) { - return std::shared_ptr<UnaryBooleanFunctionExpression>(new UnaryBooleanFunctionExpression(child->clone(), UnaryBooleanFunctionExpression::NOT)); - } - /*! - * Create a new And expression. - * @param left Left operand. - * @param right Right operand. - * @returns And expression. - */ - std::shared_ptr<BaseExpression> createAnd(std::shared_ptr<BaseExpression> const& left, std::shared_ptr<BaseExpression> const& right) { - return std::shared_ptr<BaseExpression>(new BinaryBooleanFunctionExpression(left->clone(), right->clone(), BinaryBooleanFunctionExpression::AND)); - } - /*! - * Create a new Or expression. - * @param left Left operand. - * @param right Right operand. - * @returns Or expression. - */ - std::shared_ptr<BaseExpression> createOr(std::shared_ptr<BaseExpression> const& left, std::shared_ptr<BaseExpression> const& right) { - return std::shared_ptr<BinaryBooleanFunctionExpression>(new BinaryBooleanFunctionExpression(left->clone(), right->clone(), BinaryBooleanFunctionExpression::OR)); - } - /*! - * Retrieve boolean variable by name. - * @param name Variable name. - * @returns Boolean variable. - */ - std::shared_ptr<BaseExpression> getBoolVariable(std::string const& name) { - return std::shared_ptr<BaseExpression>(new VariableExpression(*state->getBooleanVariableExpression(name))); - } - /*! - * Retrieve integer variable by name. - * @param name Variable name. - * @returns Integer variable. - */ - std::shared_ptr<BaseExpression> getIntVariable(std::string const& name) { - return std::shared_ptr<BaseExpression>(new VariableExpression(*state->getIntegerVariableExpression(name))); - } - - /*! - * Base method to switch to second run. This does nothing. - * Any subclass that needs to do something in order to proceed to the second run should override this method. - */ - virtual void prepareSecondRun() {} - - protected: - /*! - * Pointer to variable state. - */ - std::shared_ptr<VariableState> state; - - private: - static std::shared_ptr<T> instanceObject; - static bool inSecondRun; - }; - - template <typename T> - std::shared_ptr<T> BaseGrammar<T>::instanceObject; -} -} -} -#endif /* BASEGRAMMAR_H */ - diff --git a/src/parser/prismparser/BooleanExpressionGrammar.cpp b/src/parser/prismparser/BooleanExpressionGrammar.cpp deleted file mode 100644 index a7d9366f8..000000000 --- a/src/parser/prismparser/BooleanExpressionGrammar.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "BooleanExpressionGrammar.h" - -#include "IntegerExpressionGrammar.h" -#include "ConstBooleanExpressionGrammar.h" - -namespace storm { - namespace parser { - namespace prism { - - BooleanExpressionGrammar::BooleanExpressionGrammar(std::shared_ptr<VariableState> const& state) - : BooleanExpressionGrammar::base_type(booleanExpression), BaseGrammar(state) { - - booleanExpression %= orExpression; - booleanExpression.name("boolean expression"); - - orExpression = andExpression[qi::_val = qi::_1] >> *(qi::lit("|") >> andExpression)[qi::_val = phoenix::bind(&BaseGrammar::createOr, this, qi::_val, qi::_1)]; - orExpression.name("boolean expression"); - - andExpression = notExpression[qi::_val = qi::_1] >> *(qi::lit("&") >> notExpression)[qi::_val = phoenix::bind(&BaseGrammar::createAnd, this, qi::_val, qi::_1)]; - andExpression.name("boolean expression"); - - notExpression = atomicBooleanExpression[qi::_val = qi::_1] | (qi::lit("!") >> atomicBooleanExpression)[qi::_val = phoenix::bind(&BaseGrammar::createNot, this, qi::_1)]; - notExpression.name("boolean expression"); - - atomicBooleanExpression %= (relativeExpression | booleanVariableExpression | this->state->constantBooleanFormulas_ | this->state->booleanFormulas_ | qi::lit("(") >> booleanExpression >> qi::lit(")") | ConstBooleanExpressionGrammar::instance(this->state)); - atomicBooleanExpression.name("boolean expression"); - - relativeExpression = (IntegerExpressionGrammar::instance(this->state) >> relations_ >> IntegerExpressionGrammar::instance(this->state))[qi::_val = phoenix::bind(&BaseGrammar::createRelation, this, qi::_1, qi::_2, qi::_3)]; - relativeExpression.name("relative expression"); - - booleanVariableExpression = IdentifierGrammar::instance(this->state)[qi::_val = phoenix::bind(&BaseGrammar::getBoolVariable, this, qi::_1)]; - booleanVariableExpression.name("boolean variable"); - } - - void BooleanExpressionGrammar::prepareSecondRun() { - booleanVariableExpression %= this->state->booleanVariables_; - booleanVariableExpression.name("boolean variable"); - } - - } // namespace prism - } // namespace parser -} // namespace storm diff --git a/src/parser/prismparser/BooleanExpressionGrammar.h b/src/parser/prismparser/BooleanExpressionGrammar.h deleted file mode 100644 index 909219fa3..000000000 --- a/src/parser/prismparser/BooleanExpressionGrammar.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * File: BooleanExpressionGrammar.h - * Author: nafur - * - * Created on April 10, 2013, 6:27 PM - */ - -#ifndef BOOLEANEXPRESSIONGRAMMAR_H -#define BOOLEANEXPRESSIONGRAMMAR_H - -#include "Includes.h" -#include "VariableState.h" -#include "IdentifierGrammars.h" -#include "Tokens.h" - -#include <iostream> - -namespace storm { -namespace parser { -namespace prism { - -/*! - * This grammar parses (non constant) boolean expressions as used in prism models. - */ -class BooleanExpressionGrammar : public qi::grammar<Iterator, std::shared_ptr<BaseExpression>(), Skipper, Unused>, public BaseGrammar<BooleanExpressionGrammar> { -public: - BooleanExpressionGrammar(std::shared_ptr<VariableState> const& state); - /*! - * Switch to second run. - * Variable names may be any valid identifier in the first run, but only defined variables in the second run. - */ - virtual void prepareSecondRun(); - -private: - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper, Unused> booleanExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> orExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> andExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> notExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> atomicBooleanExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> relativeExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> booleanVariableExpression; - - /*! - * Parser relation operators. - */ - storm::parser::prism::relationalOperatorStruct relations_; -}; - - -} -} -} - -#endif /* BOOLEANEXPRESSIONGRAMMAR_H */ diff --git a/src/parser/prismparser/ConstBooleanExpressionGrammar.cpp b/src/parser/prismparser/ConstBooleanExpressionGrammar.cpp deleted file mode 100644 index a5ce0567d..000000000 --- a/src/parser/prismparser/ConstBooleanExpressionGrammar.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "ConstBooleanExpressionGrammar.h" - -#include "ConstIntegerExpressionGrammar.h" - -namespace storm { -namespace parser { -namespace prism { - - ConstBooleanExpressionGrammar::ConstBooleanExpressionGrammar(std::shared_ptr<VariableState> const& state) - : ConstBooleanExpressionGrammar::base_type(constantBooleanExpression), BaseGrammar(state) { - - constantBooleanExpression %= constantOrExpression; - constantBooleanExpression.name("constant boolean expression"); - - constantOrExpression = constantAndExpression[qi::_val = qi::_1] >> *(qi::lit("|") >> constantAndExpression)[qi::_val = phoenix::bind(&BaseGrammar::createOr, this, qi::_val, qi::_1)]; - constantOrExpression.name("constant boolean expression"); - - constantAndExpression = constantNotExpression[qi::_val = qi::_1] >> *(qi::lit("&") >> constantNotExpression)[qi::_val = phoenix::bind(&BaseGrammar::createAnd, this, qi::_val, qi::_1)]; - constantAndExpression.name("constant boolean expression"); - - constantNotExpression = constantAtomicBooleanExpression[qi::_val = qi::_1] | (qi::lit("!") >> constantAtomicBooleanExpression)[qi::_val = phoenix::bind(&BaseGrammar::createNot, this, qi::_1)]; - constantNotExpression.name("constant boolean expression"); - - constantAtomicBooleanExpression %= (constantRelativeExpression | this->state->constantBooleanFormulas_ | qi::lit("(") >> constantBooleanExpression >> qi::lit(")") | booleanLiteralExpression | booleanConstantExpression); - constantAtomicBooleanExpression.name("constant boolean expression"); - - constantRelativeExpression = (ConstIntegerExpressionGrammar::instance(this->state) >> relations_ >> ConstIntegerExpressionGrammar::instance(this->state))[qi::_val = phoenix::bind(&BaseGrammar::createRelation, this, qi::_1, qi::_2, qi::_3)]; - constantRelativeExpression.name("constant boolean expression"); - - booleanConstantExpression %= (this->state->booleanConstants_ | booleanLiteralExpression); - booleanConstantExpression.name("boolean constant or literal"); - - booleanLiteralExpression = qi::bool_[qi::_val = phoenix::bind(&BaseGrammar::createBoolLiteral, this, qi::_1)]; - booleanLiteralExpression.name("boolean literal"); - } -} -} -} diff --git a/src/parser/prismparser/ConstBooleanExpressionGrammar.h b/src/parser/prismparser/ConstBooleanExpressionGrammar.h deleted file mode 100644 index 8fea3ce95..000000000 --- a/src/parser/prismparser/ConstBooleanExpressionGrammar.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * File: ConstBooleanExpressionGrammar.h - * Author: nafur - * - * Created on April 10, 2013, 6:34 PM - */ - -#ifndef CONSTBOOLEANEXPRESSIONGRAMMAR_H -#define CONSTBOOLEANEXPRESSIONGRAMMAR_H - -#include "Includes.h" -#include "VariableState.h" -#include "IdentifierGrammars.h" -#include "Tokens.h" - -namespace storm { -namespace parser { -namespace prism { - -/*! - * This grammar parses constant boolean expression as used in prism models. - */ -class ConstBooleanExpressionGrammar : public qi::grammar<Iterator, std::shared_ptr<BaseExpression>(), Skipper, Unused>, public BaseGrammar<ConstBooleanExpressionGrammar> { -public: - ConstBooleanExpressionGrammar(std::shared_ptr<VariableState> const& state); - - -private: - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper, Unused> constantBooleanExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> constantOrExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> constantAndExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> constantNotExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> constantAtomicBooleanExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> constantRelativeExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> booleanConstantExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> booleanLiteralExpression; - - storm::parser::prism::relationalOperatorStruct relations_; -}; - - -} -} -} - -#endif /* CONSTBOOLEANEXPRESSIONGRAMMAR_H */ - diff --git a/src/parser/prismparser/ConstDoubleExpressionGrammar.cpp b/src/parser/prismparser/ConstDoubleExpressionGrammar.cpp deleted file mode 100644 index 18cd5ae31..000000000 --- a/src/parser/prismparser/ConstDoubleExpressionGrammar.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "ConstDoubleExpressionGrammar.h" - -namespace storm { -namespace parser { -namespace prism { - -ConstDoubleExpressionGrammar::ConstDoubleExpressionGrammar(std::shared_ptr<VariableState> const& state) - : ConstDoubleExpressionGrammar::base_type(constantDoubleExpression), BaseGrammar(state) { - - constantDoubleExpression %= constantDoublePlusExpression; - constantDoubleExpression.name("constant double expression"); - - constantDoublePlusExpression %= constantDoubleMultExpression[qi::_val = qi::_1] >> *((qi::lit("+")[qi::_a = true] | qi::lit("-")[qi::_a = false]) >> constantDoubleMultExpression) - [qi::_val = phoenix::bind(&BaseGrammar::createDoublePlus, this, qi::_val, qi::_a, qi::_1)]; - constantDoublePlusExpression.name("constant double expression"); - - constantDoubleMultExpression %= constantAtomicDoubleExpression[qi::_val = qi::_1] >> *((qi::lit("*")[qi::_a = true] | qi::lit("/")[qi::_a = false]) >> constantAtomicDoubleExpression) - [qi::_val = phoenix::bind(&BaseGrammar::createDoubleMult, this, qi::_val, qi::_a, qi::_1)]; - constantDoubleMultExpression.name("constant double expression"); - - constantAtomicDoubleExpression %= (qi::lit("(") >> constantDoubleExpression >> qi::lit(")") | this->state->constantDoubleFormulas_ | this->state->constantIntegerFormulas_ | doubleConstantExpression); - constantAtomicDoubleExpression.name("constant double expression"); - - doubleConstantExpression %= (this->state->doubleConstants_ | this->state->integerConstants_ | doubleLiteralExpression); - doubleConstantExpression.name("double constant or literal"); - - doubleLiteralExpression = qi::double_[qi::_val = phoenix::bind(&BaseGrammar::createDoubleLiteral, this, qi::_1)]; - doubleLiteralExpression.name("double literal"); -} - - -} -} -} diff --git a/src/parser/prismparser/ConstDoubleExpressionGrammar.h b/src/parser/prismparser/ConstDoubleExpressionGrammar.h deleted file mode 100644 index 41c503731..000000000 --- a/src/parser/prismparser/ConstDoubleExpressionGrammar.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * File: ConstDoubleExpressionGrammar.h - * Author: nafur - * - * Created on April 10, 2013, 7:04 PM - */ - -#ifndef CONSTDOUBLEEXPRESSIONGRAMMAR_H -#define CONSTDOUBLEEXPRESSIONGRAMMAR_H - -#include "Includes.h" -#include "VariableState.h" -#include "IdentifierGrammars.h" - -namespace storm { - namespace parser { - namespace prism { - - /*! - * This grammar parses constant double expressions as used in prism models. - */ - class ConstDoubleExpressionGrammar : public qi::grammar<Iterator, std::shared_ptr<BaseExpression>(), Skipper, Unused>, public BaseGrammar<ConstDoubleExpressionGrammar> { - public: - ConstDoubleExpressionGrammar(std::shared_ptr<VariableState> const& state); - - private: - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper, Unused> constantDoubleExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), qi::locals<bool>, Skipper> constantDoublePlusExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), qi::locals<bool>, Skipper> constantDoubleMultExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> constantAtomicDoubleExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> doubleConstantExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> doubleLiteralExpression; - }; - - - } - } -} - -#endif /* CONSTDOUBLEEXPRESSIONGRAMMAR_H */ - diff --git a/src/parser/prismparser/ConstIntegerExpressionGrammar.cpp b/src/parser/prismparser/ConstIntegerExpressionGrammar.cpp deleted file mode 100644 index 9ebebebc7..000000000 --- a/src/parser/prismparser/ConstIntegerExpressionGrammar.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "ConstIntegerExpressionGrammar.h" - -namespace storm { - namespace parser { - namespace prism { - - - ConstIntegerExpressionGrammar::ConstIntegerExpressionGrammar(std::shared_ptr<VariableState> const& state) - : ConstIntegerExpressionGrammar::base_type(constantIntegerExpression), BaseGrammar(state) { - - constantIntegerExpression %= constantIntegerPlusExpression; - constantIntegerExpression.name("constant integer expression"); - - constantIntegerPlusExpression = constantIntegerMultExpression[qi::_val = qi::_1] >> *((qi::lit("+")[qi::_a = true] | qi::lit("-")[qi::_a = false]) >> constantIntegerMultExpression) - [qi::_val = phoenix::bind(&BaseGrammar::createIntPlus, this, qi::_val, qi::_a, qi::_1)]; - constantIntegerPlusExpression.name("constant integer expression"); - - constantIntegerMultExpression %= constantAtomicIntegerExpression[qi::_val = qi::_1] >> *(qi::lit("*") >> constantAtomicIntegerExpression) - [qi::_val = phoenix::bind(&BaseGrammar::createIntMult, this, qi::_val, qi::_1)]; - constantIntegerMultExpression.name("constant integer expression"); - - constantAtomicIntegerExpression %= (constantIntegerMinMaxExpression | constantIntegerFloorCeilExpression | this->state->constantIntegerFormulas_ | qi::lit("(") >> constantIntegerExpression >> qi::lit(")") | integerConstantExpression); - constantAtomicIntegerExpression.name("constant integer expression"); - - constantIntegerMinMaxExpression = ((qi::lit("min")[qi::_a = true] | qi::lit("max")[qi::_a = false]) >> qi::lit("(") >> constantIntegerExpression >> qi::lit(",") >> constantIntegerExpression >> qi::lit(")"))[qi::_val = phoenix::bind(&BaseGrammar::createIntMinMax, this, qi::_a, qi::_1, qi::_2)]; - constantIntegerMinMaxExpression.name("integer min/max expression"); - - constantIntegerFloorCeilExpression = ((qi::lit("floor")[qi::_a = true] | qi::lit("ceil")[qi::_a = false]) >> qi::lit("(") >> constantIntegerExpression >> qi::lit(")"))[qi::_val = phoenix::bind(&BaseGrammar::createIntFloorCeil, this, qi::_a, qi::_1)]; - constantIntegerFloorCeilExpression.name("integer floor/ceil expression"); - - integerConstantExpression %= (this->state->integerConstants_ | integerLiteralExpression); - integerConstantExpression.name("integer constant or literal"); - - integerLiteralExpression = qi::int_[qi::_val = phoenix::bind(&BaseGrammar::createIntLiteral, this, qi::_1)]; - integerLiteralExpression.name("integer literal"); - - } - - } // namespace prism - } // namespace parser -} // namespace storm diff --git a/src/parser/prismparser/ConstIntegerExpressionGrammar.h b/src/parser/prismparser/ConstIntegerExpressionGrammar.h deleted file mode 100644 index 2b6a08cae..000000000 --- a/src/parser/prismparser/ConstIntegerExpressionGrammar.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * File: ConstIntegerExpressionGrammar.h - * Author: nafur - * - * Created on April 10, 2013, 6:02 PM - */ - -#ifndef CONSTINTEGEREXPRESSIONGRAMMAR_H -#define CONSTINTEGEREXPRESSIONGRAMMAR_H - -#include "Includes.h" -#include "VariableState.h" -#include "IdentifierGrammars.h" - -namespace storm { - namespace parser { - namespace prism { - - /*! - * This grammar parses constant integer expressions as used in prism models. - */ - class ConstIntegerExpressionGrammar : public qi::grammar<Iterator, std::shared_ptr<BaseExpression>(), Skipper, Unused>, public BaseGrammar<ConstIntegerExpressionGrammar> { - public: - ConstIntegerExpressionGrammar(std::shared_ptr<VariableState> const& state); - - private: - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper, Unused> constantIntegerExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), qi::locals<bool>, Skipper> constantIntegerPlusExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> constantIntegerMultExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> constantAtomicIntegerExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> integerConstantExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> integerLiteralExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), qi::locals<bool>, Skipper> constantIntegerMinMaxExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), qi::locals<bool>, Skipper> constantIntegerFloorCeilExpression; - }; - - - } - } -} - -#endif /* CONSTINTEGEREXPRESSIONGRAMMAR_H */ diff --git a/src/parser/prismparser/IdentifierGrammars.cpp b/src/parser/prismparser/IdentifierGrammars.cpp deleted file mode 100644 index ec6f14931..000000000 --- a/src/parser/prismparser/IdentifierGrammars.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "IdentifierGrammars.h" - -namespace storm { - namespace parser { - namespace prism { - - IdentifierGrammar::IdentifierGrammar(std::shared_ptr<VariableState> const& state) - : IdentifierGrammar::base_type(identifierName), BaseGrammar(state) { - - identifierName %= qi::as_string[qi::raw[qi::lexeme[((qi::alpha | qi::char_('_')) >> *(qi::alnum | qi::char_('_')))]]][ qi::_pass = phoenix::bind(&VariableState::isIdentifier, this->state.get(), qi::_1) ]; - identifierName.name("identifier"); - } - - FreeIdentifierGrammar::FreeIdentifierGrammar(std::shared_ptr<VariableState> const& state) - : FreeIdentifierGrammar::base_type(freeIdentifierName), BaseGrammar(state) { - - freeIdentifierName %= qi::as_string[qi::raw[qi::lexeme[((qi::alpha | qi::char_('_')) >> *(qi::alnum | qi::char_('_')))]]][ qi::_pass = phoenix::bind(&VariableState::isFreeIdentifier, this->state.get(), qi::_1) ]; - freeIdentifierName.name("identifier"); - } - - } - } -} \ No newline at end of file diff --git a/src/parser/prismparser/IdentifierGrammars.h b/src/parser/prismparser/IdentifierGrammars.h deleted file mode 100644 index ccb14bbc1..000000000 --- a/src/parser/prismparser/IdentifierGrammars.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * File: Keywords.h - * Author: nafur - * - * Created on April 10, 2013, 6:03 PM - */ - -#ifndef IDENTIFIERGRAMMARS_H -#define IDENTIFIERGRAMMARS_H - -#include "Includes.h" -#include "BaseGrammar.h" -#include "VariableState.h" - -namespace storm { - namespace parser { - namespace prism { - - /*! - * This grammar parses a (possibly used) identifier as used in a prism models. - */ - class IdentifierGrammar : public qi::grammar<Iterator, std::string(), Skipper, Unused>, public BaseGrammar<IdentifierGrammar> { - public: - IdentifierGrammar(std::shared_ptr<VariableState> const& state); - private: - qi::rule<Iterator, std::string(), Skipper> identifierName; - }; - - /*! - * This grammar parses an used identifier as used in a prism models. - */ - class FreeIdentifierGrammar : public qi::grammar<Iterator, std::string(), Skipper, Unused>, public BaseGrammar<IdentifierGrammar> { - public: - FreeIdentifierGrammar(std::shared_ptr<VariableState> const& state); - private: - qi::rule<Iterator, std::string(), Skipper> freeIdentifierName; - }; - } - } -} -#endif /* IDENTIFIERGRAMMARS_H */ - diff --git a/src/parser/prismparser/Includes.h b/src/parser/prismparser/Includes.h deleted file mode 100644 index 49a720dad..000000000 --- a/src/parser/prismparser/Includes.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * File: Includes - * Author: Gereon Kremer - * - * Created on April 10, 2013, 4:46 PM - */ - -#ifndef BOOSTINCLUDES_H -#define BOOSTINCLUDES_H - -// Used for Boost spirit. -#include <boost/typeof/typeof.hpp> -#include <boost/spirit/include/qi.hpp> -#include <boost/spirit/include/phoenix.hpp> - -// Include headers for spirit iterators. Needed for diagnostics and input stream iteration. -#include <boost/spirit/include/classic_position_iterator.hpp> -#include <boost/spirit/include/support_multi_pass.hpp> - -namespace qi = boost::spirit::qi; -namespace phoenix = boost::phoenix; - -typedef std::string::const_iterator BaseIteratorType; -typedef boost::spirit::classic::position_iterator2<BaseIteratorType> PositionIteratorType; -typedef PositionIteratorType Iterator; -typedef BOOST_TYPEOF(boost::spirit::ascii::space | qi::lit("//") >> *(qi::char_ - qi::eol) >> qi::eol) Skipper; -typedef BOOST_TYPEOF(qi::lit("//") >> *(qi::char_ - qi::eol) >> qi::eol | boost::spirit::ascii::space) Skipper2; -typedef boost::spirit::unused_type Unused; - -#include "src/ir/IR.h" -using namespace storm::ir; -using namespace storm::ir::expressions; - -#include "log4cplus/logger.h" -#include "log4cplus/loggingmacros.h" -extern log4cplus::Logger logger; - -#endif /* BOOSTINCLUDES_H */ - diff --git a/src/parser/prismparser/IntegerExpressionGrammar.cpp b/src/parser/prismparser/IntegerExpressionGrammar.cpp deleted file mode 100644 index 3bcfd4d4f..000000000 --- a/src/parser/prismparser/IntegerExpressionGrammar.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "IntegerExpressionGrammar.h" - -#include "IdentifierGrammars.h" -#include "ConstIntegerExpressionGrammar.h" - -namespace storm { - namespace parser { - namespace prism { - - IntegerExpressionGrammar::IntegerExpressionGrammar(std::shared_ptr<VariableState> const& state) - : IntegerExpressionGrammar::base_type(integerExpression), BaseGrammar(state) { - - integerExpression %= integerPlusExpression; - integerExpression.name("integer expression"); - - integerPlusExpression = integerMultExpression[qi::_val = qi::_1] >> *((qi::lit("+")[qi::_a = true] | qi::lit("-")[qi::_a = false]) >> integerMultExpression)[qi::_val = phoenix::bind(&BaseGrammar::createIntPlus, this, qi::_val, qi::_a, qi::_1)]; - integerPlusExpression.name("integer expression"); - - integerMultExpression %= atomicIntegerExpression[qi::_val = qi::_1] >> *(qi::lit("*") >> atomicIntegerExpression[qi::_val = phoenix::bind(&BaseGrammar::createIntMult, this, qi::_val, qi::_1)]); - integerMultExpression.name("integer expression"); - - atomicIntegerExpression %= (integerMinMaxExpression | integerFloorCeilExpression | this->state->constantIntegerFormulas_ | this->state->integerFormulas_ | qi::lit("(") >> integerExpression >> qi::lit(")") | integerVariableExpression | ConstIntegerExpressionGrammar::instance(this->state)); - atomicIntegerExpression.name("integer expression"); - - integerMinMaxExpression = ((qi::lit("min")[qi::_a = true] | qi::lit("max")[qi::_a = false]) >> qi::lit("(") >> integerExpression >> qi::lit(",") >> integerExpression >> qi::lit(")"))[qi::_val = phoenix::bind(&BaseGrammar::createIntMinMax, this, qi::_a, qi::_1, qi::_2)]; - integerMinMaxExpression.name("integer min/max expression"); - - integerFloorCeilExpression = ((qi::lit("floor")[qi::_a = true] | qi::lit("ceil")[qi::_a = false]) >> qi::lit("(") >> integerExpression >> qi::lit(")"))[qi::_val = phoenix::bind(&BaseGrammar::createIntFloorCeil, this, qi::_a, qi::_1)]; - integerFloorCeilExpression.name("integer floor/ceil expression"); - - integerVariableExpression = IdentifierGrammar::instance(this->state)[qi::_val = phoenix::bind(&BaseGrammar::getIntVariable, this, qi::_1)]; - integerVariableExpression.name("integer variable"); - } - - void IntegerExpressionGrammar::prepareSecondRun() { - integerVariableExpression %= this->state->integerVariables_; - integerVariableExpression.name("integer variable"); - } - - } - } -} diff --git a/src/parser/prismparser/IntegerExpressionGrammar.h b/src/parser/prismparser/IntegerExpressionGrammar.h deleted file mode 100644 index 7531ef36e..000000000 --- a/src/parser/prismparser/IntegerExpressionGrammar.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * File: IntegerExpressionGrammar.h - * Author: nafur - * - * Created on April 10, 2013, 4:39 PM - */ - -#ifndef INTEGEREXPRESSIONGRAMMAR_H -#define INTEGEREXPRESSIONGRAMMAR_H - -#include "src/ir/IR.h" -#include "VariableState.h" -#include "Includes.h" -#include "IdentifierGrammars.h" - -#include <memory> - -namespace storm { - namespace parser { - namespace prism { - - /*! - * This grammar parses a (non constant) integer expressions as used in prism models. - */ - class IntegerExpressionGrammar : public qi::grammar<Iterator, std::shared_ptr<BaseExpression>(), Skipper, Unused>, public BaseGrammar<IntegerExpressionGrammar> { - public: - IntegerExpressionGrammar(std::shared_ptr<VariableState> const& state); - - /*! - * Switch to second run. - * Variable names may be any valid identifier in the first run, but only defined variables in the second run. - */ - virtual void prepareSecondRun(); - - private: - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper, Unused> integerExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), qi::locals<bool>, Skipper> integerPlusExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> integerMultExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> atomicIntegerExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> integerVariableExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), qi::locals<bool>, Skipper> integerMinMaxExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), qi::locals<bool>, Skipper> integerFloorCeilExpression; - }; - - } - } -} - -#endif /* INTEGEREXPRESSIONGRAMMAR_H */ - diff --git a/src/parser/prismparser/PrismGrammar.cpp b/src/parser/prismparser/PrismGrammar.cpp deleted file mode 100644 index b6de4f0f4..000000000 --- a/src/parser/prismparser/PrismGrammar.cpp +++ /dev/null @@ -1,295 +0,0 @@ -/* - * PrismGrammar.cpp - * - * Created on: 11.01.2013 - * Author: chris - */ - -// Needed for file IO. -#include <fstream> -#include <iomanip> -#include <limits> - -#include "PrismGrammar.h" - -#include "src/utility/OsDetection.h" - -#include "src/parser/prismparser/Includes.h" -#include "src/parser/prismparser/BooleanExpressionGrammar.h" -#include "src/parser/prismparser/ConstBooleanExpressionGrammar.h" -#include "src/parser/prismparser/ConstDoubleExpressionGrammar.h" -#include "src/parser/prismparser/ConstIntegerExpressionGrammar.h" -#include "src/parser/prismparser/IntegerExpressionGrammar.h" -#include "src/parser/prismparser/IdentifierGrammars.h" -#include "src/parser/prismparser/VariableState.h" -#include "src/exceptions/InvalidArgumentException.h" - -#include "log4cplus/logger.h" -#include "log4cplus/loggingmacros.h" -extern log4cplus::Logger logger; - -// Some typedefs and namespace definitions to reduce code size. -typedef std::string::const_iterator BaseIteratorType; -typedef boost::spirit::classic::position_iterator2<BaseIteratorType> PositionIteratorType; -namespace qi = boost::spirit::qi; -namespace phoenix = boost::phoenix; - -namespace storm { - namespace parser { - namespace prism { - - void PrismGrammar::addLabel(std::string const& name, std::shared_ptr<BaseExpression> const& value, std::map<std::string, std::unique_ptr<BaseExpression>>& nameToExpressionMap) { - this->state->labelNames_.add(name, name); - nameToExpressionMap[name] = value->clone(); - } - - void PrismGrammar::addIntegerAssignment(std::string const& variable, std::shared_ptr<BaseExpression> const& value, std::map<std::string, Assignment>& variableToAssignmentMap) { - this->state->assignedIntegerVariables_.add(variable, variable); - variableToAssignmentMap[variable] = Assignment(variable, value->clone()); - } - - void PrismGrammar::addBooleanAssignment(std::string const& variable, std::shared_ptr<BaseExpression> const& value, std::map<std::string, Assignment>& variableToAssigmentMap) { - this->state->assignedBooleanVariables_.add(variable, variable); - variableToAssigmentMap[variable] = Assignment(variable, value->clone()); - } - - void PrismGrammar::addUndefinedBooleanConstant(std::string const& name, std::map<std::string, std::unique_ptr<BooleanConstantExpression>>& nameToExpressionMap) { - this->state->booleanConstants_.add(name, std::shared_ptr<BaseExpression>(new BooleanConstantExpression(name))); - this->state->allConstantNames_.add(name, name); - nameToExpressionMap.emplace(name, std::unique_ptr<BooleanConstantExpression>(new BooleanConstantExpression(dynamic_cast<BooleanConstantExpression&>(*this->state->booleanConstants_.at(name))))); - } - - void PrismGrammar::addUndefinedIntegerConstant(std::string const& name, std::map<std::string, std::unique_ptr<IntegerConstantExpression>>& nameToExpressionMap) { - this->state->integerConstants_.add(name, std::shared_ptr<BaseExpression>(new IntegerConstantExpression(name))); - this->state->allConstantNames_.add(name, name); - nameToExpressionMap.emplace(name, std::unique_ptr<IntegerConstantExpression>(new IntegerConstantExpression(dynamic_cast<IntegerConstantExpression&>(*this->state->integerConstants_.at(name))))); - } - - void PrismGrammar::addUndefinedDoubleConstant(std::string const& name, std::map<std::string, std::unique_ptr<DoubleConstantExpression>>& nameToExpressionMap) { - this->state->doubleConstants_.add(name, std::shared_ptr<BaseExpression>(new DoubleConstantExpression(name))); - this->state->allConstantNames_.add(name, name); - nameToExpressionMap.emplace(name, std::unique_ptr<DoubleConstantExpression>(new DoubleConstantExpression(dynamic_cast<DoubleConstantExpression&>(*this->state->doubleConstants_.at(name))))); - } - - Module PrismGrammar::renameModule(std::string const& newName, std::string const& oldName, std::map<std::string, std::string> const& renaming) { - this->state->moduleNames_.add(newName, newName); - Module* old = this->moduleMap_.find(oldName); - if (old == nullptr) { - LOG4CPLUS_ERROR(logger, "Renaming module failed: module " << oldName << " does not exist."); - throw storm::exceptions::InvalidArgumentException() << "Renaming module failed: module " << oldName << " does not exist."; - } - Module res(*old, newName, renaming, *this->state); - this->moduleMap_.at(newName) = res; - return res; - } - - Module PrismGrammar::createModule(std::string const& name, std::vector<BooleanVariable> const& bools, std::vector<IntegerVariable> const& ints, std::map<std::string, uint_fast64_t> const& boolids, std::map<std::string, uint_fast64_t> const& intids, std::vector<storm::ir::Command> const& commands) { - this->state->moduleNames_.add(name, name); - Module res(name, bools, ints, boolids, intids, commands); - this->moduleMap_.at(name) = res; - return res; - } - - void PrismGrammar::createIntegerVariable(std::string const& name, std::shared_ptr<BaseExpression> const& lower, std::shared_ptr<BaseExpression> const& upper, std::shared_ptr<BaseExpression> const& init, std::vector<IntegerVariable>& vars, std::map<std::string, uint_fast64_t>& varids, bool isGlobalVariable) { - uint_fast64_t id = this->state->addIntegerVariable(name); - uint_fast64_t newLocalIndex = this->state->nextLocalIntegerVariableIndex; - vars.emplace_back(newLocalIndex, id, name, lower != nullptr ? lower->clone() : nullptr, upper != nullptr ? upper->clone() : nullptr, init != nullptr ? init->clone() : nullptr); - varids[name] = newLocalIndex; - ++this->state->nextLocalIntegerVariableIndex; - this->state->localIntegerVariables_.add(name, name); - if (isGlobalVariable) { - this->state->globalIntegerVariables_.add(name, name); - } - } - - void PrismGrammar::createBooleanVariable(std::string const& name, std::shared_ptr<BaseExpression> const& init, std::vector<BooleanVariable>& vars, std::map<std::string, uint_fast64_t>& varids, bool isGlobalVariable) { - uint_fast64_t id = this->state->addBooleanVariable(name); - uint_fast64_t newLocalIndex = this->state->nextLocalBooleanVariableIndex; - vars.emplace_back(newLocalIndex, id, name, init != nullptr ? init->clone() : nullptr); - varids[name] = newLocalIndex; - ++this->state->nextLocalBooleanVariableIndex; - this->state->localBooleanVariables_.add(name, name); - if (isGlobalVariable) { - this->state->globalBooleanVariables_.add(name, name); - } - } - - StateReward createStateReward(std::shared_ptr<BaseExpression> const& guard, std::shared_ptr<BaseExpression> const& reward) { - return StateReward(guard->clone(), reward->clone()); - } - TransitionReward createTransitionReward(std::string const& label, std::shared_ptr<BaseExpression> const& guard, std::shared_ptr<BaseExpression> const& reward) { - return TransitionReward(label, guard->clone(), reward->clone()); - } - void createRewardModel(std::string const& name, std::vector<StateReward>& stateRewards, std::vector<TransitionReward>& transitionRewards, std::map<std::string, RewardModel>& mapping) { - mapping[name] = RewardModel(name, stateRewards, transitionRewards); - } - Update PrismGrammar::createUpdate(std::shared_ptr<BaseExpression> const& likelihood, std::map<std::string, Assignment> const& bools, std::map<std::string, Assignment> const& ints) { - this->state->nextGlobalUpdateIndex++; - return Update(this->state->getNextGlobalUpdateIndex() - 1, likelihood != nullptr ? likelihood->clone() : nullptr, bools, ints); - } - Command PrismGrammar::createCommand(std::string const& label, std::shared_ptr<BaseExpression> const& guard, std::vector<Update> const& updates) { - this->state->nextGlobalCommandIndex++; - return Command(this->state->getNextGlobalCommandIndex() - 1, label, guard->clone(), updates); - } - Program createProgram( - Program::ModelType modelType, - std::map<std::string, std::unique_ptr<BooleanConstantExpression>> const& undefBoolConst, - std::map<std::string, std::unique_ptr<IntegerConstantExpression>> const& undefIntConst, - std::map<std::string, std::unique_ptr<DoubleConstantExpression>> const& undefDoubleConst, - GlobalVariableInformation const& globalVariableInformation, - std::vector<Module> const& modules, - std::map<std::string, RewardModel> const& rewards, - std::map<std::string, std::unique_ptr<BaseExpression>> const& labels) { - return Program(modelType, undefBoolConst, undefIntConst, undefDoubleConst, - globalVariableInformation.booleanVariables, globalVariableInformation.integerVariables, - globalVariableInformation.booleanVariableToIndexMap, - globalVariableInformation.integerVariableToIndexMap, modules, rewards, labels); - } - - PrismGrammar::PrismGrammar() : PrismGrammar::base_type(start), state(new VariableState()) { - - labelDefinition = (qi::lit("label") >> -qi::lit("\"") >> FreeIdentifierGrammar::instance(this->state) >> -qi::lit("\"") >> qi::lit("=") >> BooleanExpressionGrammar::instance(this->state) >> qi::lit(";")) - [phoenix::bind(&PrismGrammar::addLabel, this, qi::_1, qi::_2, qi::_r1)]; - labelDefinition.name("label declaration"); - labelDefinitionList %= *labelDefinition(qi::_r1); - labelDefinitionList.name("label declaration list"); - - // This block defines all entities that are needed for parsing a reward model. - stateRewardDefinition = (BooleanExpressionGrammar::instance(this->state) > qi::lit(":") > ConstDoubleExpressionGrammar::instance(this->state) >> qi::lit(";"))[qi::_val = phoenix::bind(&createStateReward, qi::_1, qi::_2)]; - stateRewardDefinition.name("state reward definition"); - transitionRewardDefinition = (qi::lit("[") > -(commandName[qi::_a = qi::_1]) > qi::lit("]") > BooleanExpressionGrammar::instance(this->state) > qi::lit(":") > ConstDoubleExpressionGrammar::instance(this->state) > qi::lit(";"))[qi::_val = phoenix::bind(&createTransitionReward, qi::_a, qi::_2, qi::_3)]; - transitionRewardDefinition.name("transition reward definition"); - rewardDefinition = (qi::lit("rewards") > qi::lit("\"") > FreeIdentifierGrammar::instance(this->state) > qi::lit("\"") > +(stateRewardDefinition[phoenix::push_back(qi::_a, qi::_1)] | transitionRewardDefinition[phoenix::push_back(qi::_b, qi::_1)]) >> qi::lit("endrewards")) - [phoenix::bind(&createRewardModel, qi::_1, qi::_a, qi::_b, qi::_r1)]; - rewardDefinition.name("reward definition"); - rewardDefinitionList = *rewardDefinition(qi::_r1); - rewardDefinitionList.name("reward definition list"); - - commandName %= this->state->commandNames_; - commandName.name("command name"); - unassignedLocalBooleanVariableName %= (this->state->localBooleanVariables_ | this->state->globalBooleanVariables_) - this->state->assignedBooleanVariables_; - unassignedLocalBooleanVariableName.name("unassigned local/global boolean variable"); - unassignedLocalIntegerVariableName %= (this->state->localIntegerVariables_ | this->state->globalIntegerVariables_) - this->state->assignedIntegerVariables_; - unassignedLocalIntegerVariableName.name("unassigned local/global integer variable"); - - // This block defines all entities that are needed for parsing a single command. - assignmentDefinition = - (qi::lit("(") >> unassignedLocalIntegerVariableName > qi::lit("'") > qi::lit("=") > IntegerExpressionGrammar::instance(this->state) > qi::lit(")"))[phoenix::bind(&PrismGrammar::addIntegerAssignment, this, qi::_1, qi::_2, qi::_r2)] | - (qi::lit("(") >> unassignedLocalBooleanVariableName > qi::lit("'") > qi::lit("=") > BooleanExpressionGrammar::instance(this->state) > qi::lit(")"))[phoenix::bind(&PrismGrammar::addBooleanAssignment, this, qi::_1, qi::_2, qi::_r1)]; - assignmentDefinition.name("assignment"); - assignmentDefinitionList = assignmentDefinition(qi::_r1, qi::_r2) % "&"; - assignmentDefinitionList.name("assignment list"); - updateDefinition = (((ConstDoubleExpressionGrammar::instance(this->state) >> qi::lit(":")) - | qi::attr(std::shared_ptr<BaseExpression>(new storm::ir::expressions::DoubleLiteralExpression(1))))[phoenix::clear(phoenix::ref(this->state->assignedBooleanVariables_)), phoenix::clear(phoenix::ref(this->state->assignedIntegerVariables_))] - >> assignmentDefinitionList(qi::_a, qi::_b))[qi::_val = phoenix::bind(&PrismGrammar::createUpdate, this, qi::_1, qi::_a, qi::_b)]; - updateDefinition.name("update"); - updateListDefinition = +updateDefinition % "+"; - updateListDefinition.name("update list"); - commandDefinition = ( - qi::lit("[") > -( - (FreeIdentifierGrammar::instance(this->state)[phoenix::bind(this->state->commandNames_.add, qi::_1, qi::_1)] | commandName)[qi::_a = qi::_1] - ) > qi::lit("]") > BooleanExpressionGrammar::instance(this->state) > qi::lit("->") > updateListDefinition > qi::lit(";") - )[qi::_val = phoenix::bind(&PrismGrammar::createCommand, this, qi::_a, qi::_2, qi::_3)]; - commandDefinition.name("command"); - - // This block defines all entities that are needed for parsing variable definitions. - booleanVariableDefinition = (FreeIdentifierGrammar::instance(this->state) >> qi::lit(":") >> qi::lit("bool") > -(qi::lit("init") > ConstBooleanExpressionGrammar::instance(this->state)[qi::_b = phoenix::construct<std::shared_ptr<BaseExpression>>(qi::_1)]) > qi::lit(";")) - [phoenix::bind(&PrismGrammar::createBooleanVariable, this, qi::_1, qi::_b, qi::_r1, qi::_r2, qi::_r3)]; - booleanVariableDefinition.name("boolean variable declaration"); - - integerVariableDefinition = (FreeIdentifierGrammar::instance(this->state) >> qi::lit(":") >> qi::lit("[") > ConstIntegerExpressionGrammar::instance(this->state) > qi::lit("..") > ConstIntegerExpressionGrammar::instance(this->state) > qi::lit("]") > -(qi::lit("init") > ConstIntegerExpressionGrammar::instance(this->state)[qi::_b = phoenix::construct<std::shared_ptr<BaseExpression>>(qi::_1)]) > qi::lit(";")) - [phoenix::bind(&PrismGrammar::createIntegerVariable, this, qi::_1, qi::_2, qi::_3, qi::_b, qi::_r1, qi::_r2, qi::_r3)]; - integerVariableDefinition.name("integer variable declaration"); - variableDefinition = (booleanVariableDefinition(qi::_r1, qi::_r3, false) | integerVariableDefinition(qi::_r2, qi::_r4, false)); - variableDefinition.name("variable declaration"); - - // This block defines all entities that are needed for parsing a module. - moduleDefinition = (qi::lit("module") >> FreeIdentifierGrammar::instance(this->state)[phoenix::bind(&VariableState::clearLocalVariables, *this->state)] - >> *(variableDefinition(qi::_a, qi::_b, qi::_c, qi::_d)) >> +commandDefinition > qi::lit("endmodule")) - [qi::_val = phoenix::bind(&PrismGrammar::createModule, this, qi::_1, qi::_a, qi::_b, qi::_c, qi::_d, qi::_2)]; - - moduleDefinition.name("module"); - moduleRenaming = (qi::lit("module") >> FreeIdentifierGrammar::instance(this->state) >> qi::lit("=") - > this->state->moduleNames_ > qi::lit("[") > *( - (IdentifierGrammar::instance(this->state) > qi::lit("=") > IdentifierGrammar::instance(this->state) >> -qi::lit(","))[phoenix::insert(qi::_a, phoenix::construct<std::pair<std::string,std::string>>(qi::_1, qi::_2))] - ) > qi::lit("]") > qi::lit("endmodule")) - [qi::_val = phoenix::bind(&PrismGrammar::renameModule, this, qi::_1, qi::_2, qi::_a)]; - moduleRenaming.name("renamed module"); - moduleDefinitionList %= +(moduleDefinition | moduleRenaming); - moduleDefinitionList.name("module list"); - - // This block defines all entities that are needed for parsing global variable definitions. - globalVariableDefinitionList = *(qi::lit("global") > (booleanVariableDefinition(bind(&GlobalVariableInformation::booleanVariables, qi::_r1), bind(&GlobalVariableInformation::booleanVariableToIndexMap, qi::_r1), true) | integerVariableDefinition(bind(&GlobalVariableInformation::integerVariables, qi::_r1), bind(&GlobalVariableInformation::integerVariableToIndexMap, qi::_r1), true))); - globalVariableDefinitionList.name("global variable declaration list"); - - // This block defines all entities that are needed for parsing constant definitions. - definedBooleanConstantDefinition = (qi::lit("const") >> qi::lit("bool") >> FreeIdentifierGrammar::instance(this->state) >> qi::lit("=") > ConstBooleanExpressionGrammar::instance(this->state) > qi::lit(";"))[phoenix::bind(this->state->booleanConstants_.add, qi::_1, qi::_2), phoenix::bind(this->state->allConstantNames_.add, qi::_1, qi::_1), qi::_val = qi::_2]; - definedBooleanConstantDefinition.name("defined boolean constant declaration"); - definedIntegerConstantDefinition = (qi::lit("const") >> qi::lit("int") >> FreeIdentifierGrammar::instance(this->state) >> qi::lit("=") >> ConstIntegerExpressionGrammar::instance(this->state) >> qi::lit(";"))[phoenix::bind(this->state->integerConstants_.add, qi::_1, qi::_2), phoenix::bind(this->state->allConstantNames_.add, qi::_1, qi::_1), qi::_val = qi::_2]; - definedIntegerConstantDefinition.name("defined integer constant declaration"); - definedDoubleConstantDefinition = (qi::lit("const") >> qi::lit("double") >> FreeIdentifierGrammar::instance(this->state) >> qi::lit("=") > ConstDoubleExpressionGrammar::instance(this->state) > qi::lit(";"))[phoenix::bind(this->state->doubleConstants_.add, qi::_1, qi::_2), phoenix::bind(this->state->allConstantNames_.add, qi::_1, qi::_1), qi::_val = qi::_2]; - definedDoubleConstantDefinition.name("defined double constant declaration"); - undefinedBooleanConstantDefinition = (qi::lit("const") >> qi::lit("bool") > FreeIdentifierGrammar::instance(this->state) > qi::lit(";"))[phoenix::bind(&PrismGrammar::addUndefinedBooleanConstant, this, qi::_1, qi::_r1)]; - undefinedBooleanConstantDefinition.name("undefined boolean constant declaration"); - undefinedIntegerConstantDefinition = (qi::lit("const") >> qi::lit("int") > FreeIdentifierGrammar::instance(this->state) > qi::lit(";"))[phoenix::bind(&PrismGrammar::addUndefinedIntegerConstant, this, qi::_1, qi::_r1)]; - undefinedIntegerConstantDefinition.name("undefined integer constant declaration"); - undefinedDoubleConstantDefinition = (qi::lit("const") >> qi::lit("double") > FreeIdentifierGrammar::instance(this->state) > qi::lit(";"))[phoenix::bind(&PrismGrammar::addUndefinedDoubleConstant, this, qi::_1, qi::_r1)]; - undefinedDoubleConstantDefinition.name("undefined double constant declaration"); - definedConstantDefinition %= (definedBooleanConstantDefinition | definedIntegerConstantDefinition | definedDoubleConstantDefinition); - definedConstantDefinition.name("defined constant declaration"); - undefinedConstantDefinition = (undefinedBooleanConstantDefinition(qi::_r1) | undefinedIntegerConstantDefinition(qi::_r2) | undefinedDoubleConstantDefinition(qi::_r3)); - undefinedConstantDefinition.name("undefined constant declaration"); - constantDefinitionList = *(definedConstantDefinition | undefinedConstantDefinition(qi::_r1, qi::_r2, qi::_r3)); - constantDefinitionList.name("constant declaration list"); - - constantBooleanFormulaDefinition = (qi::lit("formula") >> FreeIdentifierGrammar::instance(this->state) >> qi::lit("=") >> ConstBooleanExpressionGrammar::instance(this->state) >> qi::lit(";"))[phoenix::bind(this->state->constantBooleanFormulas_.add, qi::_1, qi::_2)]; - constantBooleanFormulaDefinition.name("constant boolean formula definition"); - booleanFormulaDefinition = (qi::lit("formula") >> FreeIdentifierGrammar::instance(this->state) >> qi::lit("=") >> BooleanExpressionGrammar::instance(this->state) >> qi::lit(";"))[phoenix::bind(this->state->booleanFormulas_.add, qi::_1, qi::_2)]; - booleanFormulaDefinition.name("boolean formula definition"); - constantIntegerFormulaDefinition = (qi::lit("formula") >> FreeIdentifierGrammar::instance(this->state) >> qi::lit("=") >> ConstIntegerExpressionGrammar::instance(this->state) >> qi::lit(";"))[phoenix::bind(this->state->constantIntegerFormulas_.add, qi::_1, qi::_2)]; - constantIntegerFormulaDefinition.name("constant integer formula definition"); - integerFormulaDefinition = (qi::lit("formula") >> FreeIdentifierGrammar::instance(this->state) >> qi::lit("=") >> IntegerExpressionGrammar::instance(this->state) >> qi::lit(";"))[phoenix::bind(this->state->integerFormulas_.add, qi::_1, qi::_2)]; - integerFormulaDefinition.name("integer formula definition"); - constantDoubleFormulaDefinition = (qi::lit("formula") >> FreeIdentifierGrammar::instance(this->state) >> qi::lit("=") >> ConstDoubleExpressionGrammar::instance(this->state) >> qi::lit(";"))[phoenix::bind(this->state->constantDoubleFormulas_.add, qi::_1, qi::_2)]; - constantDoubleFormulaDefinition.name("constant double formula definition"); - formulaDefinition = constantBooleanFormulaDefinition | booleanFormulaDefinition | constantIntegerFormulaDefinition | integerFormulaDefinition | constantDoubleFormulaDefinition; - formulaDefinition.name("formula definition"); - formulaDefinitionList = *formulaDefinition; - formulaDefinitionList.name("formula definition list"); - - // This block defines all entities that are needed for parsing a program. - modelTypeDefinition = modelType_; - modelTypeDefinition.name("model type"); - start = (qi::eps > - modelTypeDefinition > - constantDefinitionList(qi::_a, qi::_b, qi::_c) > - formulaDefinitionList > - globalVariableDefinitionList(qi::_d) > - moduleDefinitionList > - rewardDefinitionList(qi::_e) > - labelDefinitionList(qi::_f))[qi::_val = phoenix::bind(&createProgram, qi::_1, qi::_a, qi::_b, qi::_c, qi::_d, qi::_2, qi::_e, qi::_f)]; - start.name("probabilistic program declaration"); - } - - void PrismGrammar::prepareForSecondRun() { - LOG4CPLUS_INFO(logger, "Preparing parser for second run."); - this->state->prepareForSecondRun(); - BooleanExpressionGrammar::secondRun(); - ConstBooleanExpressionGrammar::secondRun(); - ConstDoubleExpressionGrammar::secondRun(); - ConstIntegerExpressionGrammar::secondRun(); - IntegerExpressionGrammar::secondRun(); - } - - void PrismGrammar::resetGrammars() { - LOG4CPLUS_INFO(logger, "Resetting grammars."); - BooleanExpressionGrammar::resetInstance(); - ConstBooleanExpressionGrammar::resetInstance(); - ConstDoubleExpressionGrammar::resetInstance(); - ConstIntegerExpressionGrammar::resetInstance(); - IntegerExpressionGrammar::resetInstance(); - } - - } // namespace prism - } // namespace parser -} // namespace storm diff --git a/src/parser/prismparser/PrismGrammar.h b/src/parser/prismparser/PrismGrammar.h deleted file mode 100644 index 57ba55c5b..000000000 --- a/src/parser/prismparser/PrismGrammar.h +++ /dev/null @@ -1,267 +0,0 @@ -/* - * File: PrismGrammar.h - * Author: nafur - * - * Created on April 30, 2013, 5:20 PM - */ - -#ifndef STORM_PARSER_PRISMPARSER_PRISMGRAMMAR_H -#define STORM_PARSER_PRISMPARSER_PRISMGRAMMAR_H - -// All classes of the intermediate representation are used. -#include "src/ir/IR.h" -#include "src/parser/prismparser/Includes.h" -#include "src/parser/prismparser/Tokens.h" -#include "src/parser/prismparser/IdentifierGrammars.h" -#include "src/parser/prismparser/VariableState.h" -#include "src/parser/prismparser/ConstBooleanExpressionGrammar.h" -#include "src/parser/prismparser/ConstDoubleExpressionGrammar.h" -#include "src/parser/prismparser/ConstIntegerExpressionGrammar.h" -#include "src/parser/prismparser/BooleanExpressionGrammar.h" -#include "src/parser/prismparser/IntegerExpressionGrammar.h" - -// Used for file input. -#include <istream> -#include <memory> - -namespace storm { - namespace parser { - namespace prism { - - using namespace storm::ir; - using namespace storm::ir::expressions; - - struct GlobalVariableInformation { - std::vector<BooleanVariable> booleanVariables; - std::vector<IntegerVariable> integerVariables; - std::map<std::string, uint_fast64_t> booleanVariableToIndexMap; - std::map<std::string, uint_fast64_t> integerVariableToIndexMap; - }; - - /*! - * The Boost spirit grammar for the PRISM language. Returns the intermediate representation of - * the input that complies with the PRISM syntax. - */ - class PrismGrammar : public qi::grammar< - Iterator, - Program(), - qi::locals< - std::map<std::string, std::unique_ptr<BooleanConstantExpression>>, - std::map<std::string, std::unique_ptr<IntegerConstantExpression>>, - std::map<std::string, std::unique_ptr<DoubleConstantExpression>>, - GlobalVariableInformation, - std::map<std::string, RewardModel>, - std::map<std::string, std::unique_ptr<BaseExpression>> - >, - Skipper> { - public: - /*! - * Default constructor that creates an empty and functional grammar. - */ - PrismGrammar(); - - /*! - * Puts all sub-grammars into the mode for performing the second run. A two-run model was chosen - * because modules can involve variables that are only declared afterwards, so the first run - * creates all variables and the second one tries to parse the full model. - */ - void prepareForSecondRun(); - - /*! - * Resets all sub-grammars, i.e. puts them into an initial state. - */ - void resetGrammars(); - - private: - - std::shared_ptr<storm::parser::prism::VariableState> state; - struct qi::symbols<char, Module> moduleMap_; - - // The starting point of the grammar. - qi::rule< - Iterator, - Program(), - qi::locals< - std::map<std::string, std::unique_ptr<BooleanConstantExpression>>, - std::map<std::string, std::unique_ptr<IntegerConstantExpression>>, - std::map<std::string, std::unique_ptr<DoubleConstantExpression>>, - GlobalVariableInformation, - std::map<std::string, RewardModel>, - std::map<std::string, std::unique_ptr<BaseExpression>> - >, - Skipper> start; - qi::rule<Iterator, Program::ModelType(), Skipper> modelTypeDefinition; - qi::rule<Iterator, qi::unused_type(std::map<std::string, std::unique_ptr<BooleanConstantExpression>>&, std::map<std::string, std::unique_ptr<IntegerConstantExpression>>&, std::map<std::string, std::unique_ptr<DoubleConstantExpression>>&), Skipper> constantDefinitionList; - qi::rule<Iterator, std::vector<Module>(), Skipper> moduleDefinitionList; - - // Rules for global variable definitions - qi::rule<Iterator, qi::unused_type(GlobalVariableInformation&), Skipper> globalVariableDefinitionList; - - // Rules for module definition. - qi::rule<Iterator, Module(), qi::locals<std::vector<BooleanVariable>, std::vector<IntegerVariable>, std::map<std::string, uint_fast64_t>, std::map<std::string, uint_fast64_t>>, Skipper> moduleDefinition; - qi::rule<Iterator, Module(), qi::locals<std::map<std::string, std::string>>, Skipper> moduleRenaming; - - // Rules for variable definitions. - qi::rule<Iterator, qi::unused_type(std::vector<BooleanVariable>&, std::vector<IntegerVariable>&, std::map<std::string, uint_fast64_t>&, std::map<std::string, uint_fast64_t>&), Skipper> variableDefinition; - qi::rule<Iterator, qi::unused_type(std::vector<BooleanVariable>&, std::map<std::string, uint_fast64_t>&, bool), qi::locals<uint_fast64_t, std::shared_ptr<BaseExpression>>, Skipper> booleanVariableDefinition; - qi::rule<Iterator, qi::unused_type(std::vector<IntegerVariable>&, std::map<std::string, uint_fast64_t>&, bool), qi::locals<uint_fast64_t, std::shared_ptr<BaseExpression>>, Skipper> integerVariableDefinition; - - // Rules for command definitions. - qi::rule<Iterator, Command(), qi::locals<std::string>, Skipper> commandDefinition; - qi::rule<Iterator, std::vector<Update>(), Skipper> updateListDefinition; - qi::rule<Iterator, Update(), qi::locals<std::map<std::string, Assignment>, std::map<std::string, Assignment>>, Skipper> updateDefinition; - qi::rule<Iterator, qi::unused_type(std::map<std::string, Assignment>&, std::map<std::string, Assignment>&), Skipper> assignmentDefinitionList; - qi::rule<Iterator, qi::unused_type(std::map<std::string, Assignment>&, std::map<std::string, Assignment>&), Skipper> assignmentDefinition; - - // Rules for variable/command names. - qi::rule<Iterator, std::string(), Skipper> commandName; - qi::rule<Iterator, std::string(), Skipper> unassignedLocalBooleanVariableName; - qi::rule<Iterator, std::string(), Skipper> unassignedLocalIntegerVariableName; - - // Rules for reward definitions. - qi::rule<Iterator, qi::unused_type(std::map<std::string, RewardModel>&), Skipper> rewardDefinitionList; - qi::rule<Iterator, qi::unused_type(std::map<std::string, RewardModel>&), qi::locals<std::vector<StateReward>, std::vector<TransitionReward>>, Skipper> rewardDefinition; - qi::rule<Iterator, StateReward(), Skipper> stateRewardDefinition; - qi::rule<Iterator, TransitionReward(), qi::locals<std::string>, Skipper> transitionRewardDefinition; - - // Rules for label definitions. - qi::rule<Iterator, qi::unused_type(std::map<std::string, std::unique_ptr<BaseExpression>>&), Skipper> labelDefinitionList; - qi::rule<Iterator, qi::unused_type(std::map<std::string, std::unique_ptr<BaseExpression>>&), Skipper> labelDefinition; - - // Rules for constant definitions. - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> constantDefinition; - qi::rule<Iterator, qi::unused_type(std::map<std::string, std::unique_ptr<BooleanConstantExpression>>&, std::map<std::string, std::unique_ptr<IntegerConstantExpression>>&, std::map<std::string, std::unique_ptr<DoubleConstantExpression>>&), Skipper> undefinedConstantDefinition; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> definedConstantDefinition; - qi::rule<Iterator, qi::unused_type(std::map<std::string, std::unique_ptr<BooleanConstantExpression>>&), Skipper> undefinedBooleanConstantDefinition; - qi::rule<Iterator, qi::unused_type(std::map<std::string, std::unique_ptr<IntegerConstantExpression>>&), Skipper> undefinedIntegerConstantDefinition; - qi::rule<Iterator, qi::unused_type(std::map<std::string, std::unique_ptr<DoubleConstantExpression>>&), Skipper> undefinedDoubleConstantDefinition; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> definedBooleanConstantDefinition; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> definedIntegerConstantDefinition; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> definedDoubleConstantDefinition; - - // Rules for formula definitions. - qi::rule<Iterator, qi::unused_type(), Skipper> formulaDefinitionList; - qi::rule<Iterator, qi::unused_type(), Skipper> formulaDefinition; - qi::rule<Iterator, qi::unused_type(), Skipper> constantIntegerFormulaDefinition; - qi::rule<Iterator, qi::unused_type(), Skipper> integerFormulaDefinition; - qi::rule<Iterator, qi::unused_type(), Skipper> constantDoubleFormulaDefinition; - qi::rule<Iterator, qi::unused_type(), Skipper> constantBooleanFormulaDefinition; - qi::rule<Iterator, qi::unused_type(), Skipper> booleanFormulaDefinition; - - // Rules for variable recognition. - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), Skipper> booleanVariableCreatorExpression; - qi::rule<Iterator, std::shared_ptr<BaseExpression>(), qi::locals<std::shared_ptr<BaseExpression>>, Skipper> integerVariableCreatorExpression; - - storm::parser::prism::keywordsStruct keywords_; - storm::parser::prism::modelTypeStruct modelType_; - storm::parser::prism::relationalOperatorStruct relations_; - - /*! - * Adds a label with the given name and expression to the given label-to-expression map. - * - * @param name The name of the label. - * @param expression The expression associated with the label. - * @param nameToExpressionMap The map to which the label is added. - */ - void addLabel(std::string const& name, std::shared_ptr<BaseExpression> const& value, std::map<std::string, std::unique_ptr<BaseExpression>>& nameToExpressionMap); - - /*! - * Adds a boolean assignment for the given variable with the given expression and adds it to the - * provided variable-to-assignment map. - * - * @param variable The name of the variable that the assignment targets. - * @param expression The expression that is assigned to the variable. - * @param variableToAssignmentMap The map to which the assignment is added. - */ - void addBooleanAssignment(std::string const& variable, std::shared_ptr<BaseExpression> const& expression, std::map<std::string, Assignment>& variableToAssignmentMap); - - /*! - * Adds a boolean assignment for the given variable with the given expression and adds it to the - * provided variable-to-assignment map. - * - * @param variable The name of the variable that the assignment targets. - * @param expression The expression that is assigned to the variable. - * @param variableToAssignmentMap The map to which the assignment is added. - */ - void addIntegerAssignment(std::string const& variable, std::shared_ptr<BaseExpression> const& expression, std::map<std::string, Assignment>& variableToAssignmentMap); - - void addUndefinedBooleanConstant(std::string const& name, std::map<std::string, std::unique_ptr<BooleanConstantExpression>>& nameToExpressionMap); - - void addUndefinedIntegerConstant(std::string const& name, std::map<std::string, std::unique_ptr<IntegerConstantExpression>>& nameToExpressionMap); - - void addUndefinedDoubleConstant(std::string const& name, std::map<std::string, std::unique_ptr<DoubleConstantExpression>>& nameToExpressionMap); - - /*! - * Creates a module by renaming, i.e. takes the module given by the old name, creates a new module - * with the given name which renames all identifiers according to the given mapping. - * - * @param name The name of the new module. - * @param oldName The name of the module that is to be copied (modulo renaming). - * @param renaming A mapping from identifiers to their new names. - */ - Module renameModule(std::string const& name, std::string const& oldName, std::map<std::string, std::string> const& renaming); - - /*! - * Creates a new module with the given name, boolean and integer variables and commands. - * - * @param name The name of the module to create. - * @param booleanVariables The boolean variables of the module. - * @param integerVariables The integer variables of the module. - * @param booleanVariableToLocalIndexMap A mapping of boolean variables to module-local indices. - * @param integerVariableToLocalIndexMap A mapping of boolean variables to module-local indices. - * @param commands The commands associated with this module. - */ - Module createModule(std::string const& name, std::vector<BooleanVariable> const& booleanVariables, std::vector<IntegerVariable> const& integerVariables, std::map<std::string, uint_fast64_t> const& booleanVariableToLocalIndexMap, std::map<std::string, uint_fast64_t> const& integerVariableToLocalIndexMap, std::vector<storm::ir::Command> const& commands); - - /*! - * Creates an integer variable with the given name, domain and initial value and adds it to the - * provided list of integer variables and the given mappings. - * - * @param name The name of the integer variable. - * @param lower The expression that defines the lower bound of the domain. - * @param upper The expression that defines the upper bound of the domain. - * @param init The expression that defines the initial value of the variable. - * @param integerVariableToGlobalIndexMap A mapping of integer variables to global indices. - * @param isGlobalVariable A flag indicating whether the variable is supposed to be global or not. - */ - void createIntegerVariable(std::string const& name, std::shared_ptr<BaseExpression> const& lower, std::shared_ptr<BaseExpression> const& upper, std::shared_ptr<BaseExpression> const& init, std::vector<IntegerVariable>& integerVariables, std::map<std::string, uint_fast64_t>& integerVariableToGlobalIndexMap, bool isGlobalVariable); - - /*! - * Creates an boolean variable with the given name and initial value and adds it to the - * provided list of boolean variables and the given mappings. - * - * @param name The name of the boolean variable. - * @param init The expression that defines the initial value of the variable. - * @param booleanVariableToGlobalIndexMap A mapping of boolean variables to global indices. - * @param isGlobalVariable A flag indicating whether the variable is supposed to be global or not. - */ - void createBooleanVariable(std::string const& name, std::shared_ptr<BaseExpression> const& init, std::vector<BooleanVariable>& booleanVariables, std::map<std::string, uint_fast64_t>& booleanVariableToGlobalIndexMap, bool isGlobalVariable); - - /*! - * Creates a command with the given label, guard and updates. - * - * @param label The label of the command. - * @param guard The guard of the command. - * @param updates The updates associated with the command. - */ - Command createCommand(std::string const& label, std::shared_ptr<BaseExpression> const& guard, std::vector<Update> const& updates); - - /*! - * Creates an update with the given likelihood and the given assignments to boolean and integer variables, respectively. - * - * @param likelihood The likelihood of this update being executed. - * @param booleanAssignments The assignments to boolean variables this update involves. - * @param integerAssignments The assignments to integer variables this update involves. - */ - Update createUpdate(std::shared_ptr<BaseExpression> const& likelihood, std::map<std::string, Assignment> const& booleanAssignments, std::map<std::string, Assignment> const& integerAssignments); - - }; - - - } // namespace prism - } // namespace parser -} // namespace storm - - -#endif /* STORM_PARSER_PRISMPARSER_PRISMGRAMMAR_H */ - diff --git a/src/parser/prismparser/Tokens.h b/src/parser/prismparser/Tokens.h deleted file mode 100644 index bbf7e8418..000000000 --- a/src/parser/prismparser/Tokens.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * File: Tokens.h - * Author: nafur - * - * Created on April 19, 2013, 11:17 PM - */ - -#ifndef TOKENS_H -#define TOKENS_H - -namespace storm { -namespace parser { -namespace prism { - - /*! - * A structure mapping the textual representation of a model type to the model type - * representation of the intermediate representation. - */ - struct modelTypeStruct : qi::symbols<char, Program::ModelType> { - modelTypeStruct() { - add - ("dtmc", Program::ModelType::DTMC) - ("ctmc", Program::ModelType::CTMC) - ("mdp", Program::ModelType::MDP) - ("ctmdp", Program::ModelType::CTMDP) - ; - } - }; - - - /*! - * A structure defining the keywords that are not allowed to be chosen as identifiers. - */ - struct keywordsStruct : qi::symbols<char, unsigned> { - keywordsStruct() { - add - ("dtmc", 1) - ("ctmc", 2) - ("mdp", 3) - ("ctmdp", 4) - ("const", 5) - ("int", 6) - ("bool", 7) - ("module", 8) - ("endmodule", 9) - ("rewards", 10) - ("endrewards", 11) - ("true", 12) - ("false", 13) - ; - } - }; - - /*! - * A structure mapping the textual representation of a binary relation to the representation - * of the intermediate representation. - */ - struct relationalOperatorStruct : qi::symbols<char, BinaryRelationExpression::RelationType> { - relationalOperatorStruct() { - add - ("=", BinaryRelationExpression::EQUAL) - ("!=", BinaryRelationExpression::NOT_EQUAL) - ("<", BinaryRelationExpression::LESS) - ("<=", BinaryRelationExpression::LESS_OR_EQUAL) - (">", BinaryRelationExpression::GREATER) - (">=", BinaryRelationExpression::GREATER_OR_EQUAL) - ; - } - }; -} -} -} - -#endif /* TOKENS_H */ - diff --git a/src/parser/prismparser/VariableState.cpp b/src/parser/prismparser/VariableState.cpp deleted file mode 100644 index 244a3f17e..000000000 --- a/src/parser/prismparser/VariableState.cpp +++ /dev/null @@ -1,191 +0,0 @@ -#include "VariableState.h" -#include "src/exceptions/InvalidArgumentException.h" - -namespace storm { - namespace parser { - namespace prism { - - using namespace storm::ir; - using namespace storm::ir::expressions; - - template<typename T> - struct SymbolDump { - SymbolDump(std::ostream& out) : out(out) {} - void operator() (std::basic_string<char> s, T elem) { - this->out << "\t" << s << " -> " << elem << std::endl; - } - private: - std::ostream& out; - }; - template<typename T> - std::ostream& operator<<(std::ostream& out, qi::symbols<char, T>& symbols) { - out << "Dumping symbol table" << std::endl; - SymbolDump<T> dump(out); - symbols.for_each(dump); - return out; - } - std::ostream& operator<<(std::ostream& out, VariableState::variableNamesStruct& symbols) { - SymbolDump<std::string> dump(out); - symbols.for_each(dump); - return out; - } - - - VariableState::VariableState(bool firstRun) : firstRun(firstRun), keywords(), nextLocalBooleanVariableIndex(0), nextLocalIntegerVariableIndex(0), nextGlobalBooleanVariableIndex(0), nextGlobalIntegerVariableIndex(0), nextGlobalCommandIndex(0), nextGlobalUpdateIndex(0) { - // Nothing to do here. - } - - uint_fast64_t VariableState::getNextLocalBooleanVariableIndex() const { - return this->nextLocalBooleanVariableIndex; - } - - uint_fast64_t VariableState::getNextLocalIntegerVariableIndex() const { - return this->nextLocalIntegerVariableIndex; - } - - uint_fast64_t VariableState::getNextGlobalBooleanVariableIndex() const { - return this->nextGlobalBooleanVariableIndex; - } - - uint_fast64_t VariableState::getNextGlobalIntegerVariableIndex() const { - return this->nextGlobalIntegerVariableIndex; - } - - uint_fast64_t VariableState::getNextGlobalCommandIndex() const { - return this->nextGlobalCommandIndex; - } - - uint_fast64_t VariableState::getNextGlobalUpdateIndex() const { - return this->nextGlobalUpdateIndex; - } - - uint_fast64_t VariableState::addBooleanVariable(std::string const& name) { - if (firstRun) { - LOG4CPLUS_TRACE(logger, "Adding boolean variable " << name << " with new id " << this->nextGlobalBooleanVariableIndex << "."); - this->booleanVariables_.add(name, std::shared_ptr<VariableExpression>(new VariableExpression(storm::ir::expressions::BaseExpression::bool_, this->nextGlobalBooleanVariableIndex, name))); - this->booleanVariableNames_.add(name, name); - ++this->nextGlobalBooleanVariableIndex; - ++this->nextLocalBooleanVariableIndex; - return this->nextGlobalBooleanVariableIndex - 1; - } else { - std::shared_ptr<VariableExpression> variableExpression = this->booleanVariables_.at(name); - if (variableExpression != nullptr) { - return variableExpression->getGlobalVariableIndex(); - } else { - LOG4CPLUS_ERROR(logger, "Boolean variable " << name << " does not exist."); - throw storm::exceptions::InvalidArgumentException() << "Boolean variable " << name << " does not exist."; - } - } - } - - uint_fast64_t VariableState::addIntegerVariable(std::string const& name) { - if (firstRun) { - LOG4CPLUS_TRACE(logger, "Adding integer variable " << name << " with new id " << this->nextGlobalIntegerVariableIndex << "."); - this->integerVariables_.add(name, std::shared_ptr<VariableExpression>(new VariableExpression(storm::ir::expressions::BaseExpression::int_, this->nextGlobalIntegerVariableIndex, name))); - this->integerVariableNames_.add(name, name); - ++this->nextGlobalIntegerVariableIndex; - ++this->nextLocalIntegerVariableIndex; - return this->nextGlobalIntegerVariableIndex - 1; - } else { - std::shared_ptr<VariableExpression> variableExpression = this->integerVariables_.at(name); - if (variableExpression != nullptr) { - return variableExpression->getGlobalVariableIndex(); - } else { - LOG4CPLUS_ERROR(logger, "Integer variable " << name << " does not exist."); - throw storm::exceptions::InvalidArgumentException() << "Integer variable " << name << " does not exist."; - } - } - } - - std::shared_ptr<VariableExpression> VariableState::getBooleanVariableExpression(std::string const& name) const { - std::shared_ptr<VariableExpression> const* variableExpression = this->booleanVariables_.find(name); - if (variableExpression != nullptr) { - return *variableExpression; - } else { - if (firstRun) { - LOG4CPLUS_TRACE(logger, "Trying to retrieve boolean variable " << name << " that was not yet created; returning dummy instead."); - return std::shared_ptr<VariableExpression>(new VariableExpression(BaseExpression::bool_, name)); - } else { - LOG4CPLUS_ERROR(logger, "Boolean variable " << name << " does not exist."); - throw storm::exceptions::InvalidArgumentException() << "Boolean variable " << name << " does not exist."; - } - } - } - - std::shared_ptr<VariableExpression> VariableState::getIntegerVariableExpression(std::string const& name) const { - std::shared_ptr<VariableExpression> const* variableExpression = this->integerVariables_.find(name); - if (variableExpression != nullptr) { - return *variableExpression; - } else { - if (firstRun) { - LOG4CPLUS_TRACE(logger, "Trying to retrieve integer variable " << name << " that was not yet created; returning dummy instead."); - return std::shared_ptr<VariableExpression>(new VariableExpression(BaseExpression::int_, name)); - } else { - LOG4CPLUS_ERROR(logger, "Integer variable " << name << " does not exist."); - throw storm::exceptions::InvalidArgumentException() << "Integer variable " << name << " does not exist."; - } - } - } - - std::shared_ptr<VariableExpression> VariableState::getVariableExpression(std::string const& name) const { - std::shared_ptr<VariableExpression> const* variableExpression = this->integerVariables_.find(name); - if (variableExpression != nullptr) { - return *variableExpression; - } - - variableExpression = this->booleanVariables_.find(name); - if (variableExpression != nullptr) { - return *variableExpression; - } - LOG4CPLUS_ERROR(logger, "Variable " << name << " does not exist."); - throw storm::exceptions::InvalidArgumentException() << "Variable " << name << " does not exist."; - } - - void VariableState::clearLocalVariables() { - this->localBooleanVariables_.clear(); - this->localIntegerVariables_.clear(); - this->nextLocalBooleanVariableIndex = 0; - this->nextLocalIntegerVariableIndex = 0; - } - - bool VariableState::isFreeIdentifier(std::string const& identifier) const { - if (this->booleanVariableNames_.find(identifier) != nullptr) return false; - if (this->integerVariableNames_.find(identifier) != nullptr) return false; - if (this->allConstantNames_.find(identifier) != nullptr) return false; - if (this->labelNames_.find(identifier) != nullptr) return false; - if (this->moduleNames_.find(identifier) != nullptr) return false; - if (this->keywords.find(identifier) != nullptr) return false; - if (this->booleanFormulas_.find(identifier) != nullptr) return false; - if (this->integerFormulas_.find(identifier) != nullptr) return false; - if (this->doubleFormulas_.find(identifier) != nullptr) return false; - if (this->constantBooleanFormulas_.find(identifier) != nullptr) return false; - if (this->constantIntegerFormulas_.find(identifier) != nullptr) return false; - if (this->constantDoubleFormulas_.find(identifier) != nullptr) return false; - return true; - } - - bool VariableState::isIdentifier(std::string const& identifier) const { - if (this->allConstantNames_.find(identifier) != nullptr) return false; - if (this->keywords.find(identifier) != nullptr) return false; - return true; - } - - void VariableState::prepareForSecondRun() { - integerConstants_.clear(); - booleanConstants_.clear(); - doubleConstants_.clear(); - allConstantNames_.clear(); - constantBooleanFormulas_.clear(); - booleanFormulas_.clear(); - constantIntegerFormulas_.clear(); - integerFormulas_.clear(); - constantDoubleFormulas_.clear(); - doubleFormulas_.clear(); - this->firstRun = false; - nextGlobalCommandIndex = 0; - nextGlobalUpdateIndex = 0; - } - - } // namespace prism - } // namespace parser -} // namespace storm diff --git a/src/parser/prismparser/VariableState.h b/src/parser/prismparser/VariableState.h deleted file mode 100644 index 6dc7170a4..000000000 --- a/src/parser/prismparser/VariableState.h +++ /dev/null @@ -1,207 +0,0 @@ -/* - * File: VariableState.h - * Author: nafur - * - * Created on April 10, 2013, 4:43 PM - */ - -#ifndef STORM_PARSER_PRISMPARSER_VARIABLESTATE_H -#define STORM_PARSER_PRISMPARSER_VARIABLESTATE_H - -#include <iostream> - -#include "src/ir/IR.h" -#include "Includes.h" -#include "Tokens.h" - -namespace storm { - namespace parser { - namespace prism { - - using namespace storm::ir; - using namespace storm::ir::expressions; - - template<typename T> - std::ostream& operator<<(std::ostream& out, qi::symbols<char, T>& symbols); - - /*! - * This class contains the internal state that is needed for parsing a PRISM model. - */ - class VariableState { - public: - /*! - * Creates a new variable state object. By default, this object will be set to a state in which - * it is ready for performing a first run on some input. The first run creates all variables - * while the second one checks for the correct usage of variables in expressions. - * - * @param firstRun If set, this object will be in a state ready for performing the first run. If - * set to false, this object will assume that it has all variable data already. - */ - VariableState(bool firstRun = true); - - /*! - * Indicator, if we are still in the first run. - */ - bool firstRun; - - /*! - * A parser for all reserved keywords. - */ - keywordsStruct keywords; - - /*! - * Internal counter for the local index of the next new boolean variable. - */ - uint_fast64_t nextLocalBooleanVariableIndex; - - /*! - * Retrieves the next free local index for a boolean variable. - * - * @return The next free local index for a boolean variable. - */ - uint_fast64_t getNextLocalBooleanVariableIndex() const; - - /*! - * Internal counter for the local index of the next new integer variable. - */ - uint_fast64_t nextLocalIntegerVariableIndex; - - /*! - * Retrieves the next free global index for a integer variable. - * - * @return The next free global index for a integer variable. - */ - uint_fast64_t getNextLocalIntegerVariableIndex() const; - - /*! - * Internal counter for the index of the next new boolean variable. - */ - uint_fast64_t nextGlobalBooleanVariableIndex; - - /*! - * Retrieves the next free global index for a boolean variable. - * - * @return The next free global index for a boolean variable. - */ - uint_fast64_t getNextGlobalBooleanVariableIndex() const; - - /*! - * Internal counter for the index of the next new integer variable. - */ - uint_fast64_t nextGlobalIntegerVariableIndex; - - /*! - * Retrieves the next free global index for a integer variable. - * - * @return The next free global index for a integer variable. - */ - uint_fast64_t getNextGlobalIntegerVariableIndex() const; - - /*! - * Internal counter for the index of the next command. - */ - uint_fast64_t nextGlobalCommandIndex; - - /*! - * Retrieves the next free global index for a command. - * - * @return The next free global index for a command. - */ - uint_fast64_t getNextGlobalCommandIndex() const; - - /*! - * Internal counter for the index of the next update. - */ - uint_fast64_t nextGlobalUpdateIndex; - - /*! - * Retrieves the next free global index for an update. - * - * @return The next free global index for an update. - */ - uint_fast64_t getNextGlobalUpdateIndex() const; - - // Structures mapping variable and constant names to the corresponding expression nodes of - // the intermediate representation. - struct qi::symbols<char, std::shared_ptr<VariableExpression>> integerVariables_, booleanVariables_; - struct qi::symbols<char, std::shared_ptr<BaseExpression>> integerConstants_, booleanConstants_, doubleConstants_, booleanFormulas_, constantBooleanFormulas_, integerFormulas_, constantIntegerFormulas_, doubleFormulas_, constantDoubleFormulas_; - - // A structure representing the identity function over identifier names. - struct variableNamesStruct : qi::symbols<char, std::string> { } - integerVariableNames_, booleanVariableNames_, commandNames_, labelNames_, allConstantNames_, moduleNames_, - localBooleanVariables_, localIntegerVariables_, assignedBooleanVariables_, assignedIntegerVariables_, - globalBooleanVariables_, globalIntegerVariables_; - - /*! - * Adds a new boolean variable with the given name. - * - * @param name The name of the variable. - * @return The global index of the variable. - */ - uint_fast64_t addBooleanVariable(std::string const& name); - - /*! - * Adds a new integer variable with the given name. - * - * @param name The name of the variable. - * @return The global index of the variable. - */ - uint_fast64_t addIntegerVariable(std::string const& name); - - /*! - * Retrieves the variable expression for the boolean variable with the given name. - * - * @param name The name of the boolean variable for which to retrieve the variable expression. - * @return The variable expression for the boolean variable with the given name. - */ - std::shared_ptr<VariableExpression> getBooleanVariableExpression(std::string const& name) const; - - /*! - * Retrieves the variable expression for the integer variable with the given name. - * - * @param name The name of the integer variable for which to retrieve the variable expression. - * @return The variable expression for the integer variable with the given name. - */ - std::shared_ptr<VariableExpression> getIntegerVariableExpression(std::string const& name) const; - - /*! - * Retrieve the variable expression for the variable with the given name. - * - * @param name The name of the variable for which to retrieve the variable expression. - * @return The variable expression for the variable with the given name. - */ - std::shared_ptr<VariableExpression> getVariableExpression(std::string const& name) const; - - /*! - * Clears all local variables. - */ - void clearLocalVariables(); - - /*! - * Check if the given string is a free identifier. - * - * @param identifier A string to be checked. - * @return True iff the given string is a free identifier. - */ - bool isFreeIdentifier(std::string const& identifier) const; - - /*! - * Check if given string is a valid identifier. - * - * @param identifier A string to be checked. - * @return True iff the given string is an identifier. - */ - bool isIdentifier(std::string const& identifier) const; - - /*! - * Prepare state to proceed to second parser run. Currently, this clears the constants. - */ - void prepareForSecondRun(); - }; - - } // namespace prism - } // namespace parser -} // namespace storm - -#endif /* STORM_PARSER_PRISMPARSER_VARIABLESTATE_H */ - diff --git a/src/solver/GlpkLpSolver.cpp b/src/solver/GlpkLpSolver.cpp index e343526f2..2a9eec7f6 100644 --- a/src/solver/GlpkLpSolver.cpp +++ b/src/solver/GlpkLpSolver.cpp @@ -4,13 +4,12 @@ #include <iostream> -#include "src/exceptions/InvalidStateException.h" -#include "src/settings/Settings.h" - -#include "log4cplus/logger.h" -#include "log4cplus/loggingmacros.h" +#include "src/storage/expressions/LinearCoefficientVisitor.h" -extern log4cplus::Logger logger; +#include "src/settings/Settings.h" +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidAccessException.h" +#include "src/exceptions/InvalidStateException.h" bool GlpkLpSolverOptionsRegistered = storm::settings::Settings::registerNewModule([] (storm::settings::Settings* instance) -> bool { instance->addOption(storm::settings::OptionBuilder("GlpkLpSolver", "glpkoutput", "", "If set, the glpk output will be printed to the command line.").build()); @@ -22,7 +21,7 @@ bool GlpkLpSolverOptionsRegistered = storm::settings::Settings::registerNewModul namespace storm { namespace solver { - GlpkLpSolver::GlpkLpSolver(std::string const& name, ModelSense const& modelSense) : LpSolver(modelSense), lp(nullptr), nextVariableIndex(1), nextConstraintIndex(1), modelContainsIntegerVariables(false), isInfeasibleFlag(false), isUnboundedFlag(false), rowIndices(), columnIndices(), coefficientValues() { + GlpkLpSolver::GlpkLpSolver(std::string const& name, ModelSense const& modelSense) : LpSolver(modelSense), lp(nullptr), variableNameToIndexMap(), nextVariableIndex(1), nextConstraintIndex(1), modelContainsIntegerVariables(false), isInfeasibleFlag(false), isUnboundedFlag(false), rowIndices(), columnIndices(), coefficientValues() { // Create the LP problem for glpk. lp = glp_create_prob(); @@ -38,11 +37,11 @@ namespace storm { coefficientValues.push_back(0); } - GlpkLpSolver::GlpkLpSolver(std::string const& name) : GlpkLpSolver(name, MINIMIZE) { + GlpkLpSolver::GlpkLpSolver(std::string const& name) : GlpkLpSolver(name, ModelSense::Minimize) { // Intentionally left empty. } - GlpkLpSolver::GlpkLpSolver() : GlpkLpSolver("", MINIMIZE) { + GlpkLpSolver::GlpkLpSolver() : GlpkLpSolver("", ModelSense::Minimize) { // Intentionally left empty. } @@ -56,78 +55,120 @@ namespace storm { glp_free_env(); } - uint_fast64_t GlpkLpSolver::createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { - glp_add_cols(this->lp, 1); - glp_set_col_name(this->lp, nextVariableIndex, name.c_str()); - switch (variableType) { - case LpSolver::BOUNDED: - glp_set_col_bnds(lp, nextVariableIndex, GLP_DB, lowerBound, upperBound); - break; - case LpSolver::UNBOUNDED: - glp_set_col_bnds(lp, nextVariableIndex, GLP_FR, 0, 0); - break; - case LpSolver::UPPER_BOUND: - glp_set_col_bnds(lp, nextVariableIndex, GLP_UP, 0, upperBound); - break; - case LpSolver::LOWER_BOUND: - glp_set_col_bnds(lp, nextVariableIndex, GLP_LO, lowerBound, 0); - break; - } - glp_set_col_kind(this->lp, nextVariableIndex, GLP_CV); - glp_set_obj_coef(this->lp, nextVariableIndex, objectiveFunctionCoefficient); - ++nextVariableIndex; - - this->currentModelHasBeenOptimized = false; - return nextVariableIndex - 1; + void GlpkLpSolver::addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GLP_CV, GLP_DB, lowerBound, upperBound, objectiveFunctionCoefficient); + } + + void GlpkLpSolver::addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GLP_CV, GLP_LO, lowerBound, 0, objectiveFunctionCoefficient); + } + + void GlpkLpSolver::addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GLP_CV, GLP_UP, 0, upperBound, objectiveFunctionCoefficient); } - uint_fast64_t GlpkLpSolver::createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { - uint_fast64_t index = this->createContinuousVariable(name, variableType, lowerBound, upperBound, objectiveFunctionCoefficient); - glp_set_col_kind(this->lp, index, GLP_IV); + void GlpkLpSolver::addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient) { + this->addVariable(name, GLP_CV, GLP_FR, 0, 0, objectiveFunctionCoefficient); + } + + void GlpkLpSolver::addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GLP_IV, GLP_DB, lowerBound, upperBound, objectiveFunctionCoefficient); + this->modelContainsIntegerVariables = true; + } + + void GlpkLpSolver::addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GLP_IV, GLP_LO, lowerBound, 0, objectiveFunctionCoefficient); + this->modelContainsIntegerVariables = true; + } + + void GlpkLpSolver::addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GLP_IV, GLP_UP, 0, upperBound, objectiveFunctionCoefficient); + this->modelContainsIntegerVariables = true; + } + + void GlpkLpSolver::addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient) { + this->addVariable(name, GLP_IV, GLP_FR, 0, 0, objectiveFunctionCoefficient); this->modelContainsIntegerVariables = true; - return index; } - uint_fast64_t GlpkLpSolver::createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) { - uint_fast64_t index = this->createContinuousVariable(name, UNBOUNDED, 0, 1, objectiveFunctionCoefficient); - glp_set_col_kind(this->lp, index, GLP_BV); + void GlpkLpSolver::addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) { + this->addVariable(name, GLP_BV, GLP_FR, 0, 0, objectiveFunctionCoefficient); this->modelContainsIntegerVariables = true; - return index; + } + + void GlpkLpSolver::addVariable(std::string const& name, int variableType, int boundType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { + // Check whether variable already exists. + auto nameIndexPair = this->variableNameToIndexMap.find(name); + LOG_THROW(nameIndexPair == this->variableNameToIndexMap.end(), storm::exceptions::InvalidArgumentException, "Variable '" << nameIndexPair->first << "' already exists."); + + // Check for valid variable type. + LOG_ASSERT(variableType == GLP_CV || variableType == GLP_IV || variableType == GLP_BV, "Illegal type '" << variableType << "' for glpk variable."); + + // Check for valid bound type. + LOG_ASSERT(boundType == GLP_FR || boundType == GLP_UP || boundType == GLP_LO || boundType == GLP_DB, "Illegal bound type for variable '" << name << "'."); + + // Finally, create the actual variable. + glp_add_cols(this->lp, 1); + glp_set_col_name(this->lp, nextVariableIndex, name.c_str()); + glp_set_col_bnds(lp, nextVariableIndex, boundType, lowerBound, upperBound); + glp_set_col_kind(this->lp, nextVariableIndex, variableType); + glp_set_obj_coef(this->lp, nextVariableIndex, objectiveFunctionCoefficient); + this->variableNameToIndexMap.emplace(name, this->nextVariableIndex); + ++this->nextVariableIndex; } void GlpkLpSolver::update() const { // Intentionally left empty. } - void GlpkLpSolver::addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) { - if (variables.size() != coefficients.size()) { - LOG4CPLUS_ERROR(logger, "Sizes of variable indices vector and coefficients vector do not match."); - throw storm::exceptions::InvalidStateException() << "Sizes of variable indices vector and coefficients vector do not match."; - } - + void GlpkLpSolver::addConstraint(std::string const& name, storm::expressions::Expression const& constraint) { // Add the row that will represent this constraint. glp_add_rows(this->lp, 1); glp_set_row_name(this->lp, nextConstraintIndex, name.c_str()); + LOG_THROW(constraint.isRelationalExpression(), storm::exceptions::InvalidArgumentException, "Illegal constraint is not a relational expression."); + LOG_THROW(constraint.getOperator() != storm::expressions::OperatorType::NotEqual, storm::exceptions::InvalidArgumentException, "Illegal constraint uses inequality operator."); + + std::pair<storm::expressions::SimpleValuation, double> leftCoefficients = storm::expressions::LinearCoefficientVisitor().getLinearCoefficients(constraint.getOperand(0)); + std::pair<storm::expressions::SimpleValuation, double> rightCoefficients = storm::expressions::LinearCoefficientVisitor().getLinearCoefficients(constraint.getOperand(1)); + for (auto const& identifier : rightCoefficients.first.getDoubleIdentifiers()) { + if (leftCoefficients.first.containsDoubleIdentifier(identifier)) { + leftCoefficients.first.setDoubleValue(identifier, leftCoefficients.first.getDoubleValue(identifier) - rightCoefficients.first.getDoubleValue(identifier)); + } else { + leftCoefficients.first.addDoubleIdentifier(identifier, -rightCoefficients.first.getDoubleValue(identifier)); + } + } + rightCoefficients.second -= leftCoefficients.second; + + // Now we need to transform the coefficients to the vector representation. + std::vector<int> variables; + std::vector<double> coefficients; + for (auto const& identifier : leftCoefficients.first.getDoubleIdentifiers()) { + auto identifierIndexPair = this->variableNameToIndexMap.find(identifier); + LOG_THROW(identifierIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidArgumentException, "Constraint contains illegal identifier '" << identifier << "'."); + variables.push_back(identifierIndexPair->second); + coefficients.push_back(leftCoefficients.first.getDoubleValue(identifier)); + } + // Determine the type of the constraint and add it properly. - bool isUpperBound = boundType == LESS || boundType == LESS_EQUAL; - bool isStrict = boundType == LESS || boundType == GREATER; - switch (boundType) { - case LESS: - glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_UP, 0, rightHandSideValue - storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); + switch (constraint.getOperator()) { + case storm::expressions::OperatorType::Less: + glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_UP, 0, rightCoefficients.second - storm::settings::Settings::getInstance()->getOptionByLongName("glpkinttol").getArgument(0).getValueAsDouble()); break; - case LESS_EQUAL: - glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_UP, 0, rightHandSideValue); + case storm::expressions::OperatorType::LessOrEqual: + glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_UP, 0, rightCoefficients.second); break; - case GREATER: - glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_LO, rightHandSideValue + storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble(), 0); + case storm::expressions::OperatorType::Greater: + glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_LO, rightCoefficients.second + storm::settings::Settings::getInstance()->getOptionByLongName("glpkinttol").getArgument(0).getValueAsDouble(), 0); break; - case GREATER_EQUAL: - glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_LO, rightHandSideValue, 0); + case storm::expressions::OperatorType::GreaterOrEqual: + glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_LO, rightCoefficients.second, 0); break; - case EQUAL: - glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_FX, rightHandSideValue, rightHandSideValue); + case storm::expressions::OperatorType::Equal: + glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_FX, rightCoefficients.second, rightCoefficients.second); break; + default: + LOG_ASSERT(false, "Illegal operator in LP solver constraint."); } // Record the variables and coefficients in the coefficient matrix. @@ -145,13 +186,13 @@ namespace storm { this->isUnboundedFlag = false; // Start by setting the model sense. - glp_set_obj_dir(this->lp, this->getModelSense() == MINIMIZE ? GLP_MIN : GLP_MAX); + glp_set_obj_dir(this->lp, this->getModelSense() == LpSolver::ModelSense::Minimize ? GLP_MIN : GLP_MAX); glp_load_matrix(this->lp, rowIndices.size() - 1, rowIndices.data(), columnIndices.data(), coefficientValues.data()); int error = 0; if (this->modelContainsIntegerVariables) { - glp_iocp* parameters = new glp_iocp; + glp_iocp* parameters = new glp_iocp(); glp_init_iocp(parameters); parameters->presolve = GLP_ON; parameters->tol_int = storm::settings::Settings::getInstance()->getOptionByLongName("glpkinttol").getArgument(0).getValueAsDouble(); @@ -173,11 +214,7 @@ namespace storm { error = glp_simplex(this->lp, nullptr); } - if (error != 0) { - LOG4CPLUS_ERROR(logger, "Unable to optimize glpk model (" << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to optimize glpk model (" << error << ")."; - } - + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to optimize glpk model (" << error << ")."); this->currentModelHasBeenOptimized = true; } @@ -219,103 +256,75 @@ namespace storm { return status == GLP_OPT; } - int_fast64_t GlpkLpSolver::getIntegerValue(uint_fast64_t variableIndex) const { + double GlpkLpSolver::getContinuousValue(std::string const& name) const { if (!this->isOptimal()) { - if (this->isInfeasible()) { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from infeasible model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model."; - } else if (this->isUnbounded()) { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unbounded model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model."; - } else { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unoptimized model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model."; - } + LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from infeasible model."); + LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unbounded model."); + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unoptimized model."); } - + + auto variableIndexPair = this->variableNameToIndexMap.find(name); + LOG_THROW(variableIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidAccessException, "Accessing value of unknown variable '" << name << "'."); + double value = 0; if (this->modelContainsIntegerVariables) { - value = glp_mip_col_val(this->lp, static_cast<int>(variableIndex)); + value = glp_mip_col_val(this->lp, static_cast<int>(variableIndexPair->second)); } else { - value = glp_get_col_prim(this->lp, static_cast<int>(variableIndex)); - } - - if (std::abs(value - static_cast<int>(value)) <= storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()) { - // Nothing to do in this case. - } else if (std::abs(value) > storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()) { - LOG4CPLUS_ERROR(logger, "Illegal value for integer variable in glpk solution (" << value << ")."); - throw storm::exceptions::InvalidStateException() << "Illegal value for integer variable in glpk solution (" << value << ")."; + value = glp_get_col_prim(this->lp, static_cast<int>(variableIndexPair->second)); } - - return static_cast<int_fast64_t>(value); + return value; } - bool GlpkLpSolver::getBinaryValue(uint_fast64_t variableIndex) const { + int_fast64_t GlpkLpSolver::getIntegerValue(std::string const& name) const { if (!this->isOptimal()) { - if (this->isInfeasible()) { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from infeasible model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model."; - } else if (this->isUnbounded()) { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unbounded model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model."; - } else { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unoptimized model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model."; - } + LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from infeasible model."); + LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unbounded model."); + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unoptimized model."); } + + auto variableIndexPair = this->variableNameToIndexMap.find(name); + LOG_THROW(variableIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidAccessException, "Accessing value of unknown variable '" << name << "'."); double value = 0; if (this->modelContainsIntegerVariables) { - value = glp_mip_col_val(this->lp, static_cast<int>(variableIndex)); + value = glp_mip_col_val(this->lp, static_cast<int>(variableIndexPair->second)); } else { - value = glp_get_col_prim(this->lp, static_cast<int>(variableIndex)); + value = glp_get_col_prim(this->lp, static_cast<int>(variableIndexPair->second)); } - if (std::abs(value - 1) <= storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()) { - // Nothing to do in this case. - } else if (std::abs(value) > storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()) { - LOG4CPLUS_ERROR(logger, "Illegal value for binary variable in Gurobi solution (" << value << ")."); - throw storm::exceptions::InvalidStateException() << "Illegal value for binary variable in Gurobi solution (" << value << ")."; - } + // Now check the desired precision was actually achieved. + LOG_THROW(std::abs(static_cast<int>(value) - value) <= storm::settings::Settings::getInstance()->getOptionByLongName("glpkinttol").getArgument(0).getValueAsDouble(), storm::exceptions::InvalidStateException, "Illegal value for integer variable in glpk solution (" << value << ")."); - return static_cast<bool>(value); + return static_cast<int_fast64_t>(value); } - double GlpkLpSolver::getContinuousValue(uint_fast64_t variableIndex) const { + bool GlpkLpSolver::getBinaryValue(std::string const& name) const { if (!this->isOptimal()) { - if (this->isInfeasible()) { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from infeasible model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model."; - } else if (this->isUnbounded()) { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unbounded model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model."; - } else { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unoptimized model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model."; - } + LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from infeasible model."); + LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unbounded model."); + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unoptimized model."); } + auto variableIndexPair = this->variableNameToIndexMap.find(name); + LOG_THROW(variableIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidAccessException, "Accessing value of unknown variable '" << name << "'."); + double value = 0; if (this->modelContainsIntegerVariables) { - value = glp_mip_col_val(this->lp, static_cast<int>(variableIndex)); + value = glp_mip_col_val(this->lp, static_cast<int>(variableIndexPair->second)); } else { - value = glp_get_col_prim(this->lp, static_cast<int>(variableIndex)); + value = glp_get_col_prim(this->lp, static_cast<int>(variableIndexPair->second)); } - return value; + + LOG_THROW(std::abs(static_cast<int>(value) - value) <= storm::settings::Settings::getInstance()->getOptionByLongName("glpkinttol").getArgument(0).getValueAsDouble(), storm::exceptions::InvalidStateException, "Illegal value for binary variable in glpk solution (" << value << ")."); + + return static_cast<bool>(value); } double GlpkLpSolver::getObjectiveValue() const { if (!this->isOptimal()) { - if (this->isInfeasible()) { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from infeasible model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model."; - } else if (this->isUnbounded()) { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unbounded model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model."; - } else { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unoptimized model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model."; - } + LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from infeasible model."); + LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unbounded model."); + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unoptimized model."); } double value = 0; diff --git a/src/solver/GlpkLpSolver.h b/src/solver/GlpkLpSolver.h index 2bc2bf397..85a0847af 100644 --- a/src/solver/GlpkLpSolver.h +++ b/src/solver/GlpkLpSolver.h @@ -14,7 +14,6 @@ namespace storm { namespace solver { #ifdef STORM_HAVE_GLPK - /*! * A class that implements the LpSolver interface using glpk as the background solver. */ @@ -56,34 +55,66 @@ namespace storm { */ virtual ~GlpkLpSolver(); - virtual uint_fast64_t createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override; - virtual uint_fast64_t createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override; - virtual uint_fast64_t createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) override; + // Methods to add continuous variables. + virtual void addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override; + virtual void addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override; + virtual void addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override; + virtual void addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override; + + // Methods to add integer variables. + virtual void addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override; + virtual void addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override; + virtual void addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override; + virtual void addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override; + + // Methods to add binary variables. + virtual void addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override; + + // Methods to incorporate recent changes. virtual void update() const override; - virtual void addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) override; + // Methods to add constraints + virtual void addConstraint(std::string const& name, storm::expressions::Expression const& constraint) override; + // Methods to optimize and retrieve optimality status. virtual void optimize() const override; virtual bool isInfeasible() const override; virtual bool isUnbounded() const override; virtual bool isOptimal() const override; - virtual int_fast64_t getIntegerValue(uint_fast64_t variableIndex) const override; - virtual bool getBinaryValue(uint_fast64_t variableIndex) const override; - virtual double getContinuousValue(uint_fast64_t variableIndex) const override; + // Methods to retrieve values of variables and the objective function in the optimal solutions. + virtual double getContinuousValue(std::string const& name) const override; + virtual int_fast64_t getIntegerValue(std::string const& name) const override; + virtual bool getBinaryValue(std::string const& name) const override; virtual double getObjectiveValue() const override; + // Methods to print the LP problem to a file. virtual void writeModelToFile(std::string const& filename) const override; private: + /*! + * Adds a variable with the given name, type, lower and upper bound and objective function coefficient. + * + * @param name The name of the variable. + * @param variableType The type of the variable in terms of glpk's constants. + * @param boundType A glpk flag indicating which bounds apply to the variable. + * @param lowerBound The lower bound of the range of the variable. + * @param upperBound The upper bound of the range of the variable. + * @param objectiveFunctionCoefficient The coefficient of the variable in the objective function. + */ + void addVariable(std::string const& name, int variableType, int boundType, double lowerBound, double upperBound, double objectiveFunctionCoefficient); + // The glpk LP problem. glp_prob* lp; - // A counter that keeps track of the next free variable index. - uint_fast64_t nextVariableIndex; + // A mapping from variable names to their indices. + std::map<std::string, int> variableNameToIndexMap; + + // A counter used for getting the next variable index. + int nextVariableIndex; - // A counter that keeps track of the next free constraint index. - uint_fast64_t nextConstraintIndex; + // A counter used for getting the next constraint index. + int nextConstraintIndex; // A flag storing whether the model is an LP or an MILP. bool modelContainsIntegerVariables; @@ -121,15 +152,39 @@ namespace storm { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - virtual uint_fast64_t createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override { + virtual void addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; + } + + virtual void addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; + } + + virtual void addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; + } + + virtual void addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; + } + + virtual void addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; + } + + virtual void addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; + } + + virtual void addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - virtual uint_fast64_t createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override { + virtual void addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - virtual uint_fast64_t createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) override { + virtual void addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } @@ -137,14 +192,14 @@ namespace storm { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - virtual void addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) override { + virtual void addConstraint(std::string const& name, storm::expressions::Expression const& constraint) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - + virtual void optimize() const override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - + virtual bool isInfeasible() const override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } @@ -157,15 +212,15 @@ namespace storm { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - virtual int_fast64_t getIntegerValue(uint_fast64_t variableIndex) const override { + virtual double getContinuousValue(std::string const& name) const override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - virtual bool getBinaryValue(uint_fast64_t variableIndex) const override { + virtual int_fast64_t getIntegerValue(std::string const& name) const override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - virtual double getContinuousValue(uint_fast64_t variableIndex) const override { + virtual bool getBinaryValue(std::string const& name) const override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } diff --git a/src/solver/GmmxxLinearEquationSolver.cpp b/src/solver/GmmxxLinearEquationSolver.cpp index f81f4267a..c7ef52247 100644 --- a/src/solver/GmmxxLinearEquationSolver.cpp +++ b/src/solver/GmmxxLinearEquationSolver.cpp @@ -152,7 +152,6 @@ namespace storm { // Set up some temporary variables so that we can just swap pointers instead of copying the result after // each iteration. - std::vector<ValueType>* swap = nullptr; std::vector<ValueType>* currentX = &x; bool multiplyResultProvided = true; diff --git a/src/solver/GmmxxNondeterministicLinearEquationSolver.cpp b/src/solver/GmmxxNondeterministicLinearEquationSolver.cpp index 29829f535..1b408d740 100644 --- a/src/solver/GmmxxNondeterministicLinearEquationSolver.cpp +++ b/src/solver/GmmxxNondeterministicLinearEquationSolver.cpp @@ -59,7 +59,6 @@ namespace storm { newX = new std::vector<ValueType>(x.size()); xMemoryProvided = false; } - std::vector<ValueType>* swap = nullptr; uint_fast64_t iterations = 0; bool converged = false; diff --git a/src/solver/GurobiLpSolver.cpp b/src/solver/GurobiLpSolver.cpp index c06233942..676867ecb 100644 --- a/src/solver/GurobiLpSolver.cpp +++ b/src/solver/GurobiLpSolver.cpp @@ -1,14 +1,14 @@ #include "src/solver/GurobiLpSolver.h" #ifdef STORM_HAVE_GUROBI +#include <numeric> -#include "src/exceptions/InvalidStateException.h" -#include "src/settings/Settings.h" +#include "src/storage/expressions/LinearCoefficientVisitor.h" -#include "log4cplus/logger.h" -#include "log4cplus/loggingmacros.h" - -extern log4cplus::Logger logger; +#include "src/settings/Settings.h" +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidStateException.h" +#include "src/exceptions/InvalidAccessException.h" bool GurobiLpSolverOptionsRegistered = storm::settings::Settings::registerNewModule([] (storm::settings::Settings* instance) -> bool { instance->addOption(storm::settings::OptionBuilder("GurobiLpSolver", "gurobithreads", "", "The number of threads that may be used by Gurobi.").addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("count", "The number of threads.").setDefaultValueUnsignedInteger(1).build()).build()); @@ -42,7 +42,7 @@ namespace storm { } } - GurobiLpSolver::GurobiLpSolver(std::string const& name) : GurobiLpSolver(name, MINIMIZE) { + GurobiLpSolver::GurobiLpSolver(std::string const& name) : GurobiLpSolver(name, ModelSense::Minimize) { // Intentionally left empty. } @@ -50,7 +50,7 @@ namespace storm { // Intentionally left empty. } - GurobiLpSolver::GurobiLpSolver() : GurobiLpSolver("", MINIMIZE) { + GurobiLpSolver::GurobiLpSolver() : GurobiLpSolver("", ModelSense::Minimize) { // Intentionally left empty. } @@ -65,128 +65,124 @@ namespace storm { // Enable the following line to only print the output of Gurobi if the debug flag is set. error = GRBsetintparam(env, "OutputFlag", storm::settings::Settings::getInstance()->isSet("debug") || storm::settings::Settings::getInstance()->isSet("gurobioutput") ? 1 : 0); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to set Gurobi Parameter OutputFlag (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi Parameter OutputFlag (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi Parameter OutputFlag (" << GRBgeterrormsg(env) << ", error code " << error << ")."); // Enable the following line to restrict Gurobi to one thread only. error = GRBsetintparam(env, "Threads", storm::settings::Settings::getInstance()->getOptionByLongName("gurobithreads").getArgument(0).getValueAsUnsignedInteger()); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to set Gurobi Parameter Threads (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi Parameter Threads (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi Parameter Threads (" << GRBgeterrormsg(env) << ", error code " << error << ")."); // Enable the following line to force Gurobi to be as precise about the binary variables as required by the given precision option. error = GRBsetdblparam(env, "IntFeasTol", storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble()); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to set Gurobi Parameter IntFeasTol (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi Parameter IntFeasTol (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi Parameter IntFeasTol (" << GRBgeterrormsg(env) << ", error code " << error << ")."); } void GurobiLpSolver::update() const { int error = GRBupdatemodel(model); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to update Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to update Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to update Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); // Since the model changed, we erase the optimality flag. this->currentModelHasBeenOptimized = false; } - uint_fast64_t GurobiLpSolver::createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { - int error = 0; - switch (variableType) { - case LpSolver::BOUNDED: - error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, lowerBound, upperBound, GRB_CONTINUOUS, name.c_str()); - break; - case LpSolver::UNBOUNDED: - error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, -GRB_INFINITY, GRB_INFINITY, GRB_CONTINUOUS, name.c_str()); - break; - case LpSolver::UPPER_BOUND: - error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, -GRB_INFINITY, upperBound, GRB_CONTINUOUS, name.c_str()); - break; - case LpSolver::LOWER_BOUND: - error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, lowerBound, GRB_INFINITY, GRB_CONTINUOUS, name.c_str()); - break; - } - - if (error) { - LOG4CPLUS_ERROR(logger, "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } - ++nextVariableIndex; - return nextVariableIndex - 1; + void GurobiLpSolver::addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_CONTINUOUS, lowerBound, upperBound, objectiveFunctionCoefficient); } - uint_fast64_t GurobiLpSolver::createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { - int error = 0; - switch (variableType) { - case LpSolver::BOUNDED: - error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, lowerBound, upperBound, GRB_INTEGER, name.c_str()); - break; - case LpSolver::UNBOUNDED: - error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, -GRB_INFINITY, GRB_INFINITY, GRB_INTEGER, name.c_str()); - break; - case LpSolver::UPPER_BOUND: - error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, -GRB_INFINITY, upperBound, GRB_INTEGER, name.c_str()); - break; - case LpSolver::LOWER_BOUND: - error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, lowerBound, GRB_INFINITY, GRB_INTEGER, name.c_str()); - break; - } + void GurobiLpSolver::addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_CONTINUOUS, lowerBound, GRB_INFINITY, objectiveFunctionCoefficient); + } - if (error) { - LOG4CPLUS_ERROR(logger, "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } - ++nextVariableIndex; - return nextVariableIndex - 1; + void GurobiLpSolver::addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_CONTINUOUS, -GRB_INFINITY, upperBound, objectiveFunctionCoefficient); + } + + void GurobiLpSolver::addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_CONTINUOUS, -GRB_INFINITY, GRB_INFINITY, objectiveFunctionCoefficient); } - uint_fast64_t GurobiLpSolver::createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) { - int error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, 0.0, 1.0, GRB_BINARY, name.c_str()); - if (error) { - LOG4CPLUS_ERROR(logger, "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } - ++nextVariableIndex; - return nextVariableIndex - 1; + void GurobiLpSolver::addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_INTEGER, lowerBound, upperBound, objectiveFunctionCoefficient); } - void GurobiLpSolver::addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) { - if (variables.size() != coefficients.size()) { - LOG4CPLUS_ERROR(logger, "Sizes of variable indices vector and coefficients vector do not match."); - throw storm::exceptions::InvalidStateException() << "Sizes of variable indices vector and coefficients vector do not match."; - } - - // Convert the uint vector to ints for proper input to Gurobi. - std::vector<int> variablesCopy(variables.begin(), variables.end()); + void GurobiLpSolver::addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_INTEGER, lowerBound, GRB_INFINITY, objectiveFunctionCoefficient); + } + + void GurobiLpSolver::addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_INTEGER, -GRB_INFINITY, upperBound, objectiveFunctionCoefficient); + } + + void GurobiLpSolver::addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_INTEGER, -GRB_INFINITY, GRB_INFINITY, objectiveFunctionCoefficient); + } + + void GurobiLpSolver::addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_BINARY, 0, 1, objectiveFunctionCoefficient); + } + + void GurobiLpSolver::addVariable(std::string const& name, char variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { + // Check whether variable already exists. + auto nameIndexPair = this->variableNameToIndexMap.find(name); + LOG_THROW(nameIndexPair == this->variableNameToIndexMap.end(), storm::exceptions::InvalidArgumentException, "Variable '" << nameIndexPair->first << "' already exists."); - // Copy the coefficients, because Gurobi does not take the coefficients as const values. The alternative to this would be casting - // away the constness, which gives undefined behaviour if Gurobi actually modifies something. - std::vector<double> coefficientsCopy(coefficients); + // Check for valid variable type. + LOG_ASSERT(variableType == GRB_CONTINUOUS || variableType == GRB_INTEGER || variableType == GRB_BINARY, "Illegal type '" << variableType << "' for Gurobi variable."); - bool strictBound = boundType == LESS || boundType == GREATER; - char sense = boundType == LESS || boundType == LESS_EQUAL ? GRB_LESS_EQUAL : boundType == EQUAL ? GRB_EQUAL : GRB_GREATER_EQUAL; + // Finally, create the actual variable. + int error = 0; + error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, lowerBound, upperBound, variableType, name.c_str()); + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ")."); + this->variableNameToIndexMap.emplace(name, nextVariableIndex); + ++nextVariableIndex; + } + + void GurobiLpSolver::addConstraint(std::string const& name, storm::expressions::Expression const& constraint) { + LOG_THROW(constraint.isRelationalExpression(), storm::exceptions::InvalidArgumentException, "Illegal constraint is not a relational expression."); + LOG_THROW(constraint.getOperator() != storm::expressions::OperatorType::NotEqual, storm::exceptions::InvalidArgumentException, "Illegal constraint uses inequality operator."); - // If the constraint enforces a strict bound, we need to do some tweaking of the right-hand side value, because Gurobi only supports - // non-strict bounds. - if (strictBound) { - if (boundType == LESS) { - rightHandSideValue -= storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble(); - } else if (boundType == GREATER) { - rightHandSideValue += storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble(); + std::pair<storm::expressions::SimpleValuation, double> leftCoefficients = storm::expressions::LinearCoefficientVisitor().getLinearCoefficients(constraint.getOperand(0)); + std::pair<storm::expressions::SimpleValuation, double> rightCoefficients = storm::expressions::LinearCoefficientVisitor().getLinearCoefficients(constraint.getOperand(1)); + for (auto const& identifier : rightCoefficients.first.getDoubleIdentifiers()) { + if (leftCoefficients.first.containsDoubleIdentifier(identifier)) { + leftCoefficients.first.setDoubleValue(identifier, leftCoefficients.first.getDoubleValue(identifier) - rightCoefficients.first.getDoubleValue(identifier)); + } else { + leftCoefficients.first.addDoubleIdentifier(identifier, -rightCoefficients.first.getDoubleValue(identifier)); } } - int error = GRBaddconstr(model, variablesCopy.size(), variablesCopy.data(), coefficientsCopy.data(), sense, rightHandSideValue, name == "" ? nullptr : name.c_str()); + rightCoefficients.second -= leftCoefficients.second; - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to assert Gurobi constraint (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to assert Gurobi constraint (" << GRBgeterrormsg(env) << ", error code " << error << ")."; + // Now we need to transform the coefficients to the vector representation. + std::vector<int> variables; + std::vector<double> coefficients; + for (auto const& identifier : leftCoefficients.first.getDoubleIdentifiers()) { + auto identifierIndexPair = this->variableNameToIndexMap.find(identifier); + LOG_THROW(identifierIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidArgumentException, "Constraint contains illegal identifier '" << identifier << "'."); + variables.push_back(identifierIndexPair->second); + coefficients.push_back(leftCoefficients.first.getDoubleValue(identifier)); } + + // Determine the type of the constraint and add it properly. + int error = 0; + switch (constraint.getOperator()) { + case storm::expressions::OperatorType::Less: + error = GRBaddconstr(model, variables.size(), variables.data(), coefficients.data(), GRB_LESS_EQUAL, rightCoefficients.second - storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble(), name == "" ? nullptr : name.c_str()); + break; + case storm::expressions::OperatorType::LessOrEqual: + error = GRBaddconstr(model, variables.size(), variables.data(), coefficients.data(), GRB_LESS_EQUAL, rightCoefficients.second, name == "" ? nullptr : name.c_str()); + break; + case storm::expressions::OperatorType::Greater: + error = GRBaddconstr(model, variables.size(), variables.data(), coefficients.data(), GRB_GREATER_EQUAL, rightCoefficients.second + storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble(), name == "" ? nullptr : name.c_str()); + break; + case storm::expressions::OperatorType::GreaterOrEqual: + error = GRBaddconstr(model, variables.size(), variables.data(), coefficients.data(), GRB_GREATER_EQUAL, rightCoefficients.second, name == "" ? nullptr : name.c_str()); + break; + case storm::expressions::OperatorType::Equal: + error = GRBaddconstr(model, variables.size(), variables.data(), coefficients.data(), GRB_EQUAL, rightCoefficients.second, name == "" ? nullptr : name.c_str()); + break; + default: + LOG_ASSERT(false, "Illegal operator in LP solver constraint."); + } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Could not assert constraint (" << GRBgeterrormsg(env) << ", error code " << error << ")."); } void GurobiLpSolver::optimize() const { @@ -194,18 +190,12 @@ namespace storm { this->update(); // Set the most recently set model sense. - int error = GRBsetintattr(model, "ModelSense", this->getModelSense() == MINIMIZE ? 1 : -1); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to set Gurobi model sense (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi model sense (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + int error = GRBsetintattr(model, "ModelSense", this->getModelSense() == ModelSense::Minimize ? 1 : -1); + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi model sense (" << GRBgeterrormsg(env) << ", error code " << error << ")."); // Then we actually optimize the model. error = GRBoptimize(model); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to optimize Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to optimize Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to optimize Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); this->currentModelHasBeenOptimized = true; } @@ -218,34 +208,21 @@ namespace storm { int optimalityStatus = 0; int error = GRBgetintattr(model, GRB_INT_ATTR_STATUS, &optimalityStatus); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); // By default, Gurobi may tell us only that the model is either infeasible or unbounded. To decide which one // it is, we need to perform an extra step. if (optimalityStatus == GRB_INF_OR_UNBD) { - std::cout << "here" << std::endl; error = GRBsetintparam(GRBgetenv(model), GRB_INT_PAR_DUALREDUCTIONS, 0); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."); this->optimize(); error = GRBgetintattr(model, GRB_INT_ATTR_STATUS, &optimalityStatus); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); error = GRBsetintparam(GRBgetenv(model), GRB_INT_PAR_DUALREDUCTIONS, 1); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."); } return optimalityStatus == GRB_INFEASIBLE; @@ -259,33 +236,21 @@ namespace storm { int optimalityStatus = 0; int error = GRBgetintattr(model, GRB_INT_ATTR_STATUS, &optimalityStatus); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); // By default, Gurobi may tell us only that the model is either infeasible or unbounded. To decide which one // it is, we need to perform an extra step. if (optimalityStatus == GRB_INF_OR_UNBD) { error = GRBsetintparam(GRBgetenv(model), GRB_INT_PAR_DUALREDUCTIONS, 0); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."); this->optimize(); error = GRBgetintattr(model, GRB_INT_ATTR_STATUS, &optimalityStatus); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); error = GRBsetintparam(GRBgetenv(model), GRB_INT_PAR_DUALREDUCTIONS, 1); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."); } return optimalityStatus == GRB_UNBOUNDED; @@ -298,120 +263,79 @@ namespace storm { int optimalityStatus = 0; int error = GRBgetintattr(model, GRB_INT_ATTR_STATUS, &optimalityStatus); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); return optimalityStatus == GRB_OPTIMAL; } - int_fast64_t GurobiLpSolver::getIntegerValue(uint_fast64_t variableIndex) const { + double GurobiLpSolver::getContinuousValue(std::string const& name) const { if (!this->isOptimal()) { - if (this->isInfeasible()) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."; - } else if (this->isUnbounded()) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."; - } else { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."; - } + LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."); + LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."); + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."); } - double value = 0; - int error = GRBgetdblattrelement(model, GRB_DBL_ATTR_X, variableIndex, &value); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + auto variableIndexPair = this->variableNameToIndexMap.find(name); + LOG_THROW(variableIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidAccessException, "Accessing value of unknown variable '" << name << "'."); - if (std::abs(value - static_cast<int>(value)) <= storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble()) { - // Nothing to do in this case. - } else if (std::abs(value) > storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble()) { - LOG4CPLUS_ERROR(logger, "Illegal value for integer variable in Gurobi solution (" << value << ")."); - throw storm::exceptions::InvalidStateException() << "Illegal value for integer variable in Gurobi solution (" << value << ")."; - } + double value = 0; + int error = GRBgetdblattrelement(model, GRB_DBL_ATTR_X, variableIndexPair->second, &value); + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - return static_cast<int_fast64_t>(value); + return value; } - bool GurobiLpSolver::getBinaryValue(uint_fast64_t variableIndex) const { + int_fast64_t GurobiLpSolver::getIntegerValue(std::string const& name) const { if (!this->isOptimal()) { - if (this->isInfeasible()) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."; - } else if (this->isUnbounded()) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."; - } else { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."; - } - } - - double value = 0; - int error = GRBgetdblattrelement(model, GRB_DBL_ATTR_X, variableIndex, &value); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."; + LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."); + LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."); + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."); } - if (std::abs(value - 1) <= storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble()) { - // Nothing to do in this case. - } else if (std::abs(value) > storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble()) { - LOG4CPLUS_ERROR(logger, "Illegal value for binary variable in Gurobi solution (" << value << ")."); - throw storm::exceptions::InvalidStateException() << "Illegal value for binary variable in Gurobi solution (" << value << ")."; - } + auto variableIndexPair = this->variableNameToIndexMap.find(name); + LOG_THROW(variableIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidAccessException, "Accessing value of unknown variable '" << name << "'."); - return static_cast<bool>(value); + double value = 0; + int error = GRBgetdblattrelement(model, GRB_DBL_ATTR_X, variableIndexPair->second, &value); + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."); + LOG_THROW(std::abs(static_cast<int>(value) - value) <= storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble(), storm::exceptions::InvalidStateException, "Illegal value for integer variable in Gurobi solution (" << value << ")."); + + return static_cast<int_fast64_t>(value); } - double GurobiLpSolver::getContinuousValue(uint_fast64_t variableIndex) const { + bool GurobiLpSolver::getBinaryValue(std::string const& name) const { if (!this->isOptimal()) { - if (this->isInfeasible()) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."; - } else if (this->isUnbounded()) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."; - } else { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."; - } + LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."); + LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."); + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."); } + auto variableIndexPair = this->variableNameToIndexMap.find(name); + LOG_THROW(variableIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidAccessException, "Accessing value of unknown variable '" << name << "'."); + double value = 0; - int error = GRBgetdblattrelement(model, GRB_DBL_ATTR_X, variableIndex, &value); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."; + int error = GRBgetdblattrelement(model, GRB_DBL_ATTR_X, variableIndexPair->second, &value); + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."); + + if (value > 0.5) { + LOG_THROW(std::abs(static_cast<int>(value) - 1) <= storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble(), storm::exceptions::InvalidStateException, "Illegal value for integer variable in Gurobi solution (" << value << ")."); + } else { + LOG_THROW(value <= storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble(), storm::exceptions::InvalidStateException, "Illegal value for integer variable in Gurobi solution (" << value << ")."); } - return value; + return static_cast<bool>(value); } double GurobiLpSolver::getObjectiveValue() const { if (!this->isOptimal()) { - if (this->isInfeasible()) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."; - } else if (this->isUnbounded()) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."; - } else { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."; - } + LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."); + LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."); + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."); } double value = 0; int error = GRBgetdblattr(model, GRB_DBL_ATTR_OBJVAL, &value); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."); return value; } diff --git a/src/solver/GurobiLpSolver.h b/src/solver/GurobiLpSolver.h index 18c5bbdbd..f078ef6dd 100644 --- a/src/solver/GurobiLpSolver.h +++ b/src/solver/GurobiLpSolver.h @@ -60,23 +60,40 @@ namespace storm { */ virtual ~GurobiLpSolver(); - virtual uint_fast64_t createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override; - virtual uint_fast64_t createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override; - virtual uint_fast64_t createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) override; + // Methods to add continuous variables. + virtual void addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override; + virtual void addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override; + virtual void addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override; + virtual void addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override; + + // Methods to add integer variables. + virtual void addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override; + virtual void addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override; + virtual void addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override; + virtual void addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override; + + // Methods to add binary variables. + virtual void addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override; + + // Methods to incorporate recent changes. virtual void update() const override; - - virtual void addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) override; + // Methods to add constraints + virtual void addConstraint(std::string const& name, storm::expressions::Expression const& constraint) override; + + // Methods to optimize and retrieve optimality status. virtual void optimize() const override; virtual bool isInfeasible() const override; virtual bool isUnbounded() const override; virtual bool isOptimal() const override; - - virtual int_fast64_t getIntegerValue(uint_fast64_t variableIndex) const override; - virtual bool getBinaryValue(uint_fast64_t variableIndex) const override; - virtual double getContinuousValue(uint_fast64_t variableIndex) const override; + + // Methods to retrieve values of variables and the objective function in the optimal solutions. + virtual double getContinuousValue(std::string const& name) const override; + virtual int_fast64_t getIntegerValue(std::string const& name) const override; + virtual bool getBinaryValue(std::string const& name) const override; virtual double getObjectiveValue() const override; - + + // Methods to print the LP problem to a file. virtual void writeModelToFile(std::string const& filename) const override; private: @@ -85,14 +102,28 @@ namespace storm { */ void setGurobiEnvironmentProperties() const; + /*! + * Adds a variable with the given name, type, lower and upper bound and objective function coefficient. + * + * @param name The name of the variable. + * @param variableType The type of the variable in terms of Gurobi's constants. + * @param lowerBound The lower bound of the range of the variable. + * @param upperBound The upper bound of the range of the variable. + * @param objectiveFunctionCoefficient The coefficient of the variable in the objective function. + */ + void addVariable(std::string const& name, char variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient); + // The Gurobi environment. GRBenv* env; // The Gurobi model. GRBmodel* model; - // A counter that keeps track of the next free variable index. - uint_fast64_t nextVariableIndex; + // The index of the next variable. + int nextVariableIndex; + + // A mapping from variable names to their indices. + std::map<std::string, int> variableNameToIndexMap; }; #else // If Gurobi is not available, we provide a stub implementation that emits an error if any of its methods is called. @@ -114,27 +145,46 @@ namespace storm { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual uint_fast64_t createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override { + virtual void addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual uint_fast64_t createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override { + virtual void addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } + + virtual void addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual uint_fast64_t createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) override { + virtual void addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual void update() const override { + virtual void addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; + } + + virtual void addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; + } + + virtual void addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; + } + + virtual void addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual void addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) override { + virtual void addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual void setModelSense(ModelSense const& modelSense) { + virtual void update() const override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; + } + + virtual void addConstraint(std::string const& name, storm::expressions::Expression const& constraint) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } @@ -154,15 +204,15 @@ namespace storm { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual int_fast64_t getIntegerValue(uint_fast64_t variableIndex) const override { + virtual double getContinuousValue(std::string const& name) const override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual bool getBinaryValue(uint_fast64_t variableIndex) const override { + virtual int_fast64_t getIntegerValue(std::string const& name) const override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual double getContinuousValue(uint_fast64_t variableIndex) const override { + virtual bool getBinaryValue(std::string const& name) const override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } @@ -175,7 +225,7 @@ namespace storm { } }; #endif - + } } diff --git a/src/solver/LpSolver.h b/src/solver/LpSolver.h index 311ee4b44..8c729a323 100644 --- a/src/solver/LpSolver.h +++ b/src/solver/LpSolver.h @@ -5,42 +5,25 @@ #include <vector> #include <cstdint> +#include "src/storage/expressions/Expression.h" + namespace storm { namespace solver { - /*! * An interface that captures the functionality of an LP solver. */ class LpSolver { public: - // An enumeration to represent the possible types of variables. Variables may be either unbounded, have only - // a lower or an upper bound, respectively, or be bounded from below and from above. - enum VariableType { - UNBOUNDED, - LOWER_BOUND, - UPPER_BOUND, - BOUNDED, - }; - - // An enumeration to represent the possible types of constraints. - enum BoundType { - LESS, - LESS_EQUAL, - GREATER, - GREATER_EQUAL, - EQUAL - }; - // An enumeration to represent whether the objective function is to be minimized or maximized. - enum ModelSense { - MINIMIZE, - MAXIMIZE + enum class ModelSense { + Minimize, + Maximize }; /*! * Creates an empty LP solver. By default the objective function is assumed to be minimized. */ - LpSolver() : currentModelHasBeenOptimized(false), modelSense(MINIMIZE) { + LpSolver() : currentModelHasBeenOptimized(false), modelSense(ModelSense::Minimize) { // Intentionally left empty. } @@ -55,52 +38,99 @@ namespace storm { } /*! - * Creates a continuous variable, i.e. a variable that may take all real values within its bounds. + * Registers an upper- and lower-bounded continuous variable, i.e. a variable that may take all real values + * within its bounds. + * + * @param name The name of the variable. + * @param lowerBound The lower bound of the variable. + * @param upperBound The upper bound of the variable. + * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. + */ + virtual void addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) = 0; + + /*! + * Registers a lower-bounded continuous variable, i.e. a variable that may take all real values up to its + * lower bound. + * + * @param name The name of the variable. + * @param lowerBound The lower bound of the variable. + * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. + */ + virtual void addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) = 0; + + /*! + * Registers an upper-bounded continuous variable, i.e. a variable that may take all real values up to its + * upper bound. + * + * @param name The name of the variable. + * @param upperBound The upper bound of the variable. + * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. + */ + virtual void addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) = 0; + + /*! + * Registers a unbounded continuous variable, i.e. a variable that may take all real values. * * @param name The name of the variable. - * @param variableType The type of the variable. - * @param lowerBound The lower bound of the variable. Note that this parameter is ignored if the variable - * is not bounded from below. - * @param upperBound The upper bound of the variable. Note that this parameter is ignored if the variable - * is not bounded from above. * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective - * function. If set to zero, the variable is irrelevant for the objective value. - * @return The index of the newly created variable. This index can be used to retrieve the variables value - * in a solution after the model has been optimized. + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. */ - virtual uint_fast64_t createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) = 0; + virtual void addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient = 0) = 0; /*! - * Creates an integer variable. + * Registers an upper- and lower-bounded integer variable, i.e. a variable that may take all integer values + * within its bounds. + * + * @param name The name of the variable. + * @param lowerBound The lower bound of the variable. + * @param upperBound The upper bound of the variable. + * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. + */ + virtual void addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) = 0; + + /*! + * Registers a lower-bounded integer variable, i.e. a variable that may take all integer values up to its + * lower bound. + * + * @param name The name of the variable. + * @param lowerBound The lower bound of the variable. + * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. + */ + virtual void addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) = 0; + + /*! + * Registers an upper-bounded integer variable, i.e. a variable that may take all integer values up to its + * lower bound. + * + * @param name The name of the variable. + * @param upperBound The upper bound of the variable. + * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. + */ + virtual void addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) = 0; + + /*! + * Registers an unbounded integer variable, i.e. a variable that may take all integer values. * * @param name The name of the variable. - * @param variableType The type of the variable. - * @param lowerBound The lower bound of the variable. Note that this parameter is ignored if the variable - * is not bounded from below. - * @param upperBound The upper bound of the variable. Note that this parameter is ignored if the variable - * is not bounded from above. * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective - * function. If set to zero, the variable is irrelevant for the objective value. - * @return The index of the newly created variable. This index can be used to retrieve the variables value - * in a solution after the model has been optimized. + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. */ - virtual uint_fast64_t createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) = 0; + virtual void addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient = 0) = 0; /*! - * Creates a binary variable, i.e. a variable that may be either zero or one. + * Registers a boolean variable, i.e. a variable that may be either 0 or 1. * * @param name The name of the variable. - * @param variableType The type of the variable. - * @param lowerBound The lower bound of the variable. Note that this parameter is ignored if the variable - * is not bounded from below. - * @param upperBound The upper bound of the variable. Note that this parameter is ignored if the variable - * is not bounded from above. * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective - * function. If set to zero, the variable is irrelevant for the objective value. - * @return The index of the newly created variable. This index can be used to retrieve the variables value - * in a solution after the model has been optimized. + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. */ - virtual uint_fast64_t createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) = 0; + virtual void addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient = 0) = 0; /*! * Updates the model to make the variables that have been declared since the last call to <code>update</code> @@ -109,18 +139,13 @@ namespace storm { virtual void update() const = 0; /*! - * Adds a constraint of the form - * a_1*x_1 + ... + a_n*x_n op c - * to the LP problem. + * Adds a the given constraint to the LP problem. * * @param name The name of the constraint. If empty, the constraint has no name. - * @param variables A vector of variable indices that define the appearing variables x_1, ..., x_n. - * @param coefficients A vector of coefficients that define the a_1, ..., a_n. The i-ith entry specifies the - * coefficient of the variable whose index appears at the i-th position in the vector of variable indices. - * @param boundType The bound type specifies the operator op used in the constraint. - * @param rhs This defines the value of the constant appearing on the right-hand side of the constraint. + * @param constraint An expression that represents the constraint. The given constraint must be a linear + * (in)equality over the registered variables. */ - virtual void addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rhs) = 0; + virtual void addConstraint(std::string const& name, storm::expressions::Expression const& constraint) = 0; /*! * Optimizes the LP problem previously constructed. Afterwards, the methods isInfeasible, isUnbounded and @@ -154,34 +179,31 @@ namespace storm { virtual bool isOptimal() const = 0; /*! - * Retrieves the value of the integer variable with the given index. Note that this may only be called, if + * Retrieves the value of the integer variable with the given name. Note that this may only be called, if * the model was found to be optimal, i.e. iff isOptimal() returns true. * - * @param variableIndex The index of the integer variable whose value to query. If this index does not - * belong to a previously declared integer variable, the behaviour is undefined. + * @param name The name of the variable whose integer value (in the optimal solution) to retrieve. * @return The value of the integer variable in the optimal solution. */ - virtual int_fast64_t getIntegerValue(uint_fast64_t variableIndex) const = 0; + virtual int_fast64_t getIntegerValue(std::string const& name) const = 0; /*! - * Retrieves the value of the binary variable with the given index. Note that this may only be called, if + * Retrieves the value of the binary variable with the given name. Note that this may only be called, if * the model was found to be optimal, i.e. iff isOptimal() returns true. * - * @param variableIndex The index of the binary variable whose value to query. If this index does not - * belong to a previously declared binary variable, the behaviour is undefined. + * @param name The name of the variable whose binary (boolean) value (in the optimal solution) to retrieve. * @return The value of the binary variable in the optimal solution. */ - virtual bool getBinaryValue(uint_fast64_t variableIndex) const = 0; + virtual bool getBinaryValue(std::string const& name) const = 0; /*! - * Retrieves the value of the continuous variable with the given index. Note that this may only be called, + * Retrieves the value of the continuous variable with the given name. Note that this may only be called, * if the model was found to be optimal, i.e. iff isOptimal() returns true. * - * @param variableIndex The index of the continuous variable whose value to query. If this index does not - * belong to a previously declared continuous variable, the behaviour is undefined. + * @param name The name of the variable whose continuous value (in the optimal solution) to retrieve. * @return The value of the continuous variable in the optimal solution. */ - virtual double getContinuousValue(uint_fast64_t variableIndex) const = 0; + virtual double getContinuousValue(std::string const& name) const = 0; /*! * Retrieves the value of the objective function. Note that this may only be called, if the model was found diff --git a/src/solver/NativeLinearEquationSolver.cpp b/src/solver/NativeLinearEquationSolver.cpp index d55b4e2e5..d26ad0344 100644 --- a/src/solver/NativeLinearEquationSolver.cpp +++ b/src/solver/NativeLinearEquationSolver.cpp @@ -106,7 +106,6 @@ namespace storm { void NativeLinearEquationSolver<ValueType>::performMatrixVectorMultiplication(storm::storage::SparseMatrix<ValueType> const& A, std::vector<ValueType>& x, std::vector<ValueType>* b, uint_fast64_t n, std::vector<ValueType>* multiplyResult) const { // Set up some temporary variables so that we can just swap pointers instead of copying the result after // each iteration. - std::vector<ValueType>* swap = nullptr; std::vector<ValueType>* currentX = &x; bool multiplyResultProvided = true; diff --git a/src/solver/NativeNondeterministicLinearEquationSolver.cpp b/src/solver/NativeNondeterministicLinearEquationSolver.cpp index 5dc25f2c1..7f88258f1 100644 --- a/src/solver/NativeNondeterministicLinearEquationSolver.cpp +++ b/src/solver/NativeNondeterministicLinearEquationSolver.cpp @@ -54,7 +54,6 @@ namespace storm { newX = new std::vector<ValueType>(x.size()); xMemoryProvided = false; } - std::vector<ValueType>* swap = nullptr; uint_fast64_t iterations = 0; bool converged = false; diff --git a/src/storage/BitVector.cpp b/src/storage/BitVector.cpp index b4b282e0b..6907c45be 100644 --- a/src/storage/BitVector.cpp +++ b/src/storage/BitVector.cpp @@ -200,7 +200,6 @@ namespace storm { } BitVector& BitVector::operator&=(BitVector const& other) { - uint_fast64_t minSize = std::min(other.bucketVector.size(), bucketVector.size()); std::vector<uint64_t>::iterator it = bucketVector.begin(); for (std::vector<uint64_t>::const_iterator otherIt = other.bucketVector.begin(); it != bucketVector.end() && otherIt != other.bucketVector.end(); ++it, ++otherIt) { @@ -224,7 +223,6 @@ namespace storm { } BitVector& BitVector::operator|=(BitVector const& other) { - uint_fast64_t minSize = std::min(other.bucketVector.size(), bucketVector.size()); std::vector<uint64_t>::iterator it = bucketVector.begin(); for (std::vector<uint64_t>::const_iterator otherIt = other.bucketVector.begin(); it != bucketVector.end() && otherIt != other.bucketVector.end(); ++it, ++otherIt) { diff --git a/src/storage/MaximalEndComponentDecomposition.cpp b/src/storage/MaximalEndComponentDecomposition.cpp index 56ebbd34c..0f6a44f65 100644 --- a/src/storage/MaximalEndComponentDecomposition.cpp +++ b/src/storage/MaximalEndComponentDecomposition.cpp @@ -88,7 +88,7 @@ namespace storm { for (uint_fast64_t choice = nondeterministicChoiceIndices[state]; choice < nondeterministicChoiceIndices[state + 1]; ++choice) { bool choiceContainedInMEC = true; for (auto const& entry : transitionMatrix.getRow(choice)) { - if (scc.find(entry.first) == scc.end()) { + if (scc.find(entry.getColumn()) == scc.end()) { choiceContainedInMEC = false; break; } @@ -116,8 +116,8 @@ namespace storm { statesToCheck.clear(); for (auto state : statesToRemove) { for (auto const& entry : backwardTransitions.getRow(state)) { - if (scc.find(entry.first) != scc.end()) { - statesToCheck.set(entry.first); + if (scc.find(entry.getColumn()) != scc.end()) { + statesToCheck.set(entry.getColumn()); } } } @@ -154,7 +154,7 @@ namespace storm { for (uint_fast64_t choice = nondeterministicChoiceIndices[state]; choice < nondeterministicChoiceIndices[state + 1]; ++choice) { bool choiceContained = true; for (auto const& entry : transitionMatrix.getRow(choice)) { - if (mecStateSet.find(entry.first) == mecStateSet.end()) { + if (mecStateSet.find(entry.getColumn()) == mecStateSet.end()) { choiceContained = false; break; } diff --git a/src/storage/SparseMatrix.cpp b/src/storage/SparseMatrix.cpp index 3aee26996..cf19ef98e 100644 --- a/src/storage/SparseMatrix.cpp +++ b/src/storage/SparseMatrix.cpp @@ -17,6 +17,41 @@ extern log4cplus::Logger logger; namespace storm { namespace storage { + template<typename T> + MatrixEntry<T>::MatrixEntry(uint_fast64_t column, T value) : entry(column, value) { + // Intentionally left empty. + } + + template<typename T> + MatrixEntry<T>::MatrixEntry(std::pair<uint_fast64_t, T>&& pair) : entry(std::move(pair)) { + // Intentionally left empty. + } + + template<typename T> + uint_fast64_t const& MatrixEntry<T>::getColumn() const { + return this->entry.first; + } + + template<typename T> + uint_fast64_t& MatrixEntry<T>::getColumn() { + return this->entry.first; + } + + template<typename T> + T const& MatrixEntry<T>::getValue() const { + return this->entry.second; + } + + template<typename T> + T& MatrixEntry<T>::getValue() { + return this->entry.second; + } + + template<typename T> + std::pair<uint_fast64_t, T> const& MatrixEntry<T>::getColumnValuePair() const { + return this->entry; + } + template<typename T> SparseMatrixBuilder<T>::SparseMatrixBuilder(uint_fast64_t rows, uint_fast64_t columns, uint_fast64_t entries, bool hasCustomRowGrouping, uint_fast64_t rowGroups) : rowCountSet(rows != 0), rowCount(rows), columnCountSet(columns != 0), columnCount(columns), entryCount(entries), hasCustomRowGrouping(hasCustomRowGrouping), rowGroupCountSet(rowGroups != 0), rowGroupCount(rowGroups), rowGroupIndices(), storagePreallocated(rows != 0 && columns != 0 && entries != 0), columnsAndValues(), rowIndications(), currentEntryCount(0), lastRow(0), lastColumn(0), currentRowGroup(0) { this->prepareInternalStorage(); @@ -168,7 +203,7 @@ namespace storm { void SparseMatrixBuilder<T>::prepareInternalStorage() { // Only allocate the memory for the matrix contents if the dimensions of the matrix are already known. if (storagePreallocated) { - columnsAndValues = std::vector<std::pair<uint_fast64_t, T>>(entryCount, std::make_pair(0, storm::utility::constantZero<T>())); + columnsAndValues = std::vector<MatrixEntry<T>>(entryCount, MatrixEntry<T>(0, storm::utility::constantZero<T>())); rowIndications = std::vector<uint_fast64_t>(rowCount + 1, 0); } else { rowIndications.push_back(0); @@ -218,17 +253,17 @@ namespace storm { } template<typename T> - SparseMatrix<T>::SparseMatrix() : rowCount(0), columnCount(0), entryCount(0), columnsAndValues(), rowIndications(), rowGroupIndices() { + SparseMatrix<T>::SparseMatrix() : rowCount(0), columnCount(0), entryCount(0), nonzeroEntryCount(0), columnsAndValues(), rowIndications(), rowGroupIndices() { // Intentionally left empty. } template<typename T> - SparseMatrix<T>::SparseMatrix(SparseMatrix<T> const& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), columnsAndValues(other.columnsAndValues), rowIndications(other.rowIndications), rowGroupIndices(other.rowGroupIndices) { + SparseMatrix<T>::SparseMatrix(SparseMatrix<T> const& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), nonzeroEntryCount(other.nonzeroEntryCount), columnsAndValues(other.columnsAndValues), rowIndications(other.rowIndications), rowGroupIndices(other.rowGroupIndices) { // Intentionally left empty. } template<typename T> - SparseMatrix<T>::SparseMatrix(SparseMatrix<T>&& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), columnsAndValues(std::move(other.columnsAndValues)), rowIndications(std::move(other.rowIndications)), rowGroupIndices(std::move(other.rowGroupIndices)) { + SparseMatrix<T>::SparseMatrix(SparseMatrix<T>&& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), nonzeroEntryCount(other.nonzeroEntryCount), columnsAndValues(std::move(other.columnsAndValues)), rowIndications(std::move(other.rowIndications)), rowGroupIndices(std::move(other.rowGroupIndices)) { // Now update the source matrix other.rowCount = 0; other.columnCount = 0; @@ -236,13 +271,21 @@ namespace storm { } template<typename T> - SparseMatrix<T>::SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t> const& rowIndications, std::vector<std::pair<uint_fast64_t, T>> const& columnsAndValues, std::vector<uint_fast64_t> const& rowGroupIndices) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), columnsAndValues(columnsAndValues), rowIndications(rowIndications), rowGroupIndices(rowGroupIndices) { - // Intentionally left empty. + SparseMatrix<T>::SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t> const& rowIndications, std::vector<MatrixEntry<T>> const& columnsAndValues, std::vector<uint_fast64_t> const& rowGroupIndices) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), nonzeroEntryCount(0), columnsAndValues(columnsAndValues), rowIndications(rowIndications), rowGroupIndices(rowGroupIndices) { + for (auto const& element : *this) { + if (element.getValue() != storm::utility::constantZero<T>()) { + ++this->nonzeroEntryCount; + } + } } template<typename T> - SparseMatrix<T>::SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t>&& rowIndications, std::vector<std::pair<uint_fast64_t, T>>&& columnsAndValues, std::vector<uint_fast64_t>&& rowGroupIndices) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), columnsAndValues(std::move(columnsAndValues)), rowIndications(std::move(rowIndications)), rowGroupIndices(std::move(rowGroupIndices)) { - // Intentionally left empty. + SparseMatrix<T>::SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t>&& rowIndications, std::vector<MatrixEntry<T>>&& columnsAndValues, std::vector<uint_fast64_t>&& rowGroupIndices) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), nonzeroEntryCount(0), columnsAndValues(std::move(columnsAndValues)), rowIndications(std::move(rowIndications)), rowGroupIndices(std::move(rowGroupIndices)) { + for (auto const& element : *this) { + if (element.getValue() != storm::utility::constantZero<T>()) { + ++this->nonzeroEntryCount; + } + } } template<typename T> @@ -252,6 +295,7 @@ namespace storm { rowCount = other.rowCount; columnCount = other.columnCount; entryCount = other.entryCount; + nonzeroEntryCount = other.nonzeroEntryCount; columnsAndValues = other.columnsAndValues; rowIndications = other.rowIndications; @@ -268,6 +312,7 @@ namespace storm { rowCount = other.rowCount; columnCount = other.columnCount; entryCount = other.entryCount; + nonzeroEntryCount = other.nonzeroEntryCount; columnsAndValues = std::move(other.columnsAndValues); rowIndications = std::move(other.rowIndications); @@ -294,17 +339,17 @@ namespace storm { for (uint_fast64_t row = 0; row < this->getRowCount(); ++row) { for (const_iterator it1 = this->begin(row), ite1 = this->end(row), it2 = other.begin(row), ite2 = other.end(row); it1 != ite1 && it2 != ite2; ++it1, ++it2) { // Skip over all zero entries in both matrices. - while (it1 != ite1 && it1->second == storm::utility::constantZero<T>()) { + while (it1 != ite1 && it1->getValue() == storm::utility::constantZero<T>()) { ++it1; } - while (it2 != ite2 && it2->second == storm::utility::constantZero<T>()) { + while (it2 != ite2 && it2->getValue() == storm::utility::constantZero<T>()) { ++it2; } if ((it1 == ite1) || (it2 == ite2)) { equalityResult = (it1 == ite1) ^ (it2 == ite2); break; } else { - if (it1->first != it2->first || it1->second != it2->second) { + if (it1->getColumn() != it2->getColumn() || it1->getValue() != it2->getValue()) { equalityResult = false; break; } @@ -339,6 +384,11 @@ namespace storm { return result; } + template<typename T> + uint_fast64_t SparseMatrix<T>::getNonzeroEntryCount() const { + return nonzeroEntryCount; + } + template<typename T> uint_fast64_t SparseMatrix<T>::getRowGroupCount() const { return rowGroupIndices.size() - 1; @@ -383,12 +433,12 @@ namespace storm { // If there is at least one entry in this row, we can just set it to one, modify its column value to the // one given by the parameter and set all subsequent elements of this row to zero. - columnValuePtr->first = column; - columnValuePtr->second = storm::utility::constantOne<T>(); + columnValuePtr->getColumn() = column; + columnValuePtr->getValue() = storm::utility::constantOne<T>(); ++columnValuePtr; for (; columnValuePtr != columnValuePtrEnd; ++columnValuePtr) { - columnValuePtr->first = 0; - columnValuePtr->second = storm::utility::constantZero<T>(); + columnValuePtr->getColumn() = 0; + columnValuePtr->getValue() = storm::utility::constantZero<T>(); } } @@ -396,8 +446,8 @@ namespace storm { T SparseMatrix<T>::getConstrainedRowSum(uint_fast64_t row, storm::storage::BitVector const& constraint) const { T result(0); for (const_iterator it = this->begin(row), ite = this->end(row); it != ite; ++it) { - if (constraint.get(it->first)) { - result += it->second; + if (constraint.get(it->getColumn())) { + result += it->getValue(); } } return result; @@ -451,10 +501,10 @@ namespace storm { bool foundDiagonalElement = false; for (const_iterator it = this->begin(i), ite = this->end(i); it != ite; ++it) { - if (columnConstraint.get(it->first)) { + if (columnConstraint.get(it->getColumn())) { ++subEntries; - if (index == it->first) { + if (index == it->getColumn()) { foundDiagonalElement = true; } } @@ -501,14 +551,14 @@ namespace storm { bool insertedDiagonalElement = false; for (const_iterator it = this->begin(i), ite = this->end(i); it != ite; ++it) { - if (columnConstraint.get(it->first)) { - if (index == it->first) { + if (columnConstraint.get(it->getColumn())) { + if (index == it->getColumn()) { insertedDiagonalElement = true; - } else if (insertDiagonalEntries && !insertedDiagonalElement && it->first > index) { + } else if (insertDiagonalEntries && !insertedDiagonalElement && it->getColumn() > index) { matrixBuilder.addNextValue(rowCount, bitsSetBeforeIndex[index], storm::utility::constantZero<T>()); insertedDiagonalElement = true; } - matrixBuilder.addNextValue(rowCount, bitsSetBeforeIndex[it->first], it->second); + matrixBuilder.addNextValue(rowCount, bitsSetBeforeIndex[it->getColumn()], it->getValue()); } } if (insertDiagonalEntries && !insertedDiagonalElement) { @@ -534,7 +584,7 @@ namespace storm { // Iterate through that row and count the number of slots we have to reserve for copying. bool foundDiagonalElement = false; for (const_iterator it = this->begin(rowToCopy), ite = this->end(rowToCopy); it != ite; ++it) { - if (it->first == rowGroupIndex) { + if (it->getColumn() == rowGroupIndex) { foundDiagonalElement = true; } ++subEntries; @@ -556,13 +606,13 @@ namespace storm { // there is no entry yet. bool insertedDiagonalElement = false; for (const_iterator it = this->begin(rowToCopy), ite = this->end(rowToCopy); it != ite; ++it) { - if (it->first == rowGroupIndex) { + if (it->getColumn() == rowGroupIndex) { insertedDiagonalElement = true; - } else if (insertDiagonalEntries && !insertedDiagonalElement && it->first > rowGroupIndex) { + } else if (insertDiagonalEntries && !insertedDiagonalElement && it->getColumn() > rowGroupIndex) { matrixBuilder.addNextValue(rowGroupIndex, rowGroupIndex, storm::utility::constantZero<T>()); insertedDiagonalElement = true; } - matrixBuilder.addNextValue(rowGroupIndex, it->first, it->second); + matrixBuilder.addNextValue(rowGroupIndex, it->getColumn(), it->getValue()); } if (insertDiagonalEntries && !insertedDiagonalElement) { matrixBuilder.addNextValue(rowGroupIndex, rowGroupIndex, storm::utility::constantZero<T>()); @@ -580,13 +630,13 @@ namespace storm { uint_fast64_t entryCount = this->getEntryCount(); std::vector<uint_fast64_t> rowIndications(rowCount + 1); - std::vector<std::pair<uint_fast64_t, T>> columnsAndValues(entryCount); + std::vector<MatrixEntry<T>> columnsAndValues(entryCount); // First, we need to count how many entries each column has. for (uint_fast64_t group = 0; group < columnCount; ++group) { for (auto const& transition : joinGroups ? this->getRowGroup(group) : this->getRow(group)) { - if (transition.second != storm::utility::constantZero<T>()) { - ++rowIndications[transition.first + 1]; + if (transition.getValue() != storm::utility::constantZero<T>()) { + ++rowIndications[transition.getColumn() + 1]; } } } @@ -604,9 +654,9 @@ namespace storm { // Now we are ready to actually fill in the values of the transposed matrix. for (uint_fast64_t group = 0; group < columnCount; ++group) { for (auto const& transition : joinGroups ? this->getRowGroup(group) : this->getRow(group)) { - if (transition.second != storm::utility::constantZero<T>()) { - columnsAndValues[nextIndices[transition.first]] = std::make_pair(group, transition.second); - nextIndices[transition.first]++; + if (transition.getValue() != storm::utility::constantZero<T>()) { + columnsAndValues[nextIndices[transition.getColumn()]] = std::make_pair(group, transition.getValue()); + nextIndices[transition.getColumn()]++; } } } @@ -635,8 +685,8 @@ namespace storm { bool foundDiagonalElement = false; for (uint_fast64_t group = 0; group < this->getRowGroupCount(); ++group) { for (auto& entry : this->getRowGroup(group)) { - if (entry.first == group) { - entry.second = one - entry.second; + if (entry.getColumn() == group) { + entry.getValue() = one - entry.getValue(); foundDiagonalElement = true; } } @@ -653,8 +703,8 @@ namespace storm { // Iterate over all row groups and negate all the elements that are not on the diagonal. for (uint_fast64_t group = 0; group < this->getRowGroupCount(); ++group) { for (auto& entry : this->getRowGroup(group)) { - if (entry.first != group) { - entry.second = -entry.second; + if (entry.getColumn() != group) { + entry.getValue() = -entry.getValue(); } } } @@ -665,8 +715,8 @@ namespace storm { // Iterate over all rows and negate all the elements that are not on the diagonal. for (uint_fast64_t group = 0; group < this->getRowGroupCount(); ++group) { for (auto& entry : this->getRowGroup(group)) { - if (entry.first == group) { - entry.second = storm::utility::constantZero<T>(); + if (entry.getColumn() == group) { + entry.getValue() = storm::utility::constantZero<T>(); } } } @@ -689,9 +739,9 @@ namespace storm { // to invert the entry. T diagonalValue = storm::utility::constantZero<T>(); for (const_iterator it = this->begin(rowNumber), ite = this->end(rowNumber); it != ite; ++it) { - if (it->first == rowNumber) { - diagonalValue += it->second; - } else if (it->first > rowNumber) { + if (it->getColumn() == rowNumber) { + diagonalValue += it->getValue(); + } else if (it->getColumn() > rowNumber) { break; } } @@ -710,13 +760,13 @@ namespace storm { // add the result to the corresponding position in the vector. for (uint_fast64_t row = 0; row < rowCount && row < otherMatrix.rowCount; ++row) { for (const_iterator it1 = this->begin(row), ite1 = this->end(row), it2 = otherMatrix.begin(row), ite2 = otherMatrix.end(row); it1 != ite1 && it2 != ite2; ++it1) { - if (it1->first < it2->first) { + if (it1->getColumn() < it2->getColumn()) { continue; } else { // If the precondition of this method (i.e. that the given matrix is a submatrix // of the current one) was fulfilled, we know now that the two elements are in // the same column, so we can multiply and add them to the row sum vector. - result[row] += it2->second * it1->second; + result[row] += it2->getValue() * it1->getValue(); ++it2; } } @@ -744,7 +794,7 @@ namespace storm { *resultIterator = storm::utility::constantZero<T>(); for (ite = this->begin() + *(rowIterator + 1); it != ite; ++it) { - *resultIterator += it->second * vector[it->first]; + *resultIterator += it->getValue() * vector[it->getColumn()]; } } }); @@ -752,7 +802,6 @@ namespace storm { const_iterator it = this->begin(); const_iterator ite; typename std::vector<uint_fast64_t>::const_iterator rowIterator = rowIndications.begin(); - typename std::vector<uint_fast64_t>::const_iterator rowIteratorEnd = rowIndications.end(); typename std::vector<T>::iterator resultIterator = result.begin(); typename std::vector<T>::iterator resultIteratorEnd = result.end(); @@ -760,7 +809,7 @@ namespace storm { *resultIterator = storm::utility::constantZero<T>(); for (ite = this->begin() + *(rowIterator + 1); it != ite; ++it) { - *resultIterator += it->second * vector[it->first]; + *resultIterator += it->getValue() * vector[it->getColumn()]; } } #endif @@ -771,7 +820,7 @@ namespace storm { uint_fast64_t size = sizeof(*this); // Add size of columns and values. - size += sizeof(std::pair<uint_fast64_t, T>) * columnsAndValues.capacity(); + size += sizeof(MatrixEntry<T>) * columnsAndValues.capacity(); // Add row_indications size. size += sizeof(uint_fast64_t) * rowIndications.capacity(); @@ -843,7 +892,7 @@ namespace storm { T SparseMatrix<T>::getRowSum(uint_fast64_t row) const { T sum = storm::utility::constantZero<T>(); for (const_iterator it = this->begin(row), ite = this->end(row); it != ite; ++it) { - sum += it->second; + sum += it->getValue(); } return sum; } @@ -859,10 +908,10 @@ namespace storm { for (uint_fast64_t row = 0; row < this->getRowCount(); ++row) { for (const_iterator it1 = this->begin(row), ite1 = this->end(row), it2 = matrix.begin(row), ite2 = matrix.end(row); it1 != ite1; ++it1) { // Skip over all entries of the other matrix that are before the current entry in the current matrix. - while (it2 != ite2 && it2->first < it1->first) { + while (it2 != ite2 && it2->getColumn() < it1->getColumn()) { ++it2; } - if (it2 == ite2 || it1->first != it2->first) { + if (it2 == ite2 || it1->getColumn() != it2->getColumn()) { return false; } } @@ -905,8 +954,8 @@ namespace storm { out << i << "\t(\t"; uint_fast64_t currentRealIndex = 0; while (currentRealIndex < matrix.columnCount) { - if (nextIndex < matrix.rowIndications[i + 1] && currentRealIndex == matrix.columnsAndValues[nextIndex].first) { - out << matrix.columnsAndValues[nextIndex].second << "\t"; + if (nextIndex < matrix.rowIndications[i + 1] && currentRealIndex == matrix.columnsAndValues[nextIndex].getColumn()) { + out << matrix.columnsAndValues[nextIndex].getValue() << "\t"; ++nextIndex; } else { out << "0\t"; @@ -941,10 +990,12 @@ namespace storm { return result; } - // Explicitly instantiate the builder and the matrix. + // Explicitly instantiate the entry, builder and the matrix. + template class MatrixEntry<double>; template class SparseMatrixBuilder<double>; template class SparseMatrix<double>; template std::ostream& operator<<(std::ostream& out, SparseMatrix<double> const& matrix); + template class MatrixEntry<int>; template class SparseMatrixBuilder<int>; template class SparseMatrix<int>; template std::ostream& operator<<(std::ostream& out, SparseMatrix<int> const& matrix); diff --git a/src/storage/SparseMatrix.h b/src/storage/SparseMatrix.h index ae3a87abd..277ed069f 100644 --- a/src/storage/SparseMatrix.h +++ b/src/storage/SparseMatrix.h @@ -8,6 +8,7 @@ #include "src/storage/BitVector.h" #include "src/utility/constants.h" +#include "src/utility/OsDetection.h" #include "src/exceptions/InvalidArgumentException.h" #include "src/exceptions/OutOfRangeException.h" @@ -32,6 +33,83 @@ namespace storm { // Forward declare matrix class. template<typename T> class SparseMatrix; + template<typename T> + class MatrixEntry { + public: + /*! + * Constructs a matrix entry with the given column and value. + * + * @param column The column of the matrix entry. + * @param value The value of the matrix entry. + */ + MatrixEntry(uint_fast64_t column, T value); + + /*! + * Move-constructs the matrix entry fro the given column-value pair. + * + * @param pair The column-value pair from which to move-construct the matrix entry. + */ + MatrixEntry(std::pair<uint_fast64_t, T>&& pair); + + MatrixEntry() = default; + MatrixEntry(MatrixEntry const& other) = default; + MatrixEntry& operator=(MatrixEntry const& other) = default; +#ifndef WINDOWS + MatrixEntry(MatrixEntry&& other) = default; + MatrixEntry& operator=(MatrixEntry&& other) = default; +#endif + + /*! + * Retrieves the column of the matrix entry. + * + * @return The column of the matrix entry. + */ + uint_fast64_t const& getColumn() const; + + /*! + * Retrieves the column of the matrix entry. + * + * @return The column of the matrix entry. + */ + uint_fast64_t& getColumn(); + + /*! + * Retrieves the value of the matrix entry. + * + * @return The value of the matrix entry. + */ + T const& getValue() const; + + /*! + * Retrieves the value of the matrix entry. + * + * @return The value of the matrix entry. + */ + T& getValue(); + + /*! + * Retrieves a pair of column and value that characterizes this entry. + * + * @return A column-value pair that characterizes this entry. + */ + std::pair<uint_fast64_t, T> const& getColumnValuePair() const; + + private: + // The actual matrix entry. + std::pair<uint_fast64_t, T> entry; + }; + + /*! + * Computes the hash value of a matrix entry. + */ + template<typename T> + std::size_t hash_value(MatrixEntry<T> const& matrixEntry) { + std::size_t seed = 0; + boost::hash_combine(seed, matrixEntry.getColumn()); + boost::hash_combine(seed, matrixEntry.getValue()); + return seed; + } + /*! * A class that can be used to build a sparse matrix by adding value by value. */ @@ -133,7 +211,7 @@ namespace storm { bool storagePreallocated; // The storage for the columns and values of all entries in the matrix. - std::vector<std::pair<uint_fast64_t, T>> columnsAndValues; + std::vector<MatrixEntry<T>> columnsAndValues; // A vector containing the indices at which each given row begins. This index is to be interpreted as an // index in the valueStorage and the columnIndications vectors. Put differently, the values of the entries @@ -181,8 +259,8 @@ namespace storm { friend class storm::adapters::StormAdapter; friend class storm::solver::TopologicalValueIterationNondeterministicLinearEquationSolver<T>; - typedef typename std::vector<std::pair<uint_fast64_t, T>>::iterator iterator; - typedef typename std::vector<std::pair<uint_fast64_t, T>>::const_iterator const_iterator; + typedef typename std::vector<MatrixEntry<T>>::iterator iterator; + typedef typename std::vector<MatrixEntry<T>>::const_iterator const_iterator; /*! * This class represents a number of consecutive rows of the matrix. @@ -290,7 +368,7 @@ namespace storm { * @param columnsAndValues The vector containing the columns and values of the entries in the matrix. * @param rowGroupIndices The vector representing the row groups in the matrix. */ - SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t> const& rowIndications, std::vector<std::pair<uint_fast64_t, T>> const& columnsAndValues, std::vector<uint_fast64_t> const& rowGroupIndices); + SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t> const& rowIndications, std::vector<MatrixEntry<T>> const& columnsAndValues, std::vector<uint_fast64_t> const& rowGroupIndices); /*! * Constructs a sparse matrix by moving the given contents. @@ -300,7 +378,7 @@ namespace storm { * @param columnsAndValues The vector containing the columns and values of the entries in the matrix. * @param rowGroupIndices The vector representing the row groups in the matrix. */ - SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t>&& rowIndications, std::vector<std::pair<uint_fast64_t, T>>&& columnsAndValues, std::vector<uint_fast64_t>&& rowGroupIndices); + SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t>&& rowIndications, std::vector<MatrixEntry<T>>&& columnsAndValues, std::vector<uint_fast64_t>&& rowGroupIndices); /*! * Assigns the contents of the given matrix to the current one by deep-copying its contents. @@ -352,6 +430,13 @@ namespace storm { */ uint_fast64_t getRowGroupEntryCount(uint_fast64_t const group) const; + /*! + * Returns the number of nonzero entries in the matrix. + * + * @return The number of nonzero entries in the matrix. + */ + uint_fast64_t getNonzeroEntryCount() const; + /*! * Returns the number of row groups in the matrix. * @@ -672,8 +757,11 @@ namespace storm { // The number of entries in the matrix. uint_fast64_t entryCount; + // The number of nonzero entries in the matrix. + uint_fast64_t nonzeroEntryCount; + // The storage for the columns and values of all entries in the matrix. - std::vector<std::pair<uint_fast64_t, T>> columnsAndValues; + std::vector<MatrixEntry<T>> columnsAndValues; // A vector containing the indices at which each given row begins. This index is to be interpreted as an // index in the valueStorage and the columnIndications vectors. Put differently, the values of the entries diff --git a/src/storage/StronglyConnectedComponentDecomposition.cpp b/src/storage/StronglyConnectedComponentDecomposition.cpp index b1100f369..454ba9e00 100644 --- a/src/storage/StronglyConnectedComponentDecomposition.cpp +++ b/src/storage/StronglyConnectedComponentDecomposition.cpp @@ -133,33 +133,33 @@ namespace storm { // Now, traverse all successors of the current state. for(; successorIt != model.getRows(currentState).end(); ++successorIt) { // Record if the current state has a self-loop if we are to drop naive SCCs later. - if (dropNaiveSccs && currentState == successorIt->first) { + if (dropNaiveSccs && currentState == successorIt->getColumn()) { statesWithSelfloop.set(currentState, true); } // If we have not visited the successor already, we need to perform the procedure recursively on the // newly found state, but only if it belongs to the subsystem in which we are interested. - if (subsystem.get(successorIt->first)) { - if (!visitedStates.get(successorIt->first)) { + if (subsystem.get(successorIt->getColumn())) { + if (!visitedStates.get(successorIt->getColumn())) { // Save current iterator position so we can continue where we left off later. recursionIteratorStack.pop_back(); recursionIteratorStack.push_back(successorIt); // Put unvisited successor on top of our recursion stack and remember that. - recursionStateStack.push_back(successorIt->first); - statesInStack[successorIt->first] = true; + recursionStateStack.push_back(successorIt->getColumn()); + statesInStack[successorIt->getColumn()] = true; // Also, put initial value for iterator on corresponding recursion stack. - recursionIteratorStack.push_back(model.getRows(successorIt->first).begin()); + recursionIteratorStack.push_back(model.getRows(successorIt->getColumn()).begin()); // Perform the actual recursion step in an iterative way. goto recursionStepForward; recursionStepBackward: - lowlinks[currentState] = std::min(lowlinks[currentState], lowlinks[successorIt->first]); - } else if (tarjanStackStates.get(successorIt->first)) { + lowlinks[currentState] = std::min(lowlinks[currentState], lowlinks[successorIt->getColumn()]); + } else if (tarjanStackStates.get(successorIt->getColumn())) { // Update the lowlink of the current state. - lowlinks[currentState] = std::min(lowlinks[currentState], stateIndices[successorIt->first]); + lowlinks[currentState] = std::min(lowlinks[currentState], stateIndices[successorIt->getColumn()]); } } } @@ -184,7 +184,7 @@ namespace storm { if (onlyBottomSccs) { for (auto const& state : scc) { for (auto const& successor : model.getRows(state)) { - if (scc.find(successor.first) == scc.end()) { + if (scc.find(successor.getColumn()) == scc.end()) { isBottomScc = false; break; } diff --git a/src/storage/dd/CuddDd.cpp b/src/storage/dd/CuddDd.cpp new file mode 100644 index 000000000..57cda7018 --- /dev/null +++ b/src/storage/dd/CuddDd.cpp @@ -0,0 +1,892 @@ +#include <cstring> +#include <algorithm> + +#include "src/storage/dd/CuddDd.h" +#include "src/storage/dd/CuddOdd.h" +#include "src/storage/dd/CuddDdManager.h" +#include "src/utility/vector.h" + +#include "src/exceptions/InvalidArgumentException.h" + +namespace storm { + namespace dd { + Dd<DdType::CUDD>::Dd(std::shared_ptr<DdManager<DdType::CUDD>> ddManager, ADD cuddAdd, std::set<std::string> const& containedMetaVariableNames) : ddManager(ddManager), cuddAdd(cuddAdd), containedMetaVariableNames(containedMetaVariableNames) { + // Intentionally left empty. + } + + bool Dd<DdType::CUDD>::operator==(Dd<DdType::CUDD> const& other) const { + return this->cuddAdd == other.getCuddAdd(); + } + + bool Dd<DdType::CUDD>::operator!=(Dd<DdType::CUDD> const& other) const { + return this->cuddAdd != other.getCuddAdd(); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::ite(Dd<DdType::CUDD> const& thenDd, Dd<DdType::CUDD> const& elseDd) const { + std::set<std::string> metaVariableNames(this->getContainedMetaVariableNames()); + metaVariableNames.insert(thenDd.getContainedMetaVariableNames().begin(), thenDd.getContainedMetaVariableNames().end()); + metaVariableNames.insert(elseDd.getContainedMetaVariableNames().begin(), elseDd.getContainedMetaVariableNames().end()); + + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().Ite(thenDd.getCuddAdd(), elseDd.getCuddAdd()), metaVariableNames); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::operator+(Dd<DdType::CUDD> const& other) const { + Dd<DdType::CUDD> result(*this); + result += other; + return result; + } + + Dd<DdType::CUDD>& Dd<DdType::CUDD>::operator+=(Dd<DdType::CUDD> const& other) { + this->cuddAdd += other.getCuddAdd(); + + // Join the variable sets of the two participating DDs. + this->getContainedMetaVariableNames().insert(other.getContainedMetaVariableNames().begin(), other.getContainedMetaVariableNames().end()); + + return *this; + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::operator*(Dd<DdType::CUDD> const& other) const { + Dd<DdType::CUDD> result(*this); + result *= other; + return result; + } + + Dd<DdType::CUDD>& Dd<DdType::CUDD>::operator*=(Dd<DdType::CUDD> const& other) { + this->cuddAdd *= other.getCuddAdd(); + + // Join the variable sets of the two participating DDs. + this->getContainedMetaVariableNames().insert(other.getContainedMetaVariableNames().begin(), other.getContainedMetaVariableNames().end()); + + return *this; + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::operator-(Dd<DdType::CUDD> const& other) const { + Dd<DdType::CUDD> result(*this); + result -= other; + return result; + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::operator-() const { + return this->getDdManager()->getZero() - *this; + } + + Dd<DdType::CUDD>& Dd<DdType::CUDD>::operator-=(Dd<DdType::CUDD> const& other) { + this->cuddAdd -= other.getCuddAdd(); + + // Join the variable sets of the two participating DDs. + this->getContainedMetaVariableNames().insert(other.getContainedMetaVariableNames().begin(), other.getContainedMetaVariableNames().end()); + + return *this; + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::operator/(Dd<DdType::CUDD> const& other) const { + Dd<DdType::CUDD> result(*this); + result /= other; + return result; + } + + Dd<DdType::CUDD>& Dd<DdType::CUDD>::operator/=(Dd<DdType::CUDD> const& other) { + this->cuddAdd = this->cuddAdd.Divide(other.getCuddAdd()); + + // Join the variable sets of the two participating DDs. + this->getContainedMetaVariableNames().insert(other.getContainedMetaVariableNames().begin(), other.getContainedMetaVariableNames().end()); + + return *this; + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::operator!() const { + Dd<DdType::CUDD> result(*this); + result.complement(); + return result; + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::operator&&(Dd<DdType::CUDD> const& other) const { + std::set<std::string> metaVariableNames(this->getContainedMetaVariableNames()); + metaVariableNames.insert(other.getContainedMetaVariableNames().begin(), other.getContainedMetaVariableNames().end()); + + // Rewrite a and b to not((not a) or (not b)). + return Dd<DdType::CUDD>(this->getDdManager(), ~(~this->getCuddAdd()).Or(~other.getCuddAdd()), metaVariableNames); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::operator||(Dd<DdType::CUDD> const& other) const { + std::set<std::string> metaVariableNames(this->getContainedMetaVariableNames()); + metaVariableNames.insert(other.getContainedMetaVariableNames().begin(), other.getContainedMetaVariableNames().end()); + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().Or(other.getCuddAdd()), metaVariableNames); + } + + Dd<DdType::CUDD>& Dd<DdType::CUDD>::complement() { + this->cuddAdd = ~this->cuddAdd; + return *this; + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::equals(Dd<DdType::CUDD> const& other) const { + std::set<std::string> metaVariableNames(this->getContainedMetaVariableNames()); + metaVariableNames.insert(other.getContainedMetaVariableNames().begin(), other.getContainedMetaVariableNames().end()); + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().Equals(other.getCuddAdd()), metaVariableNames); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::notEquals(Dd<DdType::CUDD> const& other) const { + std::set<std::string> metaVariableNames(this->getContainedMetaVariableNames()); + metaVariableNames.insert(other.getContainedMetaVariableNames().begin(), other.getContainedMetaVariableNames().end()); + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().NotEquals(other.getCuddAdd()), metaVariableNames); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::less(Dd<DdType::CUDD> const& other) const { + std::set<std::string> metaVariableNames(this->getContainedMetaVariableNames()); + metaVariableNames.insert(other.getContainedMetaVariableNames().begin(), other.getContainedMetaVariableNames().end()); + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().LessThan(other.getCuddAdd()), metaVariableNames); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::lessOrEqual(Dd<DdType::CUDD> const& other) const { + std::set<std::string> metaVariableNames(this->getContainedMetaVariableNames()); + metaVariableNames.insert(other.getContainedMetaVariableNames().begin(), other.getContainedMetaVariableNames().end()); + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().LessThanOrEqual(other.getCuddAdd()), metaVariableNames); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::greater(Dd<DdType::CUDD> const& other) const { + std::set<std::string> metaVariableNames(this->getContainedMetaVariableNames()); + metaVariableNames.insert(other.getContainedMetaVariableNames().begin(), other.getContainedMetaVariableNames().end()); + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().GreaterThan(other.getCuddAdd()), metaVariableNames); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::greaterOrEqual(Dd<DdType::CUDD> const& other) const { + std::set<std::string> metaVariableNames(this->getContainedMetaVariableNames()); + metaVariableNames.insert(other.getContainedMetaVariableNames().begin(), other.getContainedMetaVariableNames().end()); + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().GreaterThanOrEqual(other.getCuddAdd()), metaVariableNames); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::minimum(Dd<DdType::CUDD> const& other) const { + std::set<std::string> metaVariableNames(this->getContainedMetaVariableNames()); + metaVariableNames.insert(other.getContainedMetaVariableNames().begin(), other.getContainedMetaVariableNames().end()); + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().Minimum(other.getCuddAdd()), metaVariableNames); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::maximum(Dd<DdType::CUDD> const& other) const { + std::set<std::string> metaVariableNames(this->getContainedMetaVariableNames()); + metaVariableNames.insert(other.getContainedMetaVariableNames().begin(), other.getContainedMetaVariableNames().end()); + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().Maximum(other.getCuddAdd()), metaVariableNames); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::existsAbstract(std::set<std::string> const& metaVariableNames) const { + Dd<DdType::CUDD> cubeDd(this->getDdManager()->getOne()); + + std::set<std::string> newMetaVariables = this->getContainedMetaVariableNames(); + for (auto const& metaVariableName : metaVariableNames) { + // First check whether the DD contains the meta variable and erase it, if this is the case. + if (!this->containsMetaVariable(metaVariableName)) { + throw storm::exceptions::InvalidArgumentException() << "Cannot abstract from meta variable that is not present in the DD."; + } + newMetaVariables.erase(metaVariableName); + + DdMetaVariable<DdType::CUDD> const& metaVariable = this->getDdManager()->getMetaVariable(metaVariableName); + cubeDd *= metaVariable.getCube(); + } + + return Dd<DdType::CUDD>(this->getDdManager(), this->cuddAdd.OrAbstract(cubeDd.getCuddAdd()), newMetaVariables); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::universalAbstract(std::set<std::string> const& metaVariableNames) const { + Dd<DdType::CUDD> cubeDd(this->getDdManager()->getOne()); + + std::set<std::string> newMetaVariables = this->getContainedMetaVariableNames(); + for (auto const& metaVariableName : metaVariableNames) { + // First check whether the DD contains the meta variable and erase it, if this is the case. + if (!this->containsMetaVariable(metaVariableName)) { + throw storm::exceptions::InvalidArgumentException() << "Cannot abstract from meta variable that is not present in the DD."; + } + newMetaVariables.erase(metaVariableName); + + DdMetaVariable<DdType::CUDD> const& metaVariable = this->getDdManager()->getMetaVariable(metaVariableName); + cubeDd *= metaVariable.getCube(); + } + + return Dd<DdType::CUDD>(this->getDdManager(), this->cuddAdd.UnivAbstract(cubeDd.getCuddAdd()), newMetaVariables); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::sumAbstract(std::set<std::string> const& metaVariableNames) const { + Dd<DdType::CUDD> cubeDd(this->getDdManager()->getOne()); + + std::set<std::string> newMetaVariables = this->getContainedMetaVariableNames(); + for (auto const& metaVariableName : metaVariableNames) { + // First check whether the DD contains the meta variable and erase it, if this is the case. + if (!this->containsMetaVariable(metaVariableName)) { + throw storm::exceptions::InvalidArgumentException() << "Cannot abstract from meta variable that is not present in the DD."; + } + newMetaVariables.erase(metaVariableName); + + DdMetaVariable<DdType::CUDD> const& metaVariable = this->getDdManager()->getMetaVariable(metaVariableName); + cubeDd *= metaVariable.getCube(); + } + + return Dd<DdType::CUDD>(this->getDdManager(), this->cuddAdd.ExistAbstract(cubeDd.getCuddAdd()), newMetaVariables); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::minAbstract(std::set<std::string> const& metaVariableNames) const { + Dd<DdType::CUDD> cubeDd(this->getDdManager()->getOne()); + + std::set<std::string> newMetaVariables = this->getContainedMetaVariableNames(); + for (auto const& metaVariableName : metaVariableNames) { + // First check whether the DD contains the meta variable and erase it, if this is the case. + if (!this->containsMetaVariable(metaVariableName)) { + throw storm::exceptions::InvalidArgumentException() << "Cannot abstract from meta variable that is not present in the DD."; + } + newMetaVariables.erase(metaVariableName); + + DdMetaVariable<DdType::CUDD> const& metaVariable = this->getDdManager()->getMetaVariable(metaVariableName); + cubeDd *= metaVariable.getCube(); + } + + return Dd<DdType::CUDD>(this->getDdManager(), this->cuddAdd.MinAbstract(cubeDd.getCuddAdd()), newMetaVariables); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::maxAbstract(std::set<std::string> const& metaVariableNames) const { + Dd<DdType::CUDD> cubeDd(this->getDdManager()->getOne()); + + std::set<std::string> newMetaVariables = this->getContainedMetaVariableNames(); + for (auto const& metaVariableName : metaVariableNames) { + // First check whether the DD contains the meta variable and erase it, if this is the case. + if (!this->containsMetaVariable(metaVariableName)) { + throw storm::exceptions::InvalidArgumentException() << "Cannot abstract from meta variable that is not present in the DD."; + } + newMetaVariables.erase(metaVariableName); + + DdMetaVariable<DdType::CUDD> const& metaVariable = this->getDdManager()->getMetaVariable(metaVariableName); + cubeDd *= metaVariable.getCube(); + } + + return Dd<DdType::CUDD>(this->getDdManager(), this->cuddAdd.MaxAbstract(cubeDd.getCuddAdd()), newMetaVariables); + } + + bool Dd<DdType::CUDD>::equalModuloPrecision(Dd<DdType::CUDD> const& other, double precision, bool relative) const { + if (relative) { + return this->getCuddAdd().EqualSupNormRel(other.getCuddAdd(), precision); + } else { + return this->getCuddAdd().EqualSupNorm(other.getCuddAdd(), precision); + } + } + + void Dd<DdType::CUDD>::swapVariables(std::vector<std::pair<std::string, std::string>> const& metaVariablePairs) { + std::vector<ADD> from; + std::vector<ADD> to; + for (auto const& metaVariablePair : metaVariablePairs) { + DdMetaVariable<DdType::CUDD> const& variable1 = this->getDdManager()->getMetaVariable(metaVariablePair.first); + DdMetaVariable<DdType::CUDD> const& variable2 = this->getDdManager()->getMetaVariable(metaVariablePair.second); + + // Check if it's legal so swap the meta variables. + if (variable1.getNumberOfDdVariables() != variable2.getNumberOfDdVariables()) { + throw storm::exceptions::InvalidArgumentException() << "Unable to swap meta variables with different size."; + } + + // Keep track of the contained meta variables in the DD. + bool containsVariable1 = this->containsMetaVariable(metaVariablePair.first); + bool containsVariable2 = this->containsMetaVariable(metaVariablePair.second); + if (containsVariable1 && !containsVariable2) { + this->removeContainedMetaVariable(metaVariablePair.first); + this->addContainedMetaVariable(metaVariablePair.second); + } else if (!containsVariable1 && containsVariable2) { + this->removeContainedMetaVariable(metaVariablePair.second); + this->addContainedMetaVariable(metaVariablePair.first); + } + + // Add the variables to swap to the corresponding vectors. + for (auto const& ddVariable : variable1.getDdVariables()) { + from.push_back(ddVariable.getCuddAdd()); + } + for (auto const& ddVariable : variable2.getDdVariables()) { + to.push_back(ddVariable.getCuddAdd()); + } + } + + // Finally, call CUDD to swap the variables. + this->cuddAdd = this->cuddAdd.SwapVariables(from, to); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::multiplyMatrix(Dd<DdType::CUDD> const& otherMatrix, std::set<std::string> const& summationMetaVariableNames) const { + std::vector<ADD> summationDdVariables; + + // Create the CUDD summation variables. + for (auto const& metaVariableName : summationMetaVariableNames) { + for (auto const& ddVariable : this->getDdManager()->getMetaVariable(metaVariableName).getDdVariables()) { + summationDdVariables.push_back(ddVariable.getCuddAdd()); + } + } + + std::set<std::string> unionOfMetaVariableNames; + std::set_union(this->getContainedMetaVariableNames().begin(), this->getContainedMetaVariableNames().end(), otherMatrix.getContainedMetaVariableNames().begin(), otherMatrix.getContainedMetaVariableNames().end(), std::inserter(unionOfMetaVariableNames, unionOfMetaVariableNames.begin())); + std::set<std::string> containedMetaVariableNames; + std::set_difference(unionOfMetaVariableNames.begin(), unionOfMetaVariableNames.end(), summationMetaVariableNames.begin(), summationMetaVariableNames.end(), std::inserter(containedMetaVariableNames, containedMetaVariableNames.begin())); + + return Dd<DdType::CUDD>(this->getDdManager(), this->cuddAdd.MatrixMultiply(otherMatrix.getCuddAdd(), summationDdVariables), containedMetaVariableNames); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::greater(double value) const { + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().BddStrictThreshold(value).Add(), this->getContainedMetaVariableNames()); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::greaterOrEqual(double value) const { + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().BddThreshold(value).Add(), this->getContainedMetaVariableNames()); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::notZero() const { + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().BddPattern().Add(), this->getContainedMetaVariableNames()); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::constrain(Dd<DdType::CUDD> const& constraint) const { + std::set<std::string> metaVariableNames(this->getContainedMetaVariableNames()); + metaVariableNames.insert(constraint.getContainedMetaVariableNames().begin(), constraint.getContainedMetaVariableNames().end()); + + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().Constrain(constraint.getCuddAdd()), metaVariableNames); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::restrict(Dd<DdType::CUDD> const& constraint) const { + std::set<std::string> metaVariableNames(this->getContainedMetaVariableNames()); + metaVariableNames.insert(constraint.getContainedMetaVariableNames().begin(), constraint.getContainedMetaVariableNames().end()); + + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().Restrict(constraint.getCuddAdd()), metaVariableNames); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::getSupport() const { + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().Support().Add(), this->getContainedMetaVariableNames()); + } + + uint_fast64_t Dd<DdType::CUDD>::getNonZeroCount() const { + std::size_t numberOfDdVariables = 0; + for (auto const& metaVariableName : this->containedMetaVariableNames) { + numberOfDdVariables += this->getDdManager()->getMetaVariable(metaVariableName).getNumberOfDdVariables(); + } + return static_cast<uint_fast64_t>(this->cuddAdd.CountMinterm(static_cast<int>(numberOfDdVariables))); + } + + uint_fast64_t Dd<DdType::CUDD>::getLeafCount() const { + return static_cast<uint_fast64_t>(this->cuddAdd.CountLeaves()); + } + + uint_fast64_t Dd<DdType::CUDD>::getNodeCount() const { + return static_cast<uint_fast64_t>(this->cuddAdd.nodeCount()); + } + + double Dd<DdType::CUDD>::getMin() const { + ADD constantMinAdd = this->cuddAdd.FindMin(); + return static_cast<double>(Cudd_V(constantMinAdd.getNode())); + } + + double Dd<DdType::CUDD>::getMax() const { + ADD constantMaxAdd = this->cuddAdd.FindMax(); + return static_cast<double>(Cudd_V(constantMaxAdd.getNode())); + } + + void Dd<DdType::CUDD>::setValue(std::string const& metaVariableName, int_fast64_t variableValue, double targetValue) { + std::map<std::string, int_fast64_t> metaVariableNameToValueMap; + metaVariableNameToValueMap.emplace(metaVariableName, variableValue); + this->setValue(metaVariableNameToValueMap, targetValue); + } + + void Dd<DdType::CUDD>::setValue(std::string const& metaVariableName1, int_fast64_t variableValue1, std::string const& metaVariableName2, int_fast64_t variableValue2, double targetValue) { + std::map<std::string, int_fast64_t> metaVariableNameToValueMap; + metaVariableNameToValueMap.emplace(metaVariableName1, variableValue1); + metaVariableNameToValueMap.emplace(metaVariableName2, variableValue2); + this->setValue(metaVariableNameToValueMap, targetValue); + } + + void Dd<DdType::CUDD>::setValue(std::map<std::string, int_fast64_t> const& metaVariableNameToValueMap, double targetValue) { + Dd<DdType::CUDD> valueEncoding(this->getDdManager()->getOne()); + for (auto const& nameValuePair : metaVariableNameToValueMap) { + valueEncoding *= this->getDdManager()->getEncoding(nameValuePair.first, nameValuePair.second); + // Also record that the DD now contains the meta variable. + this->addContainedMetaVariable(nameValuePair.first); + } + + this->cuddAdd = valueEncoding.getCuddAdd().Ite(this->getDdManager()->getConstant(targetValue).getCuddAdd(), this->cuddAdd); + } + + double Dd<DdType::CUDD>::getValue(std::map<std::string, int_fast64_t> const& metaVariableNameToValueMap) const { + std::set<std::string> remainingMetaVariables(this->getContainedMetaVariableNames()); + Dd<DdType::CUDD> valueEncoding(this->getDdManager()->getOne()); + for (auto const& nameValuePair : metaVariableNameToValueMap) { + valueEncoding *= this->getDdManager()->getEncoding(nameValuePair.first, nameValuePair.second); + if (this->containsMetaVariable(nameValuePair.first)) { + remainingMetaVariables.erase(nameValuePair.first); + } + } + + if (!remainingMetaVariables.empty()) { + throw storm::exceptions::InvalidArgumentException() << "Cannot evaluate function for which not all inputs were given."; + } + + Dd<DdType::CUDD> value = *this * valueEncoding; + value = value.sumAbstract(this->getContainedMetaVariableNames()); + return static_cast<double>(Cudd_V(value.getCuddAdd().getNode())); + } + + bool Dd<DdType::CUDD>::isOne() const { + return *this == this->getDdManager()->getOne(); + } + + bool Dd<DdType::CUDD>::isZero() const { + return *this == this->getDdManager()->getZero(); + } + + bool Dd<DdType::CUDD>::isConstant() const { + return Cudd_IsConstant(this->cuddAdd.getNode()); + } + + uint_fast64_t Dd<DdType::CUDD>::getIndex() const { + return static_cast<uint_fast64_t>(this->getCuddAdd().NodeReadIndex()); + } + + template<typename ValueType> + std::vector<ValueType> Dd<DdType::CUDD>::toVector() const { + return this->toVector<ValueType>(Odd<DdType::CUDD>(*this)); + } + + template<typename ValueType> + std::vector<ValueType> Dd<DdType::CUDD>::toVector(Odd<DdType::CUDD> const& rowOdd) const { + std::vector<ValueType> result(rowOdd.getTotalOffset()); + std::vector<uint_fast64_t> ddVariableIndices = this->getSortedVariableIndices(); + addToVectorRec(this->getCuddAdd().getNode(), 0, ddVariableIndices.size(), 0, rowOdd, ddVariableIndices, result); + return result; + } + + storm::storage::SparseMatrix<double> Dd<DdType::CUDD>::toMatrix() const { + std::set<std::string> rowVariables; + std::set<std::string> columnVariables; + + for (auto const& variableName : this->getContainedMetaVariableNames()) { + if (variableName.size() > 0 && variableName.back() == '\'') { + columnVariables.insert(variableName); + } else { + rowVariables.insert(variableName); + } + } + + return toMatrix(rowVariables, columnVariables, Odd<DdType::CUDD>(this->existsAbstract(rowVariables)), Odd<DdType::CUDD>(this->existsAbstract(columnVariables))); + } + + storm::storage::SparseMatrix<double> Dd<DdType::CUDD>::toMatrix(storm::dd::Odd<DdType::CUDD> const& rowOdd, storm::dd::Odd<DdType::CUDD> const& columnOdd) const { + std::set<std::string> rowMetaVariables; + std::set<std::string> columnMetaVariables; + + for (auto const& variableName : this->getContainedMetaVariableNames()) { + if (variableName.size() > 0 && variableName.back() == '\'') { + columnMetaVariables.insert(variableName); + } else { + rowMetaVariables.insert(variableName); + } + } + + return toMatrix(rowMetaVariables, columnMetaVariables, rowOdd, columnOdd); + } + + storm::storage::SparseMatrix<double> Dd<DdType::CUDD>::toMatrix(std::set<std::string> const& rowMetaVariables, std::set<std::string> const& columnMetaVariables, storm::dd::Odd<DdType::CUDD> const& rowOdd, storm::dd::Odd<DdType::CUDD> const& columnOdd) const { + std::vector<uint_fast64_t> ddRowVariableIndices; + std::vector<uint_fast64_t> ddColumnVariableIndices; + + for (auto const& variableName : rowMetaVariables) { + DdMetaVariable<DdType::CUDD> const& metaVariable = this->getDdManager()->getMetaVariable(variableName); + for (auto const& ddVariable : metaVariable.getDdVariables()) { + ddRowVariableIndices.push_back(ddVariable.getIndex()); + } + } + for (auto const& variableName : columnMetaVariables) { + DdMetaVariable<DdType::CUDD> const& metaVariable = this->getDdManager()->getMetaVariable(variableName); + for (auto const& ddVariable : metaVariable.getDdVariables()) { + ddColumnVariableIndices.push_back(ddVariable.getIndex()); + } + } + + // Prepare the vectors that represent the matrix. + std::vector<uint_fast64_t> rowIndications(rowOdd.getTotalOffset() + 1); + std::vector<storm::storage::MatrixEntry<double>> columnsAndValues(this->getNonZeroCount()); + + // Create a trivial row grouping. + std::vector<uint_fast64_t> trivialRowGroupIndices(rowIndications.size()); + uint_fast64_t i = 0; + for (auto& entry : trivialRowGroupIndices) { + entry = i; + ++i; + } + + // Use the toMatrixRec function to compute the number of elements in each row. Using the flag, we prevent + // it from actually generating the entries in the entry vector. + toMatrixRec(this->getCuddAdd().getNode(), rowIndications, columnsAndValues, trivialRowGroupIndices, rowOdd, columnOdd, 0, 0, ddRowVariableIndices.size() + ddColumnVariableIndices.size(), 0, 0, ddRowVariableIndices, ddColumnVariableIndices, false); + + // TODO: counting might be faster by just summing over the primed variables and then using the ODD to convert + // the resulting (DD) vector to an explicit vector. + + // Now that we computed the number of entries in each row, compute the corresponding offsets in the entry vector. + uint_fast64_t tmp = 0; + uint_fast64_t tmp2 = 0; + for (uint_fast64_t i = 1; i < rowIndications.size(); ++i) { + tmp2 = rowIndications[i]; + rowIndications[i] = rowIndications[i - 1] + tmp; + std::swap(tmp, tmp2); + } + rowIndications[0] = 0; + + // Now actually fill the entry vector. + toMatrixRec(this->getCuddAdd().getNode(), rowIndications, columnsAndValues, trivialRowGroupIndices, rowOdd, columnOdd, 0, 0, ddRowVariableIndices.size() + ddColumnVariableIndices.size(), 0, 0, ddRowVariableIndices, ddColumnVariableIndices, true); + + // Since the last call to toMatrixRec modified the rowIndications, we need to restore the correct values. + for (uint_fast64_t i = rowIndications.size() - 1; i > 0; --i) { + rowIndications[i] = rowIndications[i - 1]; + } + rowIndications[0] = 0; + + // Construct matrix and return result. + return storm::storage::SparseMatrix<double>(columnOdd.getTotalOffset(), std::move(rowIndications), std::move(columnsAndValues), std::move(trivialRowGroupIndices)); + } + + storm::storage::SparseMatrix<double> Dd<DdType::CUDD>::toMatrix(std::set<std::string> const& rowMetaVariables, std::set<std::string> const& columnMetaVariables, std::set<std::string> const& groupMetaVariables, storm::dd::Odd<DdType::CUDD> const& rowOdd, storm::dd::Odd<DdType::CUDD> const& columnOdd) const { + std::vector<uint_fast64_t> ddRowVariableIndices; + std::vector<uint_fast64_t> ddColumnVariableIndices; + std::vector<uint_fast64_t> ddGroupVariableIndices; + std::set<std::string> rowAndColumnMetaVariables; + + for (auto const& variableName : rowMetaVariables) { + DdMetaVariable<DdType::CUDD> const& metaVariable = this->getDdManager()->getMetaVariable(variableName); + for (auto const& ddVariable : metaVariable.getDdVariables()) { + ddRowVariableIndices.push_back(ddVariable.getIndex()); + } + rowAndColumnMetaVariables.insert(variableName); + } + for (auto const& variableName : columnMetaVariables) { + DdMetaVariable<DdType::CUDD> const& metaVariable = this->getDdManager()->getMetaVariable(variableName); + for (auto const& ddVariable : metaVariable.getDdVariables()) { + ddColumnVariableIndices.push_back(ddVariable.getIndex()); + } + rowAndColumnMetaVariables.insert(variableName); + } + for (auto const& variableName : groupMetaVariables) { + DdMetaVariable<DdType::CUDD> const& metaVariable = this->getDdManager()->getMetaVariable(variableName); + for (auto const& ddVariable : metaVariable.getDdVariables()) { + ddGroupVariableIndices.push_back(ddVariable.getIndex()); + } + } + + // TODO: assert that the group variables are at the very top of the variable ordering? + + // Start by computing the offsets (in terms of rows) for each row group. + Dd<DdType::CUDD> stateToNumberOfChoices = this->notZero().existsAbstract(columnMetaVariables).sumAbstract(groupMetaVariables); + std::vector<uint_fast64_t> rowGroupIndices = stateToNumberOfChoices.toVector<uint_fast64_t>(rowOdd); + rowGroupIndices.resize(rowGroupIndices.size() + 1); + uint_fast64_t tmp = 0; + uint_fast64_t tmp2 = 0; + for (uint_fast64_t i = 1; i < rowGroupIndices.size(); ++i) { + tmp2 = rowGroupIndices[i]; + rowGroupIndices[i] = rowGroupIndices[i - 1] + tmp; + std::swap(tmp, tmp2); + } + rowGroupIndices[0] = 0; + + // Next, we split the matrix into one for each group. This only works if the group variables are at the very + // top. + std::vector<Dd<DdType::CUDD>> groups; + splitGroupsRec(this->getCuddAdd().getNode(), groups, ddGroupVariableIndices, 0, ddGroupVariableIndices.size(), rowAndColumnMetaVariables); + + // Create the actual storage for the non-zero entries. + std::vector<storm::storage::MatrixEntry<double>> columnsAndValues(this->getNonZeroCount()); + + // Now compute the indices at which the individual rows start. + std::vector<uint_fast64_t> rowIndications(rowGroupIndices.back() + 1); + std::vector<storm::dd::Dd<DdType::CUDD>> statesWithGroupEnabled(groups.size()); + for (uint_fast64_t i = 0; i < groups.size(); ++i) { + auto const& dd = groups[i]; + + toMatrixRec(dd.getCuddAdd().getNode(), rowIndications, columnsAndValues, rowGroupIndices, rowOdd, columnOdd, 0, 0, ddRowVariableIndices.size() + ddColumnVariableIndices.size(), 0, 0, ddRowVariableIndices, ddColumnVariableIndices, false); + + statesWithGroupEnabled[i] = dd.notZero().existsAbstract(columnMetaVariables); + addToVectorRec(statesWithGroupEnabled[i].getCuddAdd().getNode(), 0, ddRowVariableIndices.size(), 0, rowOdd, ddRowVariableIndices, rowGroupIndices); + } + + // Since we modified the rowGroupIndices, we need to restore the correct values. + for (uint_fast64_t i = rowGroupIndices.size() - 1; i > 0; --i) { + rowGroupIndices[i] = rowGroupIndices[i - 1]; + } + rowGroupIndices[0] = 0; + + // Now that we computed the number of entries in each row, compute the corresponding offsets in the entry vector. + tmp = 0; + tmp2 = 0; + for (uint_fast64_t i = 1; i < rowIndications.size(); ++i) { + tmp2 = rowIndications[i]; + rowIndications[i] = rowIndications[i - 1] + tmp; + std::swap(tmp, tmp2); + } + rowIndications[0] = 0; + + // Now actually fill the entry vector. + for (uint_fast64_t i = 0; i < groups.size(); ++i) { + auto const& dd = groups[i]; + + toMatrixRec(dd.getCuddAdd().getNode(), rowIndications, columnsAndValues, rowGroupIndices, rowOdd, columnOdd, 0, 0, ddRowVariableIndices.size() + ddColumnVariableIndices.size(), 0, 0, ddRowVariableIndices, ddColumnVariableIndices, true); + + addToVectorRec(statesWithGroupEnabled[i].getCuddAdd().getNode(), 0, ddRowVariableIndices.size(), 0, rowOdd, ddRowVariableIndices, rowGroupIndices); + } + + // Since we modified the rowGroupIndices, we need to restore the correct values. + for (uint_fast64_t i = rowGroupIndices.size() - 1; i > 0; --i) { + rowGroupIndices[i] = rowGroupIndices[i - 1]; + } + rowGroupIndices[0] = 0; + + // Since the last call to toMatrixRec modified the rowIndications, we need to restore the correct values. + for (uint_fast64_t i = rowIndications.size() - 1; i > 0; --i) { + rowIndications[i] = rowIndications[i - 1]; + } + rowIndications[0] = 0; + + return storm::storage::SparseMatrix<double>(columnOdd.getTotalOffset(), std::move(rowIndications), std::move(columnsAndValues), std::move(rowGroupIndices)); + } + + void Dd<DdType::CUDD>::toMatrixRec(DdNode const* dd, std::vector<uint_fast64_t>& rowIndications, std::vector<storm::storage::MatrixEntry<double>>& columnsAndValues, std::vector<uint_fast64_t> const& rowGroupOffsets, Odd<DdType::CUDD> const& rowOdd, Odd<DdType::CUDD> const& columnOdd, uint_fast64_t currentRowLevel, uint_fast64_t currentColumnLevel, uint_fast64_t maxLevel, uint_fast64_t currentRowOffset, uint_fast64_t currentColumnOffset, std::vector<uint_fast64_t> const& ddRowVariableIndices, std::vector<uint_fast64_t> const& ddColumnVariableIndices, bool generateValues) const { + // For the empty DD, we do not need to add any entries. + if (dd == this->getDdManager()->getZero().getCuddAdd().getNode()) { + return; + } + + // If we are at the maximal level, the value to be set is stored as a constant in the DD. + if (currentRowLevel + currentColumnLevel == maxLevel) { + if (generateValues) { + columnsAndValues[rowIndications[rowGroupOffsets[currentRowOffset]]] = storm::storage::MatrixEntry<double>(currentColumnOffset, Cudd_V(dd)); + } + ++rowIndications[rowGroupOffsets[currentRowOffset]]; + } else { + DdNode const* elseElse; + DdNode const* elseThen; + DdNode const* thenElse; + DdNode const* thenThen; + + if (ddColumnVariableIndices[currentColumnLevel] < dd->index) { + elseElse = elseThen = thenElse = thenThen = dd; + } else if (ddRowVariableIndices[currentColumnLevel] < dd->index) { + elseElse = thenElse = Cudd_E(dd); + elseThen = thenThen = Cudd_T(dd); + } else { + DdNode const* elseNode = Cudd_E(dd); + if (ddColumnVariableIndices[currentColumnLevel] < elseNode->index) { + elseElse = elseThen = elseNode; + } else { + elseElse = Cudd_E(elseNode); + elseThen = Cudd_T(elseNode); + } + + DdNode const* thenNode = Cudd_T(dd); + if (ddColumnVariableIndices[currentColumnLevel] < thenNode->index) { + thenElse = thenThen = thenNode; + } else { + thenElse = Cudd_E(thenNode); + thenThen = Cudd_T(thenNode); + } + } + + // Visit else-else. + toMatrixRec(elseElse, rowIndications, columnsAndValues, rowGroupOffsets, rowOdd.getElseSuccessor(), columnOdd.getElseSuccessor(), currentRowLevel + 1, currentColumnLevel + 1, maxLevel, currentRowOffset, currentColumnOffset, ddRowVariableIndices, ddColumnVariableIndices, generateValues); + // Visit else-then. + toMatrixRec(elseThen, rowIndications, columnsAndValues, rowGroupOffsets, rowOdd.getElseSuccessor(), columnOdd.getThenSuccessor(), currentRowLevel + 1, currentColumnLevel + 1, maxLevel, currentRowOffset, currentColumnOffset + columnOdd.getElseOffset(), ddRowVariableIndices, ddColumnVariableIndices, generateValues); + // Visit then-else. + toMatrixRec(thenElse, rowIndications, columnsAndValues, rowGroupOffsets, rowOdd.getThenSuccessor(), columnOdd.getElseSuccessor(), currentRowLevel + 1, currentColumnLevel + 1, maxLevel, currentRowOffset + rowOdd.getElseOffset(), currentColumnOffset, ddRowVariableIndices, ddColumnVariableIndices, generateValues); + // Visit then-then. + toMatrixRec(thenThen, rowIndications, columnsAndValues, rowGroupOffsets, rowOdd.getThenSuccessor(), columnOdd.getThenSuccessor(), currentRowLevel + 1, currentColumnLevel + 1, maxLevel, currentRowOffset + rowOdd.getElseOffset(), currentColumnOffset + columnOdd.getElseOffset(), ddRowVariableIndices, ddColumnVariableIndices, generateValues); + } + } + + void Dd<DdType::CUDD>::splitGroupsRec(DdNode* dd, std::vector<Dd<DdType::CUDD>>& groups, std::vector<uint_fast64_t> const& ddGroupVariableIndices, uint_fast64_t currentLevel, uint_fast64_t maxLevel, std::set<std::string> const& remainingMetaVariables) const { + // For the empty DD, we do not need to create a group. + if (dd == this->getDdManager()->getZero().getCuddAdd().getNode()) { + return; + } + + if (currentLevel == maxLevel) { + groups.push_back(Dd<DdType::CUDD>(this->getDdManager(), ADD(this->getDdManager()->getCuddManager(), dd), remainingMetaVariables)); + } else if (ddGroupVariableIndices[currentLevel] < dd->index) { + splitGroupsRec(dd, groups, ddGroupVariableIndices, currentLevel + 1, maxLevel, remainingMetaVariables); + splitGroupsRec(dd, groups, ddGroupVariableIndices, currentLevel + 1, maxLevel, remainingMetaVariables); + } else { + splitGroupsRec(Cudd_E(dd), groups, ddGroupVariableIndices, currentLevel + 1, maxLevel, remainingMetaVariables); + splitGroupsRec(Cudd_T(dd), groups, ddGroupVariableIndices, currentLevel + 1, maxLevel, remainingMetaVariables); + } + } + + template<typename ValueType> + void Dd<DdType::CUDD>::addToVectorRec(DdNode const* dd, uint_fast64_t currentLevel, uint_fast64_t maxLevel, uint_fast64_t currentOffset, Odd<DdType::CUDD> const& odd, std::vector<uint_fast64_t> const& ddVariableIndices, std::vector<ValueType>& targetVector) const { + // For the empty DD, we do not need to add any entries. + if (dd == this->getDdManager()->getZero().getCuddAdd().getNode()) { + return; + } + + // If we are at the maximal level, the value to be set is stored as a constant in the DD. + if (currentLevel == maxLevel) { + targetVector[currentOffset] += static_cast<ValueType>(Cudd_V(dd)); + } else if (ddVariableIndices[currentLevel] < dd->index) { + // If we skipped a level, we need to enumerate the explicit entries for the case in which the bit is set + // and for the one in which it is not set. + addToVectorRec(dd, currentLevel + 1, maxLevel, currentOffset, odd.getElseSuccessor(), ddVariableIndices, targetVector); + addToVectorRec(dd, currentLevel + 1, maxLevel, currentOffset + odd.getElseOffset(), odd.getThenSuccessor(), ddVariableIndices, targetVector); + } else { + // Otherwise, we simply recursively call the function for both (different) cases. + addToVectorRec(Cudd_E(dd), currentLevel + 1, maxLevel, currentOffset, odd.getElseSuccessor(), ddVariableIndices, targetVector); + addToVectorRec(Cudd_T(dd), currentLevel + 1, maxLevel, currentOffset + odd.getElseOffset(), odd.getThenSuccessor(), ddVariableIndices, targetVector); + } + } + + std::vector<uint_fast64_t> Dd<DdType::CUDD>::getSortedVariableIndices() const { + std::vector<uint_fast64_t> ddVariableIndices; + for (auto const& metaVariableName : this->getContainedMetaVariableNames()) { + auto const& metaVariable = this->getDdManager()->getMetaVariable(metaVariableName); + for (auto const& ddVariable : metaVariable.getDdVariables()) { + ddVariableIndices.push_back(ddVariable.getIndex()); + } + } + + // Next, we need to sort them, since they may be arbitrarily ordered otherwise. + std::sort(ddVariableIndices.begin(), ddVariableIndices.end()); + return ddVariableIndices; + } + + bool Dd<DdType::CUDD>::containsMetaVariable(std::string const& metaVariableName) const { + auto const& metaVariable = containedMetaVariableNames.find(metaVariableName); + return metaVariable != containedMetaVariableNames.end(); + } + + bool Dd<DdType::CUDD>::containsMetaVariables(std::set<std::string> metaVariableNames) const { + for (auto const& metaVariableName : metaVariableNames) { + auto const& metaVariable = containedMetaVariableNames.find(metaVariableName); + + if (metaVariable == containedMetaVariableNames.end()) { + return false; + } + } + return true; + } + + std::set<std::string> const& Dd<DdType::CUDD>::getContainedMetaVariableNames() const { + return this->containedMetaVariableNames; + } + + std::set<std::string>& Dd<DdType::CUDD>::getContainedMetaVariableNames() { + return this->containedMetaVariableNames; + } + + void Dd<DdType::CUDD>::exportToDot(std::string const& filename) const { + std::vector<ADD> cuddAddVector = { this->cuddAdd }; + if (filename.empty()) { + this->getDdManager()->getCuddManager().DumpDot(cuddAddVector); + } else { + // Build the name input of the DD. + std::vector<char*> ddNames; + std::string ddName("f"); + ddNames.push_back(new char[ddName.size() + 1]); + std::copy(ddName.c_str(), ddName.c_str() + 2, ddNames.back()); + + // Now build the variables names. + std::vector<std::string> ddVariableNamesAsStrings = this->getDdManager()->getDdVariableNames(); + std::vector<char*> ddVariableNames; + for (auto const& element : ddVariableNamesAsStrings) { + ddVariableNames.push_back(new char[element.size() + 1]); + std::copy(element.c_str(), element.c_str() + element.size() + 1, ddVariableNames.back()); + } + + // Open the file, dump the DD and close it again. + FILE* filePointer = fopen(filename.c_str() , "w"); + this->getDdManager()->getCuddManager().DumpDot(cuddAddVector, &ddVariableNames[0], &ddNames[0], filePointer); + fclose(filePointer); + + // Finally, delete the names. + for (char* element : ddNames) { + delete element; + } + for (char* element : ddVariableNames) { + delete element; + } + } + } + + ADD Dd<DdType::CUDD>::getCuddAdd() { + return this->cuddAdd; + } + + ADD const& Dd<DdType::CUDD>::getCuddAdd() const { + return this->cuddAdd; + } + + void Dd<DdType::CUDD>::addContainedMetaVariable(std::string const& metaVariableName) { + this->getContainedMetaVariableNames().insert(metaVariableName); + } + + void Dd<DdType::CUDD>::removeContainedMetaVariable(std::string const& metaVariableName) { + this->getContainedMetaVariableNames().erase(metaVariableName); + } + + std::shared_ptr<DdManager<DdType::CUDD>> Dd<DdType::CUDD>::getDdManager() const { + return this->ddManager; + } + + DdForwardIterator<DdType::CUDD> Dd<DdType::CUDD>::begin(bool enumerateDontCareMetaVariables) const { + int* cube; + double value; + DdGen* generator = this->getCuddAdd().FirstCube(&cube, &value); + return DdForwardIterator<DdType::CUDD>(this->getDdManager(), generator, cube, value, (Cudd_IsGenEmpty(generator) != 0), &this->getContainedMetaVariableNames(), enumerateDontCareMetaVariables); + } + + DdForwardIterator<DdType::CUDD> Dd<DdType::CUDD>::end(bool enumerateDontCareMetaVariables) const { + return DdForwardIterator<DdType::CUDD>(this->getDdManager(), nullptr, nullptr, 0, true, nullptr, enumerateDontCareMetaVariables); + } + + storm::expressions::Expression Dd<DdType::CUDD>::toExpression() const { + return toExpressionRecur(this->getCuddAdd().getNode(), this->getDdManager()->getDdVariableNames()); + } + + storm::expressions::Expression Dd<DdType::CUDD>::getMintermExpression() const { + // Note that we first transform the ADD into a BDD to convert all non-zero terminals to ones and therefore + // make the DD more compact. + Dd<DdType::CUDD> tmp(this->getDdManager(), this->getCuddAdd().BddPattern().Add(), this->getContainedMetaVariableNames()); + return getMintermExpressionRecur(this->getDdManager()->getCuddManager().getManager(), this->getCuddAdd().BddPattern().getNode(), this->getDdManager()->getDdVariableNames()); + } + + storm::expressions::Expression Dd<DdType::CUDD>::toExpressionRecur(DdNode const* dd, std::vector<std::string> const& variableNames) { + // If the DD is a terminal node, we can simply return a constant expression. + if (Cudd_IsConstant(dd)) { + return storm::expressions::Expression::createDoubleLiteral(static_cast<double>(Cudd_V(dd))); + } else { + return storm::expressions::Expression::createBooleanVariable(variableNames[dd->index]).ite(toExpressionRecur(Cudd_T(dd), variableNames), toExpressionRecur(Cudd_E(dd), variableNames)); + } + } + + storm::expressions::Expression Dd<DdType::CUDD>::getMintermExpressionRecur(::DdManager* manager, DdNode const* dd, std::vector<std::string> const& variableNames) { + // If the DD is a terminal node, we can simply return a constant expression. + if (Cudd_IsConstant(dd)) { + if (Cudd_IsComplement(dd)) { + return storm::expressions::Expression::createBooleanLiteral(false); + } else { + return storm::expressions::Expression::createBooleanLiteral((dd == Cudd_ReadOne(manager)) ? true : false); + } + } else { + // Get regular versions of the pointers. + DdNode* regularDd = Cudd_Regular(dd); + DdNode* thenDd = Cudd_T(regularDd); + DdNode* elseDd = Cudd_E(regularDd); + + // Compute expression recursively. + storm::expressions::Expression result = storm::expressions::Expression::createBooleanVariable(variableNames[dd->index]).ite(getMintermExpressionRecur(manager, thenDd, variableNames), getMintermExpressionRecur(manager, elseDd, variableNames)); + if (Cudd_IsComplement(dd)) { + result = !result; + } + + return result; + } + } + + std::ostream & operator<<(std::ostream& out, const Dd<DdType::CUDD>& dd) { + dd.exportToDot(); + return out; + } + + // Explicitly instantiate some templated functions. + template std::vector<double> Dd<DdType::CUDD>::toVector() const; + template std::vector<double> Dd<DdType::CUDD>::toVector(Odd<DdType::CUDD> const& rowOdd) const; + template void Dd<DdType::CUDD>::addToVectorRec(DdNode const* dd, uint_fast64_t currentLevel, uint_fast64_t maxLevel, uint_fast64_t currentOffset, Odd<DdType::CUDD> const& odd, std::vector<uint_fast64_t> const& ddVariableIndices, std::vector<double>& targetVector) const; + template std::vector<uint_fast64_t> Dd<DdType::CUDD>::toVector() const; + template std::vector<uint_fast64_t> Dd<DdType::CUDD>::toVector(Odd<DdType::CUDD> const& rowOdd) const; + template void Dd<DdType::CUDD>::addToVectorRec(DdNode const* dd, uint_fast64_t currentLevel, uint_fast64_t maxLevel, uint_fast64_t currentOffset, Odd<DdType::CUDD> const& odd, std::vector<uint_fast64_t> const& ddVariableIndices, std::vector<uint_fast64_t>& targetVector) const; + } +} \ No newline at end of file diff --git a/src/storage/dd/CuddDd.h b/src/storage/dd/CuddDd.h new file mode 100644 index 000000000..316e30c89 --- /dev/null +++ b/src/storage/dd/CuddDd.h @@ -0,0 +1,736 @@ +#ifndef STORM_STORAGE_DD_CUDDDD_H_ +#define STORM_STORAGE_DD_CUDDDD_H_ + +#include <map> +#include <set> +#include <memory> +#include <iostream> + +#include "src/storage/dd/Dd.h" +#include "src/storage/dd/CuddDdForwardIterator.h" +#include "src/storage/SparseMatrix.h" +#include "src/storage/expressions/Expression.h" +#include "src/utility/OsDetection.h" + +// Include the C++-interface of CUDD. +#include "cuddObj.hh" + +namespace storm { + namespace dd { + // Forward-declare some classes. + template<DdType Type> class DdManager; + template<DdType Type> class Odd; + + template<> + class Dd<DdType::CUDD> { + public: + // Declare the DdManager and DdIterator class as friend so it can access the internals of a DD. + friend class DdManager<DdType::CUDD>; + friend class DdForwardIterator<DdType::CUDD>; + friend class Odd<DdType::CUDD>; + + // Instantiate all copy/move constructors/assignments with the default implementation. + Dd() = default; + Dd(Dd<DdType::CUDD> const& other) = default; + Dd& operator=(Dd<DdType::CUDD> const& other) = default; +#ifndef WINDOWS + Dd(Dd<DdType::CUDD>&& other) = default; + Dd& operator=(Dd<DdType::CUDD>&& other) = default; +#endif + + /*! + * Retrieves whether the two DDs represent the same function. + * + * @param other The DD that is to be compared with the current one. + * @return True if the DDs represent the same function. + */ + bool operator==(Dd<DdType::CUDD> const& other) const; + + /*! + * Retrieves whether the two DDs represent different functions. + * + * @param other The DD that is to be compared with the current one. + * @return True if the DDs represent the different functions. + */ + bool operator!=(Dd<DdType::CUDD> const& other) const; + + /*! + * Performs an if-then-else with the given operands, i.e. maps all valuations that are mapped to a non-zero + * function value to the function values specified by the first DD and all others to the function values + * specified by the second DD. + */ + Dd<DdType::CUDD> ite(Dd<DdType::CUDD> const& thenDd, Dd<DdType::CUDD> const& elseDd) const; + + /*! + * Performs a logical or of the current and the given DD. + * + * @return The logical or of the operands. + */ + Dd<DdType::CUDD> operator||(Dd<DdType::CUDD> const& other) const; + + /*! + * Performs a logical and of the current and the given DD. + * + * @return The logical and of the operands. + */ + Dd<DdType::CUDD> operator&&(Dd<DdType::CUDD> const& other) const; + + /*! + * Adds the two DDs. + * + * @param other The DD to add to the current one. + * @return The result of the addition. + */ + Dd<DdType::CUDD> operator+(Dd<DdType::CUDD> const& other) const; + + /*! + * Adds the given DD to the current one. + * + * @param other The DD to add to the current one. + * @return A reference to the current DD after the operation. + */ + Dd<DdType::CUDD>& operator+=(Dd<DdType::CUDD> const& other); + + /*! + * Multiplies the two DDs. + * + * @param other The DD to multiply with the current one. + * @return The result of the multiplication. + */ + Dd<DdType::CUDD> operator*(Dd<DdType::CUDD> const& other) const; + + /*! + * Multiplies the given DD with the current one and assigns the result to the current DD. + * + * @param other The DD to multiply with the current one. + * @return A reference to the current DD after the operation. + */ + Dd<DdType::CUDD>& operator*=(Dd<DdType::CUDD> const& other); + + /*! + * Subtracts the given DD from the current one. + * + * @param other The DD to subtract from the current one. + * @return The result of the subtraction. + */ + Dd<DdType::CUDD> operator-(Dd<DdType::CUDD> const& other) const; + + /*! + * Subtracts the DD from the constant zero function. + * + * @return The resulting function represented as a DD. + */ + Dd<DdType::CUDD> operator-() const; + + /*! + * Subtracts the given DD from the current one and assigns the result to the current DD. + * + * @param other The DD to subtract from the current one. + * @return A reference to the current DD after the operation. + */ + Dd<DdType::CUDD>& operator-=(Dd<DdType::CUDD> const& other); + + /*! + * Divides the current DD by the given one. + * + * @param other The DD by which to divide the current one. + * @return The result of the division. + */ + Dd<DdType::CUDD> operator/(Dd<DdType::CUDD> const& other) const; + + /*! + * Divides the current DD by the given one and assigns the result to the current DD. + * + * @param other The DD by which to divide the current one. + * @return A reference to the current DD after the operation. + */ + Dd<DdType::CUDD>& operator/=(Dd<DdType::CUDD> const& other); + + /*! + * Retrieves the logical complement of the current DD. The result will map all encodings with a value + * unequal to zero to false and all others to true. + * + * @return The logical complement of the current DD. + */ + Dd<DdType::CUDD> operator!() const; + + /*! + * Logically complements the current DD. The result will map all encodings with a value + * unequal to zero to false and all others to true. + * + * @return A reference to the current DD after the operation. + */ + Dd<DdType::CUDD>& complement(); + + /*! + * Retrieves the function that maps all evaluations to one that have an identical function values. + * + * @param other The DD with which to perform the operation. + * @return The resulting function represented as a DD. + */ + Dd<DdType::CUDD> equals(Dd<DdType::CUDD> const& other) const; + + /*! + * Retrieves the function that maps all evaluations to one that have distinct function values. + * + * @param other The DD with which to perform the operation. + * @return The resulting function represented as a DD. + */ + Dd<DdType::CUDD> notEquals(Dd<DdType::CUDD> const& other) const; + + /*! + * Retrieves the function that maps all evaluations to one whose function value in the first DD are less + * than the one in the given DD. + * + * @param other The DD with which to perform the operation. + * @return The resulting function represented as a DD. + */ + Dd<DdType::CUDD> less(Dd<DdType::CUDD> const& other) const; + + /*! + * Retrieves the function that maps all evaluations to one whose function value in the first DD are less or + * equal than the one in the given DD. + * + * @param other The DD with which to perform the operation. + * @return The resulting function represented as a DD. + */ + Dd<DdType::CUDD> lessOrEqual(Dd<DdType::CUDD> const& other) const; + + /*! + * Retrieves the function that maps all evaluations to one whose function value in the first DD are greater + * than the one in the given DD. + * + * @param other The DD with which to perform the operation. + * @return The resulting function represented as a DD. + */ + Dd<DdType::CUDD> greater(Dd<DdType::CUDD> const& other) const; + + /*! + * Retrieves the function that maps all evaluations to one whose function value in the first DD are greater + * or equal than the one in the given DD. + * + * @param other The DD with which to perform the operation. + * @return The resulting function represented as a DD. + */ + Dd<DdType::CUDD> greaterOrEqual(Dd<DdType::CUDD> const& other) const; + + /*! + * Retrieves the function that maps all evaluations to the minimum of the function values of the two DDs. + * + * @param other The DD with which to perform the operation. + * @return The resulting function represented as a DD. + */ + Dd<DdType::CUDD> minimum(Dd<DdType::CUDD> const& other) const; + + /*! + * Retrieves the function that maps all evaluations to the maximum of the function values of the two DDs. + * + * @param other The DD with which to perform the operation. + * @return The resulting function represented as a DD. + */ + Dd<DdType::CUDD> maximum(Dd<DdType::CUDD> const& other) const; + + /*! + * Existentially abstracts from the given meta variables. + * + * @param metaVariableNames The names of all meta variables from which to abstract. + */ + Dd<DdType::CUDD> existsAbstract(std::set<std::string> const& metaVariableNames) const; + + /*! + * Universally abstracts from the given meta variables. + * + * @param metaVariableNames The names of all meta variables from which to abstract. + */ + Dd<DdType::CUDD> universalAbstract(std::set<std::string> const& metaVariableNames) const; + + /*! + * Sum-abstracts from the given meta variables. + * + * @param metaVariableNames The names of all meta variables from which to abstract. + */ + Dd<DdType::CUDD> sumAbstract(std::set<std::string> const& metaVariableNames) const; + + /*! + * Min-abstracts from the given meta variables. + * + * @param metaVariableNames The names of all meta variables from which to abstract. + */ + Dd<DdType::CUDD> minAbstract(std::set<std::string> const& metaVariableNames) const; + + /*! + * Max-abstracts from the given meta variables. + * + * @param metaVariableNames The names of all meta variables from which to abstract. + */ + Dd<DdType::CUDD> maxAbstract(std::set<std::string> const& metaVariableNames) const; + + /*! + * Checks whether the current and the given DD represent the same function modulo some given precision. + * + * @param other The DD with which to compare. + * @param precision An upper bound on the maximal difference between any two function values that is to be + * tolerated. + * @param relative If set to true, not the absolute values have to be within the precision, but the relative + * values. + */ + bool equalModuloPrecision(Dd<DdType::CUDD> const& other, double precision, bool relative = true) const; + + /*! + * Swaps the given pairs of meta variables in the DD. The pairs of meta variables must be guaranteed to have + * the same number of underlying DD variables. + * + * @param metaVariablePairs A vector of meta variable pairs that are to be swapped for one another. + */ + void swapVariables(std::vector<std::pair<std::string, std::string>> const& metaVariablePairs); + + /*! + * Multiplies the current DD (representing a matrix) with the given matrix by summing over the given meta + * variables. + * + * @param otherMatrix The matrix with which to multiply. + * @param summationMetaVariableNames The names of the meta variables over which to sum during the matrix- + * matrix multiplication. + * @return A DD representing the result of the matrix-matrix multiplication. + */ + Dd<DdType::CUDD> multiplyMatrix(Dd<DdType::CUDD> const& otherMatrix, std::set<std::string> const& summationMetaVariableNames) const; + + /*! + * Computes a DD that represents the function in which all assignments with a function value strictly larger + * than the given value are mapped to one and all others to zero. + * + * @param value The value used for the comparison. + * @return The resulting DD. + */ + Dd<DdType::CUDD> greater(double value) const; + + /*! + * Computes a DD that represents the function in which all assignments with a function value larger or equal + * to the given value are mapped to one and all others to zero. + * + * @param value The value used for the comparison. + * @return The resulting DD. + */ + Dd<DdType::CUDD> greaterOrEqual(double value) const; + + /*! + * Computes a DD that represents the function in which all assignments with a function value unequal to zero + * are mapped to one and all others to zero. + * + * @return The resulting DD. + */ + Dd<DdType::CUDD> notZero() const; + + /*! + * Computes the constraint of the current DD with the given constraint. That is, the function value of the + * resulting DD will be the same as the current ones for all assignments mapping to one in the constraint + * and may be different otherwise. + * + * @param constraint The constraint to use for the operation. + * @return The resulting DD. + */ + Dd<DdType::CUDD> constrain(Dd<DdType::CUDD> const& constraint) const; + + /*! + * Computes the restriction of the current DD with the given constraint. That is, the function value of the + * resulting DD will be the same as the current ones for all assignments mapping to one in the constraint + * and may be different otherwise. + * + * @param constraint The constraint to use for the operation. + * @return The resulting DD. + */ + Dd<DdType::CUDD> restrict(Dd<DdType::CUDD> const& constraint) const; + + /*! + * Retrieves the support of the current DD. + * + * @return The support represented as a DD. + */ + Dd<DdType::CUDD> getSupport() const; + + /*! + * Retrieves the number of encodings that are mapped to a non-zero value. + * + * @return The number of encodings that are mapped to a non-zero value. + */ + uint_fast64_t getNonZeroCount() const; + + /*! + * Retrieves the number of leaves of the DD. + * + * @return The number of leaves of the DD. + */ + uint_fast64_t getLeafCount() const; + + /*! + * Retrieves the number of nodes necessary to represent the DD. + * + * @return The number of nodes in this DD. + */ + uint_fast64_t getNodeCount() const; + + /*! + * Retrieves the lowest function value of any encoding. + * + * @return The lowest function value of any encoding. + */ + double getMin() const; + + /*! + * Retrieves the highest function value of any encoding. + * + * @return The highest function value of any encoding. + */ + double getMax() const; + + /*! + * Sets the function values of all encodings that have the given value of the meta variable to the given + * target value. + * + * @param metaVariableName The name of the meta variable that has to be equal to the given value. + * @param variableValue The value that the meta variable is supposed to have. This must be within the range + * of the meta variable. + * @param targetValue The new function value of the modified encodings. + */ + void setValue(std::string const& metaVariableName, int_fast64_t variableValue, double targetValue); + + /*! + * Sets the function values of all encodings that have the given values of the two meta variables to the + * given target value. + * + * @param metaVariableName1 The name of the first meta variable that has to be equal to the first given + * value. + * @param variableValue1 The value that the first meta variable is supposed to have. This must be within the + * range of the meta variable. + * @param metaVariableName2 The name of the first meta variable that has to be equal to the second given + * value. + * @param variableValue2 The value that the second meta variable is supposed to have. This must be within + * the range of the meta variable. + * @param targetValue The new function value of the modified encodings. + */ + void setValue(std::string const& metaVariableName1, int_fast64_t variableValue1, std::string const& metaVariableName2, int_fast64_t variableValue2, double targetValue); + + /*! + * Sets the function values of all encodings that have the given values of the given meta variables to the + * given target value. + * + * @param metaVariableNameToValueMap A mapping of meta variable names to the values they are supposed to + * have. All values must be within the range of the respective meta variable. + * @param targetValue The new function value of the modified encodings. + */ + void setValue(std::map<std::string, int_fast64_t> const& metaVariableNameToValueMap = std::map<std::string, int_fast64_t>(), double targetValue = 0); + + /*! + * Retrieves the value of the function when all meta variables are assigned the values of the given mapping. + * Note that the mapping must specify values for all meta variables contained in the DD. + * + * @param metaVariableNameToValueMap A mapping of meta variable names to their values. + * @return The value of the function evaluated with the given input. + */ + double getValue(std::map<std::string, int_fast64_t> const& metaVariableNameToValueMap = std::map<std::string, int_fast64_t>()) const; + + /*! + * Retrieves whether this DD represents the constant one function. + * + * @return True if this DD represents the constant one function. + */ + bool isOne() const; + + /*! + * Retrieves whether this DD represents the constant zero function. + * + * @return True if this DD represents the constant zero function. + */ + bool isZero() const; + + /*! + * Retrieves whether this DD represents a constant function. + * + * @return True if this DD represents a constants function. + */ + bool isConstant() const; + + /*! + * Retrieves the index of the topmost variable in the DD. + * + * @return The index of the topmost variable in DD. + */ + uint_fast64_t getIndex() const; + + /*! + * Converts the DD to a vector. + * + * @return The double vector that is represented by this DD. + */ + template<typename ValueType> + std::vector<ValueType> toVector() const; + + /*! + * Converts the DD to a vector. The given offset-labeled DD is used to determine the correct row of + * each entry. + * + * @param rowOdd The ODD used for determining the correct row. + * @return The double vector that is represented by this DD. + */ + template<typename ValueType> + std::vector<ValueType> toVector(storm::dd::Odd<DdType::CUDD> const& rowOdd) const; + + /*! + * Converts the DD to a (sparse) double matrix. All contained non-primed variables are assumed to encode the + * row, whereas all primed variables are assumed to encode the column. + * + * @return The matrix that is represented by this DD. + */ + storm::storage::SparseMatrix<double> toMatrix() const; + + /*! + * Converts the DD to a (sparse) double matrix. All contained non-primed variables are assumed to encode the + * row, whereas all primed variables are assumed to encode the column. The given offset-labeled DDs are used + * to determine the correct row and column, respectively, for each entry. + * + * @param rowOdd The ODD used for determining the correct row. + * @param columnOdd The ODD used for determining the correct column. + * @return The matrix that is represented by this DD. + */ + storm::storage::SparseMatrix<double> toMatrix(storm::dd::Odd<DdType::CUDD> const& rowOdd, storm::dd::Odd<DdType::CUDD> const& columnOdd) const; + + /*! + * Converts the DD to a (sparse) double matrix. The given offset-labeled DDs are used to determine the + * correct row and column, respectively, for each entry. + * + * @param rowMetaVariables The meta variables that encode the rows of the matrix. + * @param columnMetaVariables The meta variables that encode the columns of the matrix. + * @param rowOdd The ODD used for determining the correct row. + * @param columnOdd The ODD used for determining the correct column. + * @return The matrix that is represented by this DD. + */ + storm::storage::SparseMatrix<double> toMatrix(std::set<std::string> const& rowMetaVariables, std::set<std::string> const& columnMetaVariables, storm::dd::Odd<DdType::CUDD> const& rowOdd, storm::dd::Odd<DdType::CUDD> const& columnOdd) const; + + /*! + * Converts the DD to a row-grouped (sparse) double matrix. The given offset-labeled DDs are used to + * determine the correct row and column, respectively, for each entry. Note: this function assumes that + * the meta variables used to distinguish different row groups are at the very top of the DD. + * + * @param rowMetaVariables The meta variables that encode the rows of the matrix. + * @param columnMetaVariables The meta variables that encode the columns of the matrix. + * @param groupMetaVariables The meta variables that are used to distinguish different row groups. + * @param rowOdd The ODD used for determining the correct row. + * @param columnOdd The ODD used for determining the correct column. + * @return The matrix that is represented by this DD. + */ + storm::storage::SparseMatrix<double> toMatrix(std::set<std::string> const& rowMetaVariables, std::set<std::string> const& columnMetaVariables, std::set<std::string> const& groupMetaVariables, storm::dd::Odd<DdType::CUDD> const& rowOdd, storm::dd::Odd<DdType::CUDD> const& columnOdd) const; + + /*! + * Retrieves whether the given meta variable is contained in the DD. + * + * @param metaVariableName The name of the meta variable for which to query membership. + * @return True iff the meta variable is contained in the DD. + */ + bool containsMetaVariable(std::string const& metaVariableName) const; + + /*! + * Retrieves whether the given meta variables are all contained in the DD. + * + * @param metaVariableNames The names of the meta variable for which to query membership. + * @return True iff all meta variables are contained in the DD. + */ + bool containsMetaVariables(std::set<std::string> metaVariableNames) const; + + /*! + * Retrieves the set of all names of meta variables contained in the DD. + * + * @return The set of names of all meta variables contained in the DD. + */ + std::set<std::string> const& getContainedMetaVariableNames() const; + + /*! + * Retrieves the set of all names of meta variables contained in the DD. + * + * @return The set of names of all meta variables contained in the DD. + */ + std::set<std::string>& getContainedMetaVariableNames(); + + /*! + * Exports the DD to the given file in the dot format. + * + * @param filename The name of the file to which the DD is to be exported. + */ + void exportToDot(std::string const& filename = "") const; + + /*! + * Retrieves the manager that is responsible for this DD. + * + * A pointer to the manager that is responsible for this DD. + */ + std::shared_ptr<DdManager<DdType::CUDD>> getDdManager() const; + + /*! + * Retrieves an iterator that points to the first meta variable assignment with a non-zero function value. + * + * @param enumerateDontCareMetaVariables If set to true, all meta variable assignments are enumerated, even + * if a meta variable does not at all influence the the function value. + * @return An iterator that points to the first meta variable assignment with a non-zero function value. + */ + DdForwardIterator<DdType::CUDD> begin(bool enumerateDontCareMetaVariables = true) const; + + /*! + * Retrieves an iterator that points past the end of the container. + * + * @param enumerateDontCareMetaVariables If set to true, all meta variable assignments are enumerated, even + * if a meta variable does not at all influence the the function value. + * @return An iterator that points past the end of the container. + */ + DdForwardIterator<DdType::CUDD> end(bool enumerateDontCareMetaVariables = true) const; + + /*! + * Converts the DD into a (heavily nested) if-then-else expression that represents the very same function. + * The variable names used in the expression are derived from the meta variable name and are extended with a + * suffix ".i" if the meta variable is integer-valued, expressing that the variable is the i-th bit of the + * meta variable. + * + * @return The resulting expression. + */ + storm::expressions::Expression toExpression() const; + + /*! + * Converts the DD into a (heavily nested) if-then-else (with negations) expression that evaluates to true + * if and only if the assignment is minterm of the DD. The variable names used in the expression are derived + * from the meta variable name and are extended with a suffix ".i" if the meta variable is integer-valued, + * expressing that the variable is the i-th bit of the meta variable. + * + * @return The resulting expression. + */ + storm::expressions::Expression getMintermExpression() const; + + friend std::ostream & operator<<(std::ostream& out, const Dd<DdType::CUDD>& dd); + private: + /*! + * Retrieves a reference to the CUDD ADD object associated with this DD. + * + * @return The CUDD ADD object associated with this DD. + */ + ADD getCuddAdd(); + + /*! + * Retrieves the CUDD ADD object associated with this DD. + * + * @return The CUDD ADD object assoicated with this DD. + */ + ADD const& getCuddAdd() const; + + /*! + * Adds the given meta variable name to the set of meta variables that are contained in this DD. + * + * @param metaVariableName The name of the meta variable to add. + */ + void addContainedMetaVariable(std::string const& metaVariableName); + + /*! + * Removes the given meta variable name to the set of meta variables that are contained in this DD. + * + * @param metaVariableName The name of the meta variable to remove. + */ + void removeContainedMetaVariable(std::string const& metaVariableName); + + /*! + * Performs the recursive step of toExpression on the given DD. + * + * @param dd The dd to translate into an expression. + * @param variableNames The names of the variables to use in the expression. + * @return The resulting expression. + */ + static storm::expressions::Expression toExpressionRecur(DdNode const* dd, std::vector<std::string> const& variableNames); + + /*! + * Performs the recursive step of getMintermExpression on the given DD. + * + * @param manager The manager of the DD. + * @param dd The dd whose minterms to translate into an expression. + * @param variableNames The names of the variables to use in the expression. + * @return The resulting expression. + */ + static storm::expressions::Expression getMintermExpressionRecur(::DdManager* manager, DdNode const* dd, std::vector<std::string> const& variableNames); + + /*! + * Creates a DD that encapsulates the given CUDD ADD. + * + * @param ddManager The manager responsible for this DD. + * @param cuddAdd The CUDD ADD to store. + * @param containedMetaVariableNames The names of the meta variables that appear in the DD. + */ + Dd(std::shared_ptr<DdManager<DdType::CUDD>> ddManager, ADD cuddAdd, std::set<std::string> const& containedMetaVariableNames = std::set<std::string>()); + + /*! + * Helper function to convert the DD into a (sparse) matrix. + * + * @param dd The DD to convert. + * @param rowIndications A vector indicating at which position in the columnsAndValues vector the entries + * of row i start. Note: this vector is modified in the computation. More concretely, each entry i in the + * vector will be increased by the number of entries in the row. This can be used to count the number + * of entries in each row. If the values are not to be modified, a copy needs to be provided or the entries + * need to be restored afterwards. + * @param columnsAndValues The vector that will hold the columns and values of non-zero entries upon successful + * completion. + * @param rowGroupOffsets The row offsets at which a given row group starts. + * @param rowOdd The ODD used for the row translation. + * @param columnOdd The ODD used for the column translation. + * @param currentRowLevel The currently considered row level in the DD. + * @param currentColumnLevel The currently considered row level in the DD. + * @param maxLevel The number of levels that need to be considered. + * @param currentRowOffset The current row offset. + * @param currentColumnOffset The current row offset. + * @param ddRowVariableIndices The (sorted) indices of all DD row variables that need to be considered. + * @param ddColumnVariableIndices The (sorted) indices of all DD row variables that need to be considered. + * @param generateValues If set to true, the vector columnsAndValues is filled with the actual entries, which + * only works if the offsets given in rowIndications are already correct. If they need to be computed first, + * this flag needs to be false. + */ + void toMatrixRec(DdNode const* dd, std::vector<uint_fast64_t>& rowIndications, std::vector<storm::storage::MatrixEntry<double>>& columnsAndValues, std::vector<uint_fast64_t> const& rowGroupOffsets, Odd<DdType::CUDD> const& rowOdd, Odd<DdType::CUDD> const& columnOdd, uint_fast64_t currentRowLevel, uint_fast64_t currentColumnLevel, uint_fast64_t maxLevel, uint_fast64_t currentRowOffset, uint_fast64_t currentColumnOffset, std::vector<uint_fast64_t> const& ddRowVariableIndices, std::vector<uint_fast64_t> const& ddColumnVariableIndices, bool generateValues = true) const; + + /*! + * Splits the given matrix DD into the groups using the given group variables. + * + * @param dd The DD to split. + * @param groups A vector that is to be filled with the DDs for the individual groups. + * @param ddGroupVariableIndices The (sorted) indices of all DD group variables that need to be considered. + * @param currentLevel The currently considered level in the DD. + * @param maxLevel The number of levels that need to be considered. + * @param remainingMetaVariables The meta variables that remain in the DDs after the groups have been split. + */ + void splitGroupsRec(DdNode* dd, std::vector<Dd<DdType::CUDD>>& groups, std::vector<uint_fast64_t> const& ddGroupVariableIndices, uint_fast64_t currentLevel, uint_fast64_t maxLevel, std::set<std::string> const& remainingMetaVariables) const; + + /*! + * Performs a recursive step to add the given DD-based vector to the given explicit vector. + * + * @param dd The DD to add to the explicit vector. + * @param currentLevel The currently considered level in the DD. + * @param maxLevel The number of levels that need to be considered. + * @param currentOffset The current offset. + * @param odd The ODD used for the translation. + * @param ddVariableIndices The (sorted) indices of all DD variables that need to be considered. + * @param targetVector The vector to which the translated DD-based vector is to be added. + */ + template<typename ValueType> + void addToVectorRec(DdNode const* dd, uint_fast64_t currentLevel, uint_fast64_t maxLevel, uint_fast64_t currentOffset, Odd<DdType::CUDD> const& odd, std::vector<uint_fast64_t> const& ddVariableIndices, std::vector<ValueType>& targetVector) const; + + /*! + * Retrieves the indices of all DD variables that are contained in this DD (not necessarily in the support, + * because they could be "don't cares"). Additionally, the indices are sorted to allow for easy access. + * + * @return The (sorted) indices of all DD variables that are contained in this DD. + */ + std::vector<uint_fast64_t> getSortedVariableIndices() const; + + // A pointer to the manager responsible for this DD. + std::shared_ptr<DdManager<DdType::CUDD>> ddManager; + + // The ADD created by CUDD. + ADD cuddAdd; + + // The names of all meta variables that appear in this DD. + std::set<std::string> containedMetaVariableNames; + }; + } +} + +#endif /* STORM_STORAGE_DD_CUDDDD_H_ */ \ No newline at end of file diff --git a/src/storage/dd/CuddDdForwardIterator.cpp b/src/storage/dd/CuddDdForwardIterator.cpp new file mode 100644 index 000000000..4a990746e --- /dev/null +++ b/src/storage/dd/CuddDdForwardIterator.cpp @@ -0,0 +1,197 @@ +#include "src/storage/dd/CuddDdForwardIterator.h" +#include "src/storage/dd/CuddDdManager.h" +#include "src/storage/dd/DdMetaVariable.h" +#include "src/exceptions/ExceptionMacros.h" + +namespace storm { + namespace dd { + DdForwardIterator<DdType::CUDD>::DdForwardIterator() : ddManager(), generator(), cube(), value(), isAtEnd(), metaVariables(), enumerateDontCareMetaVariables(), cubeCounter(), relevantDontCareDdVariables(), currentValuation() { + // Intentionally left empty. + } + + DdForwardIterator<DdType::CUDD>::DdForwardIterator(std::shared_ptr<DdManager<DdType::CUDD>> ddManager, DdGen* generator, int* cube, double value, bool isAtEnd, std::set<std::string> const* metaVariables, bool enumerateDontCareMetaVariables) : ddManager(ddManager), generator(generator), cube(cube), value(value), isAtEnd(isAtEnd), metaVariables(metaVariables), enumerateDontCareMetaVariables(enumerateDontCareMetaVariables), cubeCounter(), relevantDontCareDdVariables(), currentValuation() { + // If the given generator is not yet at its end, we need to create the current valuation from the cube from + // scratch. + if (!this->isAtEnd) { + // Start by registering all meta variables in the valuation with the appropriate type. + for (auto const& metaVariableName : *this->metaVariables) { + auto const& metaVariable = this->ddManager->getMetaVariable(metaVariableName); + switch (metaVariable.getType()) { + case DdMetaVariable<DdType::CUDD>::MetaVariableType::Bool: currentValuation.addBooleanIdentifier(metaVariableName); break; + case DdMetaVariable<DdType::CUDD>::MetaVariableType::Int: currentValuation.addIntegerIdentifier(metaVariableName); break; + } + } + + // And then get ready for treating the first cube. + this->treatNewCube(); + } + } + + DdForwardIterator<DdType::CUDD>::DdForwardIterator(DdForwardIterator<DdType::CUDD>&& other) : ddManager(other.ddManager), generator(other.generator), cube(other.cube), value(other.value), isAtEnd(other.isAtEnd), metaVariables(other.metaVariables), cubeCounter(other.cubeCounter), relevantDontCareDdVariables(other.relevantDontCareDdVariables), currentValuation(other.currentValuation) { + // Null-out the pointers of which we took possession. + other.cube = nullptr; + other.generator = nullptr; + } + + DdForwardIterator<DdType::CUDD>& DdForwardIterator<DdType::CUDD>::operator=(DdForwardIterator<DdType::CUDD>&& other) { + if (this != &other) { + this->ddManager = other.ddManager; + this->generator = other.generator; + this->cube = other.cube; + this->value = other.value; + this->isAtEnd = other.isAtEnd; + this->metaVariables = other.metaVariables; + this->cubeCounter = other.cubeCounter; + this->relevantDontCareDdVariables = other.relevantDontCareDdVariables; + this->currentValuation = other.currentValuation; + + // Null-out the pointers of which we took possession. + other.cube = nullptr; + other.generator = nullptr; + } + return *this; + } + + DdForwardIterator<DdType::CUDD>::~DdForwardIterator() { + // We free the pointers sind Cudd allocates them using malloc rather than new/delete. + if (this->cube != nullptr) { + free(this->cube); + } + if (this->generator != nullptr) { + free(this->generator); + } + } + + DdForwardIterator<DdType::CUDD>& DdForwardIterator<DdType::CUDD>::operator++() { + LOG_ASSERT(!this->isAtEnd, "Illegally incrementing iterator that is already at its end."); + + // If there were no (relevant) don't cares or we have enumerated all combination, we need to eliminate the + // found solutions and get the next "first" cube. + if (this->relevantDontCareDdVariables.empty() || this->cubeCounter >= std::pow(2, this->relevantDontCareDdVariables.size()) - 1) { + // Get the next cube and check for emptiness. + ABDD::NextCube(generator, &cube, &value); + this->isAtEnd = (Cudd_IsGenEmpty(generator) != 0); + + // In case we are not done yet, we get ready to treat the next cube. + if (!this->isAtEnd) { + this->treatNewCube(); + } + } else { + // Treat the next solution of the cube. + this->treatNextInCube(); + } + + return *this; + } + + void DdForwardIterator<DdType::CUDD>::treatNextInCube() { + // Start by increasing the counter and check which bits we need to set/unset in the new valuation. + ++this->cubeCounter; + + for (uint_fast64_t index = 0; index < this->relevantDontCareDdVariables.size(); ++index) { + auto const& metaVariable = this->ddManager->getMetaVariable(std::get<1>(this->relevantDontCareDdVariables[index])); + if (metaVariable.getType() == DdMetaVariable<DdType::CUDD>::MetaVariableType::Bool) { + if ((this->cubeCounter & (1ull << index)) != 0) { + currentValuation.setBooleanValue(metaVariable.getName(), true); + } else { + currentValuation.setBooleanValue(metaVariable.getName(), false); + } + } else { + if ((this->cubeCounter & (1ull << index)) != 0) { + currentValuation.setIntegerValue(metaVariable.getName(), ((currentValuation.getIntegerValue(metaVariable.getName()) - metaVariable.getLow()) | (1ull << std::get<2>(this->relevantDontCareDdVariables[index]))) + metaVariable.getLow()); + } else { + currentValuation.setIntegerValue(metaVariable.getName(), ((currentValuation.getIntegerValue(metaVariable.getName()) - metaVariable.getLow()) & ~(1ull << std::get<2>(this->relevantDontCareDdVariables[index]))) + metaVariable.getLow()); + } + } + } + } + + void DdForwardIterator<DdType::CUDD>::treatNewCube() { + this->relevantDontCareDdVariables.clear(); + + // Now loop through the bits of all meta variables and check whether they need to be set, not set or are + // don't cares. In the latter case, we add them to a special list, so we can iterate over their concrete + // valuations later. + for (auto const& metaVariableName : *this->metaVariables) { + bool metaVariableAppearsInCube = false; + std::vector<std::tuple<ADD, std::string, uint_fast64_t>> localRelenvantDontCareDdVariables; + auto const& metaVariable = this->ddManager->getMetaVariable(metaVariableName); + if (metaVariable.getType() == DdMetaVariable<DdType::CUDD>::MetaVariableType::Bool) { + if (this->cube[metaVariable.getDdVariables()[0].getCuddAdd().NodeReadIndex()] == 0) { + metaVariableAppearsInCube = true; + if (!currentValuation.containsBooleanIdentifier(metaVariableName)) { + currentValuation.addBooleanIdentifier(metaVariableName, false); + } else { + currentValuation.setBooleanValue(metaVariableName, false); + } + } else if (this->cube[metaVariable.getDdVariables()[0].getCuddAdd().NodeReadIndex()] == 1) { + metaVariableAppearsInCube = true; + if (!currentValuation.containsBooleanIdentifier(metaVariableName)) { + currentValuation.addBooleanIdentifier(metaVariableName, true); + } else { + currentValuation.setBooleanValue(metaVariableName, true); + } + } else { + localRelenvantDontCareDdVariables.push_back(std::make_tuple(metaVariable.getDdVariables()[0].getCuddAdd(), metaVariableName, 0)); + } + } else { + int_fast64_t intValue = 0; + for (uint_fast64_t bitIndex = 0; bitIndex < metaVariable.getNumberOfDdVariables(); ++bitIndex) { + if (cube[metaVariable.getDdVariables()[bitIndex].getCuddAdd().NodeReadIndex()] == 0) { + // Leave bit unset. + metaVariableAppearsInCube = true; + } else if (cube[metaVariable.getDdVariables()[bitIndex].getCuddAdd().NodeReadIndex()] == 1) { + intValue |= 1ull << (metaVariable.getNumberOfDdVariables() - bitIndex - 1); + metaVariableAppearsInCube = true; + } else { + // Temporarily leave bit unset so we can iterate trough the other option later. + // Add the bit to the relevant don't care bits. + localRelenvantDontCareDdVariables.push_back(std::make_tuple(metaVariable.getDdVariables()[bitIndex].getCuddAdd(), metaVariableName, metaVariable.getNumberOfDdVariables() - bitIndex - 1)); + } + } + if (this->enumerateDontCareMetaVariables || metaVariableAppearsInCube) { + if (!currentValuation.containsIntegerIdentifier(metaVariableName)) { + currentValuation.addIntegerIdentifier(metaVariableName); + } + currentValuation.setIntegerValue(metaVariableName, intValue + metaVariable.getLow()); + } + } + + // If all meta variables are to be enumerated or the meta variable appeared in the cube, we register the + // missing bits to later enumerate all possible valuations. + if (this->enumerateDontCareMetaVariables || metaVariableAppearsInCube) { + relevantDontCareDdVariables.insert(relevantDontCareDdVariables.end(), localRelenvantDontCareDdVariables.begin(), localRelenvantDontCareDdVariables.end()); + } + + // If the meta variable does not appear in the cube and we're not supposed to enumerate such meta variables + // we remove the meta variable from the valuation. + if (!this->enumerateDontCareMetaVariables && !metaVariableAppearsInCube) { + currentValuation.removeIdentifier(metaVariableName); + } + } + + // Finally, reset the cube counter. + this->cubeCounter = 0; + } + + bool DdForwardIterator<DdType::CUDD>::operator==(DdForwardIterator<DdType::CUDD> const& other) const { + if (this->isAtEnd && other.isAtEnd) { + return true; + } else { + return this->ddManager.get() == other.ddManager.get() && this->generator == other.generator + && this->cube == other.cube && this->value == other.value && this->isAtEnd == other.isAtEnd + && this->metaVariables == other.metaVariables && this->cubeCounter == other.cubeCounter + && this->relevantDontCareDdVariables == other.relevantDontCareDdVariables + && this->currentValuation == other.currentValuation; + } + } + + bool DdForwardIterator<DdType::CUDD>::operator!=(DdForwardIterator<DdType::CUDD> const& other) const { + return !(*this == other); + } + + std::pair<storm::expressions::SimpleValuation, double> DdForwardIterator<DdType::CUDD>::operator*() const { + return std::make_pair(currentValuation, value); + } + } +} \ No newline at end of file diff --git a/src/storage/dd/CuddDdForwardIterator.h b/src/storage/dd/CuddDdForwardIterator.h new file mode 100644 index 000000000..8b08af929 --- /dev/null +++ b/src/storage/dd/CuddDdForwardIterator.h @@ -0,0 +1,136 @@ +#ifndef STORM_STORAGE_DD_CUDDDDFORWARDITERATOR_H_ +#define STORM_STORAGE_DD_CUDDDDFORWARDITERATOR_H_ + +#include <memory> +#include <cstdint> +#include <set> +#include <tuple> +#include <utility> + +#include "src/storage/dd/DdForwardIterator.h" +#include "src/storage/expressions/SimpleValuation.h" + +// Include the C++-interface of CUDD. +#include "cuddObj.hh" + +namespace storm { + namespace dd { + // Forward-declare the DdManager class. + template<DdType Type> class DdManager; + template<DdType Type> class Dd; + + template<> + class DdForwardIterator<DdType::CUDD> { + public: + friend class Dd<DdType::CUDD>; + + // Default-instantiate the constructor. + DdForwardIterator(); + + // Forbid copy-construction and copy assignment, because ownership of the internal pointer is unclear then. + DdForwardIterator(DdForwardIterator<DdType::CUDD> const& other) = delete; + DdForwardIterator& operator=(DdForwardIterator<DdType::CUDD> const& other) = delete; + + // Provide move-construction and move-assignment, though. + DdForwardIterator(DdForwardIterator<DdType::CUDD>&& other); + DdForwardIterator& operator=(DdForwardIterator<DdType::CUDD>&& other); + + /*! + * Destroys the forward iterator and frees the generator as well as the cube if they are not the nullptr. + */ + ~DdForwardIterator(); + + /*! + * Moves the iterator one position forward. + */ + DdForwardIterator<DdType::CUDD>& operator++(); + + /*! + * Returns a pair consisting of a valuation of meta variables and the value to which this valuation is + * mapped. Note that the result is returned by value. + * + * @return A pair of a valuation and the function value. + */ + std::pair<storm::expressions::SimpleValuation, double> operator*() const; + + /*! + * Compares the iterator with the given one. Two iterators are considered equal when all their underlying + * data members are the same or they both are at their end. + * + * @param other The iterator with which to compare. + * @return True if the two iterators are considered equal. + */ + bool operator==(DdForwardIterator<DdType::CUDD> const& other) const; + + /*! + * Compares the iterator with the given one. Two iterators are considered unequal iff they are not + * considered equal. + * + * @param other The iterator with which to compare. + * @return True if the two iterators are considered unequal. + */ + bool operator!=(DdForwardIterator<DdType::CUDD> const& other) const; + + private: + /*! + * Constructs a forward iterator using the given generator with the given set of relevant meta variables. + * + * @param ddManager The manager responsible for the DD over which to iterate. + * @param generator The generator used to enumerate the cubes of the DD. + * @param cube The cube as represented by CUDD. + * @param value The value the cube is mapped to. + * @param isAtEnd A flag that indicates whether the iterator is at its end and may not be moved forward any + * more. + * @param metaVariables The meta variables that appear in the DD. + * @param enumerateDontCareMetaVariables If set to true, all meta variable assignments are enumerated, even + * if a meta variable does not at all influence the the function value. + */ + DdForwardIterator(std::shared_ptr<DdManager<DdType::CUDD>> ddManager, DdGen* generator, int* cube, double value, bool isAtEnd, std::set<std::string> const* metaVariables = nullptr, bool enumerateDontCareMetaVariables = true); + + /*! + * Recreates the internal information when a new cube needs to be treated. + */ + void treatNewCube(); + + /*! + * Updates the internal information when the next solution of the current cube needs to be treated. + */ + void treatNextInCube(); + + // The manager responsible for the meta variables (and therefore the underlying DD). + std::shared_ptr<DdManager<DdType::CUDD>> ddManager; + + // The CUDD generator used to enumerate the cubes of the DD. + DdGen* generator; + + // The currently considered cube of the DD. + int* cube; + + // The function value of the current cube. + double value; + + // A flag that indicates whether the iterator is at its end and may not be moved further. This is also used + // for the check against the end iterator. + bool isAtEnd; + + // The set of meta variables appearing in the DD. + std::set<std::string> const* metaVariables; + + // A flag that indicates whether the iterator is supposed to enumerate meta variable valuations even if + // they don't influence the function value. + bool enumerateDontCareMetaVariables; + + // A number that represents how many assignments of the current cube have already been returned previously. + // This is needed, because cubes may represent many assignments (if they have don't care variables). + uint_fast64_t cubeCounter; + + // A vector of tuples of the form <variable, metaVariableName, bitIndex>. + std::vector<std::tuple<ADD, std::string, uint_fast64_t>> relevantDontCareDdVariables; + + // The current valuation of meta variables. + storm::expressions::SimpleValuation currentValuation; + }; + } +} + +#endif /* STORM_STORAGE_DD_CUDDDDFORWARDITERATOR_H_ */ \ No newline at end of file diff --git a/src/storage/dd/CuddDdManager.cpp b/src/storage/dd/CuddDdManager.cpp new file mode 100644 index 000000000..f265a9f6a --- /dev/null +++ b/src/storage/dd/CuddDdManager.cpp @@ -0,0 +1,274 @@ +#include <cmath> +#include <string> +#include <algorithm> + +#include "src/storage/dd/CuddDdManager.h" +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidArgumentException.h" +#include "src/settings/Settings.h" + +bool CuddOptionsRegistered = storm::settings::Settings::registerNewModule([] (storm::settings::Settings* instance) -> bool { + // Set up options for precision and maximal memory available to Cudd. + instance->addOption(storm::settings::OptionBuilder("Cudd", "cuddprec", "", "Sets the precision used by Cudd.").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("value", "The precision up to which to constants are considered to be different.").setDefaultValueDouble(1e-15).addValidationFunctionDouble(storm::settings::ArgumentValidators::doubleRangeValidatorExcluding(0.0, 1.0)).build()).build()); + + instance->addOption(storm::settings::OptionBuilder("Cudd", "cuddmaxmem", "", "Sets the upper bound of memory available to Cudd in MB.").addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("mb", "The memory available to Cudd (0 means unlimited).").setDefaultValueUnsignedInteger(2048).build()).build()); + + // Set up option for reordering. + std::vector<std::string> reorderingTechniques; + reorderingTechniques.push_back("none"); + reorderingTechniques.push_back("random"); + reorderingTechniques.push_back("randompivot"); + reorderingTechniques.push_back("sift"); + reorderingTechniques.push_back("siftconv"); + reorderingTechniques.push_back("ssift"); + reorderingTechniques.push_back("ssiftconv"); + reorderingTechniques.push_back("gsift"); + reorderingTechniques.push_back("gsiftconv"); + reorderingTechniques.push_back("win2"); + reorderingTechniques.push_back("win2conv"); + reorderingTechniques.push_back("win3"); + reorderingTechniques.push_back("win3conv"); + reorderingTechniques.push_back("win4"); + reorderingTechniques.push_back("win4conv"); + reorderingTechniques.push_back("annealing"); + reorderingTechniques.push_back("genetic"); + reorderingTechniques.push_back("exact"); + instance->addOption(storm::settings::OptionBuilder("Cudd", "reorder", "", "Sets the reordering technique used by Cudd.").addArgument(storm::settings::ArgumentBuilder::createStringArgument("method", "Sets which technique is used by Cudd's reordering routines. Must be in {\"none\", \"random\", \"randompivot\", \"sift\", \"siftconv\", \"ssift\", \"ssiftconv\", \"gsift\", \"gsiftconv\", \"win2\", \"win2conv\", \"win3\", \"win3conv\", \"win4\", \"win4conv\", \"annealing\", \"genetic\", \"exact\"}.").setDefaultValueString("gsift").addValidationFunctionString(storm::settings::ArgumentValidators::stringInListValidator(reorderingTechniques)).build()).build()); + + return true; +}); + +namespace storm { + namespace dd { + DdManager<DdType::CUDD>::DdManager() : metaVariableMap(), cuddManager(), reorderingTechnique(CUDD_REORDER_NONE) { + this->cuddManager.SetMaxMemory(static_cast<unsigned long>(storm::settings::Settings::getInstance()->getOptionByLongName("cuddmaxmem").getArgument(0).getValueAsUnsignedInteger() * 1024ul * 1024ul)); + this->cuddManager.SetEpsilon(storm::settings::Settings::getInstance()->getOptionByLongName("cuddprec").getArgument(0).getValueAsDouble()); + + // Now set the selected reordering technique. + std::string const& reorderingTechnique = storm::settings::Settings::getInstance()->getOptionByLongName("reorder").getArgument(0).getValueAsString(); + if (reorderingTechnique == "none") { + this->reorderingTechnique = CUDD_REORDER_NONE; + } else if (reorderingTechnique == "random") { + this->reorderingTechnique = CUDD_REORDER_RANDOM; + } else if (reorderingTechnique == "randompivot") { + this->reorderingTechnique = CUDD_REORDER_RANDOM_PIVOT; + } else if (reorderingTechnique == "sift") { + this->reorderingTechnique = CUDD_REORDER_SIFT; + } else if (reorderingTechnique == "siftconv") { + this->reorderingTechnique = CUDD_REORDER_SIFT_CONVERGE; + } else if (reorderingTechnique == "ssift") { + this->reorderingTechnique = CUDD_REORDER_SYMM_SIFT; + } else if (reorderingTechnique == "ssiftconv") { + this->reorderingTechnique = CUDD_REORDER_SYMM_SIFT_CONV; + } else if (reorderingTechnique == "gsift") { + this->reorderingTechnique = CUDD_REORDER_GROUP_SIFT; + } else if (reorderingTechnique == "gsiftconv") { + this->reorderingTechnique = CUDD_REORDER_GROUP_SIFT_CONV; + } else if (reorderingTechnique == "win2") { + this->reorderingTechnique = CUDD_REORDER_WINDOW2; + } else if (reorderingTechnique == "win2conv") { + this->reorderingTechnique = CUDD_REORDER_WINDOW2_CONV; + } else if (reorderingTechnique == "win3") { + this->reorderingTechnique = CUDD_REORDER_WINDOW3; + } else if (reorderingTechnique == "win3conv") { + this->reorderingTechnique = CUDD_REORDER_WINDOW3_CONV; + } else if (reorderingTechnique == "win4") { + this->reorderingTechnique = CUDD_REORDER_WINDOW4; + } else if (reorderingTechnique == "win4conv") { + this->reorderingTechnique = CUDD_REORDER_WINDOW4_CONV; + } else if (reorderingTechnique == "annealing") { + this->reorderingTechnique = CUDD_REORDER_ANNEALING; + } else if (reorderingTechnique == "genetic") { + this->reorderingTechnique = CUDD_REORDER_GENETIC; + } else if (reorderingTechnique == "exact") { + this->reorderingTechnique = CUDD_REORDER_EXACT; + } + } + + Dd<DdType::CUDD> DdManager<DdType::CUDD>::getOne() { + return Dd<DdType::CUDD>(this->shared_from_this(), cuddManager.addOne()); + } + + Dd<DdType::CUDD> DdManager<DdType::CUDD>::getZero() { + return Dd<DdType::CUDD>(this->shared_from_this(), cuddManager.addZero()); + } + + Dd<DdType::CUDD> DdManager<DdType::CUDD>::getConstant(double value) { + return Dd<DdType::CUDD>(this->shared_from_this(), cuddManager.constant(value)); + } + + Dd<DdType::CUDD> DdManager<DdType::CUDD>::getEncoding(std::string const& metaVariableName, int_fast64_t value) { + DdMetaVariable<DdType::CUDD> const& metaVariable = this->getMetaVariable(metaVariableName); + + LOG_THROW(value >= metaVariable.getLow() && value <= metaVariable.getHigh(), storm::exceptions::InvalidArgumentException, "Illegal value " << value << " for meta variable '" << metaVariableName << "'."); + + // Now compute the encoding relative to the low value of the meta variable. + value -= metaVariable.getLow(); + + std::vector<Dd<DdType::CUDD>> const& ddVariables = metaVariable.getDdVariables(); + + Dd<DdType::CUDD> result; + if (value & (1ull << (ddVariables.size() - 1))) { + result = ddVariables[0]; + } else { + result = !ddVariables[0]; + } + + for (std::size_t i = 1; i < ddVariables.size(); ++i) { + if (value & (1ull << (ddVariables.size() - i - 1))) { + result *= ddVariables[i]; + } else { + result *= !ddVariables[i]; + } + } + + return result; + } + + Dd<DdType::CUDD> DdManager<DdType::CUDD>::getRange(std::string const& metaVariableName) { + storm::dd::DdMetaVariable<DdType::CUDD> const& metaVariable = this->getMetaVariable(metaVariableName); + + Dd<DdType::CUDD> result = this->getZero(); + + for (int_fast64_t value = metaVariable.getLow(); value <= metaVariable.getHigh(); ++value) { + result.setValue(metaVariableName, value, static_cast<double>(1)); + } + return result; + } + + Dd<DdType::CUDD> DdManager<DdType::CUDD>::getIdentity(std::string const& metaVariableName) { + storm::dd::DdMetaVariable<DdType::CUDD> const& metaVariable = this->getMetaVariable(metaVariableName); + + Dd<DdType::CUDD> result = this->getZero(); + for (int_fast64_t value = metaVariable.getLow(); value <= metaVariable.getHigh(); ++value) { + result.setValue(metaVariableName, value, static_cast<double>(value)); + } + return result; + } + + void DdManager<DdType::CUDD>::addMetaVariable(std::string const& name, int_fast64_t low, int_fast64_t high) { + // Check whether the variable name is legal. + LOG_THROW(name != "" && name.back() != '\'', storm::exceptions::InvalidArgumentException, "Illegal name of meta variable: '" << name << "'."); + + // Check whether a meta variable already exists. + LOG_THROW(!this->hasMetaVariable(name), storm::exceptions::InvalidArgumentException, "A meta variable '" << name << "' already exists."); + + // Check that the range is legal. + LOG_THROW(high != low, storm::exceptions::InvalidArgumentException, "Range of meta variable must be at least 2 elements."); + + std::size_t numberOfBits = static_cast<std::size_t>(std::ceil(std::log2(high - low + 1))); + + std::vector<Dd<DdType::CUDD>> variables; + std::vector<Dd<DdType::CUDD>> variablesPrime; + for (std::size_t i = 0; i < numberOfBits; ++i) { + variables.emplace_back(Dd<DdType::CUDD>(this->shared_from_this(), cuddManager.addVar(), {name})); + variablesPrime.emplace_back(Dd<DdType::CUDD>(this->shared_from_this(), cuddManager.addVar(), {name + "'"})); + } + + // Now group the non-primed and primed variable. + for (uint_fast64_t i = 0; i < numberOfBits; ++i) { + this->getCuddManager().MakeTreeNode(variables[i].getCuddAdd().NodeReadIndex(), 2, MTR_FIXED); + } + + metaVariableMap.emplace(name, DdMetaVariable<DdType::CUDD>(name, low, high, variables, this->shared_from_this())); + metaVariableMap.emplace(name + "'", DdMetaVariable<DdType::CUDD>(name + "'", low, high, variablesPrime, this->shared_from_this())); + } + + void DdManager<DdType::CUDD>::addMetaVariable(std::string const& name) { + // Check whether the variable name is legal. + LOG_THROW(name != "" && name.back() != '\'', storm::exceptions::InvalidArgumentException, "Illegal name of meta variable: '" << name << "'."); + + // Check whether a meta variable already exists. + LOG_THROW(!this->hasMetaVariable(name), storm::exceptions::InvalidArgumentException, "A meta variable '" << name << "' already exists."); + + std::vector<Dd<DdType::CUDD>> variables; + std::vector<Dd<DdType::CUDD>> variablesPrime; + variables.emplace_back(Dd<DdType::CUDD>(this->shared_from_this(), cuddManager.addVar(), {name})); + variablesPrime.emplace_back(Dd<DdType::CUDD>(this->shared_from_this(), cuddManager.addVar(), {name + "'"})); + + // Now group the non-primed and primed variable. + this->getCuddManager().MakeTreeNode(variables.front().getCuddAdd().NodeReadIndex(), 2, MTR_FIXED); + + metaVariableMap.emplace(name, DdMetaVariable<DdType::CUDD>(name, variables, this->shared_from_this())); + metaVariableMap.emplace(name + "'", DdMetaVariable<DdType::CUDD>(name + "'", variablesPrime, this->shared_from_this())); + } + + DdMetaVariable<DdType::CUDD> const& DdManager<DdType::CUDD>::getMetaVariable(std::string const& metaVariableName) const { + auto const& nameVariablePair = metaVariableMap.find(metaVariableName); + + // Check whether the meta variable exists. + LOG_THROW(nameVariablePair != metaVariableMap.end(), storm::exceptions::InvalidArgumentException, "Unknown meta variable name '" << metaVariableName << "'."); + + return nameVariablePair->second; + } + + std::set<std::string> DdManager<DdType::CUDD>::getAllMetaVariableNames() const { + std::set<std::string> result; + for (auto const& nameValuePair : metaVariableMap) { + result.insert(nameValuePair.first); + } + return result; + } + + std::size_t DdManager<DdType::CUDD>::getNumberOfMetaVariables() const { + return this->metaVariableMap.size(); + } + + bool DdManager<DdType::CUDD>::hasMetaVariable(std::string const& metaVariableName) const { + return this->metaVariableMap.find(metaVariableName) != this->metaVariableMap.end(); + } + + Cudd& DdManager<DdType::CUDD>::getCuddManager() { + return this->cuddManager; + } + + Cudd const& DdManager<DdType::CUDD>::getCuddManager() const { + return this->cuddManager; + } + + std::vector<std::string> DdManager<DdType::CUDD>::getDdVariableNames() const { + // First, we initialize a list DD variables and their names. + std::vector<std::pair<ADD, std::string>> variableNamePairs; + for (auto const& nameMetaVariablePair : this->metaVariableMap) { + DdMetaVariable<DdType::CUDD> const& metaVariable = nameMetaVariablePair.second; + // If the meta variable is of type bool, we don't need to suffix it with the bit number. + if (metaVariable.getType() == DdMetaVariable<storm::dd::DdType::CUDD>::MetaVariableType::Bool) { + variableNamePairs.emplace_back(metaVariable.getDdVariables().front().getCuddAdd(), metaVariable.getName()); + } else { + // For integer-valued meta variables, we, however, have to add the suffix. + for (uint_fast64_t variableIndex = 0; variableIndex < metaVariable.getNumberOfDdVariables(); ++variableIndex) { + variableNamePairs.emplace_back(metaVariable.getDdVariables()[variableIndex].getCuddAdd(), metaVariable.getName() + "." + std::to_string(variableIndex)); + } + } + } + + // Then, we sort this list according to the indices of the ADDs. + std::sort(variableNamePairs.begin(), variableNamePairs.end(), [](std::pair<ADD, std::string> const& a, std::pair<ADD, std::string> const& b) { return a.first.NodeReadIndex() < b.first.NodeReadIndex(); }); + + // Now, we project the sorted vector to its second component. + std::vector<std::string> result; + for (auto const& element : variableNamePairs) { + result.push_back(element.second); + } + + return result; + } + + void DdManager<DdType::CUDD>::allowDynamicReordering(bool value) { + if (value) { + this->getCuddManager().AutodynEnable(this->reorderingTechnique); + } else { + this->getCuddManager().AutodynDisable(); + } + } + + bool DdManager<DdType::CUDD>::isDynamicReorderingAllowed() const { + Cudd_ReorderingType type; + return this->getCuddManager().ReorderingStatus(&type); + } + + void DdManager<DdType::CUDD>::triggerReordering() { + this->getCuddManager().ReduceHeap(this->reorderingTechnique, 0); + } + } +} \ No newline at end of file diff --git a/src/storage/dd/CuddDdManager.h b/src/storage/dd/CuddDdManager.h new file mode 100644 index 000000000..682fb4c8b --- /dev/null +++ b/src/storage/dd/CuddDdManager.h @@ -0,0 +1,184 @@ +#ifndef STORM_STORAGE_DD_CUDDDDMANAGER_H_ +#define STORM_STORAGE_DD_CUDDDDMANAGER_H_ + +#include <unordered_map> + +#include "src/storage/dd/DdManager.h" +#include "src/storage/dd/CuddDdMetaVariable.h" +#include "src/utility/OsDetection.h" + +// Include the C++-interface of CUDD. +#include "cuddObj.hh" + +namespace storm { + namespace dd { + template<> + class DdManager<DdType::CUDD> : public std::enable_shared_from_this<DdManager<DdType::CUDD>> { + public: + friend class Dd<DdType::CUDD>; + friend class Odd<DdType::CUDD>; + friend class DdForwardIterator<DdType::CUDD>; + + /*! + * Creates an empty manager without any meta variables. + */ + DdManager(); + + // Explictly forbid copying a DdManager, but allow moving it. + DdManager(DdManager<DdType::CUDD> const& other) = delete; + DdManager<DdType::CUDD>& operator=(DdManager<DdType::CUDD> const& other) = delete; +#ifndef WINDOWS + DdManager(DdManager<DdType::CUDD>&& other) = default; + DdManager<DdType::CUDD>& operator=(DdManager<DdType::CUDD>&& other) = default; +#endif + + /*! + * Retrieves a DD representing the constant one function. + * + * @return A DD representing the constant one function. + */ + Dd<DdType::CUDD> getOne(); + + /*! + * Retrieves a DD representing the constant zero function. + * + * @return A DD representing the constant zero function. + */ + Dd<DdType::CUDD> getZero(); + + /*! + * Retrieves a DD representing the constant function with the given value. + * + * @return A DD representing the constant function with the given value. + */ + Dd<DdType::CUDD> getConstant(double value); + + /*! + * Retrieves the DD representing the function that maps all inputs which have the given meta variable equal + * to the given value one. + * + * @param metaVariableName The meta variable that is supposed to have the given value. + * @param value The value the meta variable is supposed to have. + * @return The DD representing the function that maps all inputs which have the given meta variable equal + * to the given value one. + */ + Dd<DdType::CUDD> getEncoding(std::string const& metaVariableName, int_fast64_t value); + + /*! + * Retrieves the DD representing the range of the meta variable, i.e., a function that maps all legal values + * of the range of the meta variable to one. + * + * @param metaVariableName The name of the meta variable whose range to retrieve. + * @return The range of the meta variable. + */ + Dd<DdType::CUDD> getRange(std::string const& metaVariableName); + + /*! + * Retrieves the DD representing the identity of the meta variable, i.e., a function that maps all legal + * values of the range of the meta variable to themselves. + * + * @param metaVariableName The name of the meta variable whose identity to retrieve. + * @return The identity of the meta variable. + */ + Dd<DdType::CUDD> getIdentity(std::string const& metaVariableName); + + /*! + * Adds an integer meta variable with the given name and range. + * + * @param name The (non-empty) name of the meta variable. + * @param low The lowest value of the range of the variable. + * @param high The highest value of the range of the variable. + */ + void addMetaVariable(std::string const& name, int_fast64_t low, int_fast64_t high); + + /*! + * Adds a boolean meta variable with the given name. + * + * @param name The (non-empty) name of the meta variable. + */ + void addMetaVariable(std::string const& name); + + /*! + * Retrieves the names of all meta variables that have been added to the manager. + * + * @return The set of all meta variable names of the manager. + */ + std::set<std::string> getAllMetaVariableNames() const; + + /*! + * Retrieves the number of meta variables that are contained in this manager. + * + * @return The number of meta variables contained in this manager. + */ + std::size_t getNumberOfMetaVariables() const; + + /*! + * Retrieves whether the given meta variable name is already in use. + * + * @param metaVariableName The meta variable name whose membership to query. + * @return True if the given meta variable name is managed by this manager. + */ + bool hasMetaVariable(std::string const& metaVariableName) const; + + /*! + * Sets whether or not dynamic reordering is allowed for the DDs managed by this manager. + * + * @param value If set to true, dynamic reordering is allowed and forbidden otherwise. + */ + void allowDynamicReordering(bool value); + + /*! + * Retrieves whether dynamic reordering is currently allowed. + * + * @return True iff dynamic reordering is currently allowed. + */ + bool isDynamicReorderingAllowed() const; + + /*! + * Triggers a reordering of the DDs managed by this manager. + */ + void triggerReordering(); + + /*! + * Retrieves the meta variable with the given name if it exists. + * + * @param metaVariableName The name of the meta variable to retrieve. + * @return The meta variable with the given name. + */ + DdMetaVariable<DdType::CUDD> const& getMetaVariable(std::string const& metaVariableName) const; + + private: + /*! + * Retrieves a list of names of the DD variables in the order of their index. + * + * @return A list of DD variable names. + */ + std::vector<std::string> getDdVariableNames() const; + + /*! + * Retrieves the underlying CUDD manager. + * + * @return The underlying CUDD manager. + */ + Cudd& getCuddManager(); + + /*! + * Retrieves the underlying CUDD manager. + * + * @return The underlying CUDD manager. + */ + Cudd const& getCuddManager() const; + + // A mapping from variable names to the meta variable information. + std::unordered_map<std::string, DdMetaVariable<DdType::CUDD>> metaVariableMap; + + // The manager responsible for the DDs created/modified with this DdManager. + Cudd cuddManager; + + // The technique that is used for dynamic reordering. + Cudd_ReorderingType reorderingTechnique; + }; + } +} + +#endif /* STORM_STORAGE_DD_CUDDDDMANAGER_H_ */ \ No newline at end of file diff --git a/src/storage/dd/CuddDdMetaVariable.cpp b/src/storage/dd/CuddDdMetaVariable.cpp new file mode 100644 index 000000000..13667bd99 --- /dev/null +++ b/src/storage/dd/CuddDdMetaVariable.cpp @@ -0,0 +1,52 @@ +#include "src/storage/dd/CuddDdMetaVariable.h" +#include "src/storage/dd/CuddDdManager.h" + +namespace storm { + namespace dd { + DdMetaVariable<DdType::CUDD>::DdMetaVariable(std::string const& name, int_fast64_t low, int_fast64_t high, std::vector<Dd<DdType::CUDD>> const& ddVariables, std::shared_ptr<DdManager<DdType::CUDD>> manager) : name(name), type(MetaVariableType::Int), low(low), high(high), ddVariables(ddVariables), cube(manager->getOne()), manager(manager) { + // Create the cube of all variables of this meta variable. + for (auto const& ddVariable : this->ddVariables) { + this->cube *= ddVariable; + } + } + + DdMetaVariable<DdType::CUDD>::DdMetaVariable(std::string const& name, std::vector<Dd<DdType::CUDD>> const& ddVariables, std::shared_ptr<DdManager<DdType::CUDD>> manager) : name(name), type(MetaVariableType::Bool), low(0), high(1), ddVariables(ddVariables), cube(manager->getOne()), manager(manager) { + // Create the cube of all variables of this meta variable. + for (auto const& ddVariable : this->ddVariables) { + this->cube *= ddVariable; + } + } + + std::string const& DdMetaVariable<DdType::CUDD>::getName() const { + return this->name; + } + + DdMetaVariable<DdType::CUDD>::MetaVariableType DdMetaVariable<DdType::CUDD>::getType() const { + return this->type; + } + + int_fast64_t DdMetaVariable<DdType::CUDD>::getLow() const { + return this->low; + } + + int_fast64_t DdMetaVariable<DdType::CUDD>::getHigh() const { + return this->high; + } + + std::size_t DdMetaVariable<DdType::CUDD>::getNumberOfDdVariables() const { + return this->ddVariables.size(); + } + + std::shared_ptr<DdManager<DdType::CUDD>> DdMetaVariable<DdType::CUDD>::getDdManager() const { + return this->manager; + } + + std::vector<Dd<DdType::CUDD>> const& DdMetaVariable<DdType::CUDD>::getDdVariables() const { + return this->ddVariables; + } + + Dd<DdType::CUDD> const& DdMetaVariable<DdType::CUDD>::getCube() const { + return this->cube; + } + } +} \ No newline at end of file diff --git a/src/storage/dd/CuddDdMetaVariable.h b/src/storage/dd/CuddDdMetaVariable.h new file mode 100644 index 000000000..25375dc4d --- /dev/null +++ b/src/storage/dd/CuddDdMetaVariable.h @@ -0,0 +1,140 @@ +#ifndef STORM_STORAGE_DD_DDMETAVARIABLE_H_ +#define STORM_STORAGE_DD_DDMETAVARIABLE_H_ + +#include <memory> +#include <vector> +#include <cstdint> +#include <string> + +#include "utility/OsDetection.h" +#include "src/storage/dd/CuddDd.h" +#include "src/storage/dd/DdMetaVariable.h" +#include "src/storage/expressions/Expression.h" + +namespace storm { + namespace dd { + // Forward-declare some classes. + template<DdType Type> class DdManager; + template<DdType Type> class Odd; + + template<> + class DdMetaVariable<DdType::CUDD> { + public: + // Declare the DdManager class as friend so it can access the internals of a meta variable. + friend class DdManager<DdType::CUDD>; + friend class Dd<DdType::CUDD>; + friend class Odd<DdType::CUDD>; + friend class DdForwardIterator<DdType::CUDD>; + + // An enumeration for all legal types of meta variables. + enum class MetaVariableType { Bool, Int }; + + /*! + * Creates an integer meta variable with the given name and range bounds. + * + * @param name The name of the meta variable. + * @param low The lowest value of the range of the variable. + * @param high The highest value of the range of the variable. + * @param ddVariables The vector of variables used to encode this variable. + * @param manager A pointer to the manager that is responsible for this meta variable. + */ + DdMetaVariable(std::string const& name, int_fast64_t low, int_fast64_t high, std::vector<Dd<DdType::CUDD>> const& ddVariables, std::shared_ptr<DdManager<DdType::CUDD>> manager); + + /*! + * Creates a boolean meta variable with the given name. + * @param name The name of the meta variable. + * @param ddVariables The vector of variables used to encode this variable. + * @param manager A pointer to the manager that is responsible for this meta variable. + */ + DdMetaVariable(std::string const& name, std::vector<Dd<DdType::CUDD>> const& ddVariables, std::shared_ptr<DdManager<DdType::CUDD>> manager); + + // Explictly generate all default versions of copy/move constructors/assignments. + DdMetaVariable(DdMetaVariable const& other) = default; + DdMetaVariable& operator=(DdMetaVariable const& other) = default; +#ifndef WINDOWS + DdMetaVariable(DdMetaVariable&& other) = default; + DdMetaVariable& operator=(DdMetaVariable&& other) = default; +#endif + + /*! + * Retrieves the name of the meta variable. + * + * @return The name of the variable. + */ + std::string const& getName() const; + + /*! + * Retrieves the type of the meta variable. + * + * @return The type of the meta variable. + */ + MetaVariableType getType() const; + + /*! + * Retrieves the lowest value of the range of the variable. + * + * @return The lowest value of the range of the variable. + */ + int_fast64_t getLow() const; + + /*! + * Retrieves the highest value of the range of the variable. + * + * @return The highest value of the range of the variable. + */ + int_fast64_t getHigh() const; + + /*! + * Retrieves the manager that is responsible for this meta variable. + * + * A pointer to the manager that is responsible for this meta variable. + */ + std::shared_ptr<DdManager<DdType::CUDD>> getDdManager() const; + + /*! + * Retrieves the number of DD variables for this meta variable. + * + * @return The number of DD variables for this meta variable. + */ + std::size_t getNumberOfDdVariables() const; + + private: + /*! + * Retrieves the variables used to encode the meta variable. + * + * @return A vector of variables used to encode the meta variable. + */ + std::vector<Dd<DdType::CUDD>> const& getDdVariables() const; + + /*! + * Retrieves the cube of all variables that encode this meta variable. + * + * @return The cube of all variables that encode this meta variable. + */ + Dd<DdType::CUDD> const& getCube() const; + + // The name of the meta variable. + std::string name; + + // The type of the variable. + MetaVariableType type; + + // The lowest value of the range of the variable. + int_fast64_t low; + + // The highest value of the range of the variable. + int_fast64_t high; + + // The vector of variables that are used to encode the meta variable. + std::vector<Dd<DdType::CUDD>> ddVariables; + + // The cube consisting of all variables that encode the meta variable. + Dd<DdType::CUDD> cube; + + // A pointer to the manager responsible for this meta variable. + std::shared_ptr<DdManager<DdType::CUDD>> manager; + }; + } +} + +#endif /* STORM_STORAGE_DD_DDMETAVARIABLE_H_ */ \ No newline at end of file diff --git a/src/storage/dd/CuddOdd.cpp b/src/storage/dd/CuddOdd.cpp new file mode 100644 index 000000000..dc84d9fd7 --- /dev/null +++ b/src/storage/dd/CuddOdd.cpp @@ -0,0 +1,111 @@ +#include "src/storage/dd/CuddOdd.h" + +#include <algorithm> + +#include "src/storage/dd/CuddDdManager.h" +#include "src/storage/dd/CuddDdMetaVariable.h" + +namespace storm { + namespace dd { + Odd<DdType::CUDD>::Odd(Dd<DdType::CUDD> const& dd) { + std::shared_ptr<DdManager<DdType::CUDD>> manager = dd.getDdManager(); + + // First, we need to determine the involved DD variables indices. + std::vector<uint_fast64_t> ddVariableIndices = dd.getSortedVariableIndices(); + + // Prepare a unique table for each level that keeps the constructed ODD nodes unique. + std::vector<std::map<DdNode*, std::shared_ptr<Odd<DdType::CUDD>>>> uniqueTableForLevels(ddVariableIndices.size() + 1); + + // Now construct the ODD structure. + std::shared_ptr<Odd<DdType::CUDD>> rootOdd = buildOddRec(dd.getCuddAdd().getNode(), manager->getCuddManager(), 0, ddVariableIndices.size(), ddVariableIndices, uniqueTableForLevels); + + // Finally, move the children of the root ODD into this ODD. + this->dd = rootOdd->dd; + this->elseNode = std::move(rootOdd->elseNode); + this->thenNode = std::move(rootOdd->thenNode); + this->elseOffset = rootOdd->elseOffset; + this->thenOffset = rootOdd->thenOffset; + } + + Odd<DdType::CUDD>::Odd(ADD dd, std::shared_ptr<Odd<DdType::CUDD>>&& elseNode, uint_fast64_t elseOffset, std::shared_ptr<Odd<DdType::CUDD>>&& thenNode, uint_fast64_t thenOffset) : dd(dd), elseNode(elseNode), thenNode(thenNode), elseOffset(elseOffset), thenOffset(thenOffset) { + // Intentionally left empty. + } + + Odd<DdType::CUDD> const& Odd<DdType::CUDD>::getThenSuccessor() const { + return *this->thenNode; + } + + Odd<DdType::CUDD> const& Odd<DdType::CUDD>::getElseSuccessor() const { + return *this->elseNode; + } + + uint_fast64_t Odd<DdType::CUDD>::getElseOffset() const { + return this->elseOffset; + } + + void Odd<DdType::CUDD>::setElseOffset(uint_fast64_t newOffset) { + this->elseOffset = newOffset; + } + + uint_fast64_t Odd<DdType::CUDD>::getThenOffset() const { + return this->thenOffset; + } + + void Odd<DdType::CUDD>::setThenOffset(uint_fast64_t newOffset) { + this->thenOffset = newOffset; + } + + uint_fast64_t Odd<DdType::CUDD>::getTotalOffset() const { + return this->elseOffset + this->thenOffset; + } + + uint_fast64_t Odd<DdType::CUDD>::getNodeCount() const { + // If the ODD contains a constant (and thus has no children), the size is 1. + if (this->elseNode == nullptr && this->thenNode == nullptr) { + return 1; + } + + // If the two successors are actually the same, we need to count the subnodes only once. + if (this->elseNode == this->thenNode) { + return this->elseNode->getNodeCount(); + } else { + return this->elseNode->getNodeCount() + this->thenNode->getNodeCount(); + } + } + + std::shared_ptr<Odd<DdType::CUDD>> Odd<DdType::CUDD>::buildOddRec(DdNode* dd, Cudd const& manager, uint_fast64_t currentLevel, uint_fast64_t maxLevel, std::vector<uint_fast64_t> const& ddVariableIndices, std::vector<std::map<DdNode*, std::shared_ptr<Odd<DdType::CUDD>>>>& uniqueTableForLevels) { + // Check whether the ODD for this node has already been computed (for this level) and if so, return this instead. + auto const& iterator = uniqueTableForLevels[currentLevel].find(dd); + if (iterator != uniqueTableForLevels[currentLevel].end()) { + return iterator->second; + } else { + // Otherwise, we need to recursively compute the ODD. + + // If we are already past the maximal level that is to be considered, we can simply create a Odd without + // successors + if (currentLevel == maxLevel) { + uint_fast64_t elseOffset = 0; + uint_fast64_t thenOffset = 0; + + // If the DD is not the zero leaf, then the then-offset is 1. + if (dd != Cudd_ReadZero(manager.getManager())) { + thenOffset = 1; + } + + return std::shared_ptr<Odd<DdType::CUDD>>(new Odd<DdType::CUDD>(ADD(manager, dd), nullptr, elseOffset, nullptr, thenOffset)); + } else if (ddVariableIndices[currentLevel] < static_cast<uint_fast64_t>(dd->index)) { + // If we skipped the level in the DD, we compute the ODD just for the else-successor and use the same + // node for the then-successor as well. + std::shared_ptr<Odd<DdType::CUDD>> elseNode = buildOddRec(dd, manager, currentLevel + 1, maxLevel, ddVariableIndices, uniqueTableForLevels); + std::shared_ptr<Odd<DdType::CUDD>> thenNode = elseNode; + return std::shared_ptr<Odd<DdType::CUDD>>(new Odd<DdType::CUDD>(ADD(manager, dd), std::move(elseNode), elseNode->getElseOffset() + elseNode->getThenOffset(), std::move(thenNode), thenNode->getElseOffset() + thenNode->getThenOffset())); + } else { + // Otherwise, we compute the ODDs for both the then- and else successors. + std::shared_ptr<Odd<DdType::CUDD>> elseNode = buildOddRec(Cudd_E(dd), manager, currentLevel + 1, maxLevel, ddVariableIndices, uniqueTableForLevels); + std::shared_ptr<Odd<DdType::CUDD>> thenNode = buildOddRec(Cudd_T(dd), manager, currentLevel + 1, maxLevel, ddVariableIndices, uniqueTableForLevels); + return std::shared_ptr<Odd<DdType::CUDD>>(new Odd<DdType::CUDD>(ADD(manager, dd), std::move(elseNode), elseNode->getElseOffset() + elseNode->getThenOffset(), std::move(thenNode), thenNode->getElseOffset() + thenNode->getThenOffset())); + } + } + } + } +} \ No newline at end of file diff --git a/src/storage/dd/CuddOdd.h b/src/storage/dd/CuddOdd.h new file mode 100644 index 000000000..1d36613f0 --- /dev/null +++ b/src/storage/dd/CuddOdd.h @@ -0,0 +1,131 @@ +#ifndef STORM_STORAGE_DD_CUDDODD_H_ +#define STORM_STORAGE_DD_CUDDODD_H_ + +#include <memory> + +#include "src/storage/dd/Odd.h" +#include "src/storage/dd/CuddDd.h" +#include "src/utility/OsDetection.h" + +// Include the C++-interface of CUDD. +#include "cuddObj.hh" + +namespace storm { + namespace dd { + template<> + class Odd<DdType::CUDD> { + public: + /*! + * Constructs an offset-labeled DD from the given DD. + * + * @param dd The DD for which to build the offset-labeled DD. + */ + Odd(Dd<DdType::CUDD> const& dd); + + // Instantiate all copy/move constructors/assignments with the default implementation. + Odd() = default; + Odd(Odd<DdType::CUDD> const& other) = default; + Odd& operator=(Odd<DdType::CUDD> const& other) = default; +#ifndef WINDOWS + Odd(Odd<DdType::CUDD>&& other) = default; + Odd& operator=(Odd<DdType::CUDD>&& other) = default; +#endif + + /*! + * Retrieves the then-successor of this ODD node. + * + * @return The then-successor of this ODD node. + */ + Odd<DdType::CUDD> const& getThenSuccessor() const; + + /*! + * Retrieves the else-successor of this ODD node. + * + * @return The else-successor of this ODD node. + */ + Odd<DdType::CUDD> const& getElseSuccessor() const; + + /*! + * Retrieves the else-offset of this ODD node. + * + * @return The else-offset of this ODD node. + */ + uint_fast64_t getElseOffset() const; + + /*! + * Sets the else-offset of this ODD node. + * + * @param newOffset The new else-offset of this ODD node. + */ + void setElseOffset(uint_fast64_t newOffset); + + /*! + * Retrieves the then-offset of this ODD node. + * + * @return The then-offset of this ODD node. + */ + uint_fast64_t getThenOffset() const; + + /*! + * Sets the then-offset of this ODD node. + * + * @param newOffset The new then-offset of this ODD node. + */ + void setThenOffset(uint_fast64_t newOffset); + + /*! + * Retrieves the total offset, i.e., the sum of the then- and else-offset. + * + * @return The total offset of this ODD. + */ + uint_fast64_t getTotalOffset() const; + + /*! + * Retrieves the size of the ODD. Note: the size is computed by a traversal, so this may be costlier than + * expected. + * + * @return The size (in nodes) of this ODD. + */ + uint_fast64_t getNodeCount() const; + + private: + /*! + * Constructs an offset-labeled DD with the given topmost DD node, else- and then-successor. + * + * @param dd The DD associated with this ODD node. + * @param elseNode The else-successor of thie ODD node. + * @param elseOffset The offset of the else-successor. + * @param thenNode The then-successor of thie ODD node. + * @param thenOffset The offset of the then-successor. + */ + Odd(ADD dd, std::shared_ptr<Odd<DdType::CUDD>>&& elseNode, uint_fast64_t elseOffset, std::shared_ptr<Odd<DdType::CUDD>>&& thenNode, uint_fast64_t thenOffset); + + /*! + * Recursively builds the ODD. + * + * @param dd The DD for which to build the ODD. + * @param manager The manager responsible for the DD. + * @param currentLevel The currently considered level in the DD. + * @param maxLevel The number of levels that need to be considered. + * @param ddVariableIndices The (sorted) indices of all DD variables that need to be considered. + * @param uniqueTableForLevels A vector of unique tables, one for each level to be considered, that keeps + * ODD nodes for the same DD and level unique. + * @return A pointer to the constructed ODD for the given arguments. + */ + static std::shared_ptr<Odd<DdType::CUDD>> buildOddRec(DdNode* dd, Cudd const& manager, uint_fast64_t currentLevel, uint_fast64_t maxLevel, std::vector<uint_fast64_t> const& ddVariableIndices, std::vector<std::map<DdNode*, std::shared_ptr<Odd<DdType::CUDD>>>>& uniqueTableForLevels); + + // The DD associated with this ODD node. + ADD dd; + + // The then- and else-nodes. + std::shared_ptr<Odd<DdType::CUDD>> elseNode; + std::shared_ptr<Odd<DdType::CUDD>> thenNode; + + // The offsets that need to be added if the then- or else-successor is taken, respectively. + uint_fast64_t elseOffset; + uint_fast64_t thenOffset; + }; + } +} + +#endif /* STORM_STORAGE_DD_CUDDODD_H_ */ \ No newline at end of file diff --git a/src/storage/dd/Dd.h b/src/storage/dd/Dd.h new file mode 100644 index 000000000..bc7075f46 --- /dev/null +++ b/src/storage/dd/Dd.h @@ -0,0 +1,13 @@ +#ifndef STORM_STORAGE_DD_DD_H_ +#define STORM_STORAGE_DD_DD_H_ + +#include "src/storage/dd/DdType.h" + +namespace storm { + namespace dd { + // Declare Dd class so we can then specialize it for the different DD types. + template<DdType Type> class Dd; + } +} + +#endif /* STORM_STORAGE_DD_DD_H_ */ \ No newline at end of file diff --git a/src/storage/dd/DdForwardIterator.h b/src/storage/dd/DdForwardIterator.h new file mode 100644 index 000000000..2eed23090 --- /dev/null +++ b/src/storage/dd/DdForwardIterator.h @@ -0,0 +1,13 @@ +#ifndef STORM_STORAGE_DD_DDFORWARDITERATOR_H_ +#define STORM_STORAGE_DD_DDFORWARDITERATOR_H_ + +#include "src/storage/dd/DdType.h" + +namespace storm { + namespace dd { + // Declare DdIterator class so we can then specialize it for the different DD types. + template<DdType Type> class DdForwardIterator; + } +} + +#endif /* STORM_STORAGE_DD_DDFORWARDITERATOR_H_ */ \ No newline at end of file diff --git a/src/storage/dd/DdManager.h b/src/storage/dd/DdManager.h new file mode 100644 index 000000000..53fde64bb --- /dev/null +++ b/src/storage/dd/DdManager.h @@ -0,0 +1,13 @@ +#ifndef STORM_STORAGE_DD_DDMANAGER_H_ +#define STORM_STORAGE_DD_DDMANAGER_H_ + +#include "src/storage/dd/DdType.h" + +namespace storm { + namespace dd { + // Declare DdManager class so we can then specialize it for the different DD types. + template<DdType Type> class DdManager; + } +} + +#endif /* STORM_STORAGE_DD_DDMANAGER_H_ */ \ No newline at end of file diff --git a/src/storage/dd/DdMetaVariable.h b/src/storage/dd/DdMetaVariable.h new file mode 100644 index 000000000..b85b23c1c --- /dev/null +++ b/src/storage/dd/DdMetaVariable.h @@ -0,0 +1,13 @@ +#ifndef STORM_STORAGE_DD_DDMETAVARIBLE_H_ +#define STORM_STORAGE_DD_DDMETAVARIBLE_H_ + +#include "src/storage/dd/DdType.h" + +namespace storm { + namespace dd { + // Declare DdMetaVariable class so we can then specialize it for the different DD types. + template<DdType Type> class DdMetaVariable; + } +} + +#endif /* STORM_STORAGE_DD_DDMETAVARIBLE_H_ */ \ No newline at end of file diff --git a/src/storage/dd/DdType.h b/src/storage/dd/DdType.h new file mode 100644 index 000000000..feb2fe70a --- /dev/null +++ b/src/storage/dd/DdType.h @@ -0,0 +1,12 @@ +#ifndef STORM_STORAGE_DD_DDTYPE_H_ +#define STORM_STORAGE_DD_DDTYPE_H_ + +namespace storm { + namespace dd { + enum class DdType { + CUDD + }; + } +} + +#endif /* STORM_STORAGE_DD_DDTYPE_H_ */ diff --git a/src/storage/dd/Odd.h b/src/storage/dd/Odd.h new file mode 100644 index 000000000..6a7231300 --- /dev/null +++ b/src/storage/dd/Odd.h @@ -0,0 +1,13 @@ +#ifndef STORM_STORAGE_DD_ODD_H_ +#define STORM_STORAGE_DD_ODD_H_ + +#include "src/storage/dd/DdType.h" + +namespace storm { + namespace dd { + // Declare Odd class so we can then specialize it for the different DD types. + template<DdType Type> class Odd; + } +} + +#endif /* STORM_STORAGE_DD_ODD_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/BaseExpression.cpp b/src/storage/expressions/BaseExpression.cpp new file mode 100644 index 000000000..38a0604e2 --- /dev/null +++ b/src/storage/expressions/BaseExpression.cpp @@ -0,0 +1,89 @@ +#include "src/storage/expressions/BaseExpression.h" +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidTypeException.h" +#include "src/exceptions/InvalidAccessException.h" + +namespace storm { + namespace expressions { + BaseExpression::BaseExpression(ExpressionReturnType returnType) : returnType(returnType) { + // Intentionally left empty. + } + + ExpressionReturnType BaseExpression::getReturnType() const { + return this->returnType; + } + + bool BaseExpression::hasIntegralReturnType() const { + return this->getReturnType() == ExpressionReturnType::Int; + } + + bool BaseExpression::hasNumericalReturnType() const { + return this->getReturnType() == ExpressionReturnType::Double || this->getReturnType() == ExpressionReturnType::Int; + } + + bool BaseExpression::hasBooleanReturnType() const { + return this->getReturnType() == ExpressionReturnType::Bool; + } + + int_fast64_t BaseExpression::evaluateAsInt(Valuation const* valuation) const { + LOG_THROW(false, storm::exceptions::InvalidTypeException, "Unable to evaluate expression as integer."); + } + + bool BaseExpression::evaluateAsBool(Valuation const* valuation) const { + LOG_THROW(false, storm::exceptions::InvalidTypeException, "Unable to evaluate expression as boolean."); + } + + double BaseExpression::evaluateAsDouble(Valuation const* valuation) const { + LOG_THROW(false, storm::exceptions::InvalidTypeException, "Unable to evaluate expression as double."); + } + + uint_fast64_t BaseExpression::getArity() const { + return 0; + } + + std::shared_ptr<BaseExpression const> BaseExpression::getOperand(uint_fast64_t operandIndex) const { + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to access operand " << operandIndex << " in expression of arity 0."); + } + + std::string const& BaseExpression::getIdentifier() const { + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to access identifier of non-constant, non-variable expression."); + } + + OperatorType BaseExpression::getOperator() const { + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to access operator of non-function application expression."); + } + + bool BaseExpression::containsVariables() const { + return false; + } + + bool BaseExpression::isLiteral() const { + return false; + } + + bool BaseExpression::isVariable() const { + return false; + } + + bool BaseExpression::isTrue() const { + return false; + } + + bool BaseExpression::isFalse() const { + return false; + } + + bool BaseExpression::isFunctionApplication() const { + return false; + } + + std::shared_ptr<BaseExpression const> BaseExpression::getSharedPointer() const { + return this->shared_from_this(); + } + + std::ostream& operator<<(std::ostream& stream, BaseExpression const& expression) { + expression.printToStream(stream); + return stream; + } + } +} \ No newline at end of file diff --git a/src/storage/expressions/BaseExpression.h b/src/storage/expressions/BaseExpression.h new file mode 100644 index 000000000..9524fcdbb --- /dev/null +++ b/src/storage/expressions/BaseExpression.h @@ -0,0 +1,216 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_BASEEXPRESSION_H_ +#define STORM_STORAGE_EXPRESSIONS_BASEEXPRESSION_H_ + +#include <cstdint> +#include <memory> +#include <set> +#include <iostream> + +#include "src/storage/expressions/ExpressionReturnType.h" +#include "src/storage/expressions/Valuation.h" +#include "src/storage/expressions/ExpressionVisitor.h" +#include "src/storage/expressions/OperatorType.h" +#include "src/exceptions/InvalidArgumentException.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace expressions { + /*! + * The base class of all expression classes. + */ + class BaseExpression : public std::enable_shared_from_this<BaseExpression> { + public: + /*! + * Constructs a base expression with the given return type. + * + * @param returnType The return type of the expression. + */ + BaseExpression(ExpressionReturnType returnType); + + // Create default versions of constructors and assignments. + BaseExpression(BaseExpression const&) = default; + BaseExpression& operator=(BaseExpression const&) = default; +#ifndef WINDOWS + BaseExpression(BaseExpression&&) = default; + BaseExpression& operator=(BaseExpression&&) = default; +#endif + + // Make the destructor virtual (to allow destruction via base class pointer) and default it. + virtual ~BaseExpression() = default; + + /*! + * Evaluates the expression under the valuation of unknowns (variables and constants) given by the + * valuation and returns the resulting boolean value. If the return type of the expression is not a boolean + * an exception is thrown. + * + * @param valuation The valuation of unknowns under which to evaluate the expression. + * @return The boolean value of the expression under the given valuation. + */ + virtual bool evaluateAsBool(Valuation const* valuation = nullptr) const; + + /*! + * Evaluates the expression under the valuation of unknowns (variables and constants) given by the + * valuation and returns the resulting integer value. If the return type of the expression is not an integer + * an exception is thrown. + * + * @param valuation The valuation of unknowns under which to evaluate the expression. + * @return The integer value of the expression under the given valuation. + */ + virtual int_fast64_t evaluateAsInt(Valuation const* valuation = nullptr) const; + + /*! + * Evaluates the expression under the valuation of unknowns (variables and constants) given by the + * valuation and returns the resulting double value. If the return type of the expression is not a double + * an exception is thrown. + * + * @param valuation The valuation of unknowns under which to evaluate the expression. + * @return The double value of the expression under the given valuation. + */ + virtual double evaluateAsDouble(Valuation const* valuation = nullptr) const; + + /*! + * Returns the arity of the expression. + * + * @return The arity of the expression. + */ + virtual uint_fast64_t getArity() const; + + /*! + * Retrieves the given operand from the expression. + * + * @param operandIndex The index of the operand to retrieve. This must be lower than the arity of the expression. + * @return The operand at the given index. + */ + virtual std::shared_ptr<BaseExpression const> getOperand(uint_fast64_t operandIndex) const; + + /*! + * Retrieves the identifier associated with this expression. This is only legal to call if the expression + * is a variable. + * + * @return The identifier associated with this expression. + */ + virtual std::string const& getIdentifier() const; + + /*! + * Retrieves the operator of a function application. This is only legal to call if the expression is + * function application. + * + * @return The operator associated with the function application. + */ + virtual OperatorType getOperator() const; + + /*! + * Retrieves whether the expression contains a variable. + * + * @return True iff the expression contains a variable. + */ + virtual bool containsVariables() const; + + /*! + * Retrieves whether the expression is a literal. + * + * @return True iff the expression is a literal. + */ + virtual bool isLiteral() const; + + /*! + * Retrieves whether the expression is a variable. + * + * @return True iff the expression is a variable. + */ + virtual bool isVariable() const; + + /*! + * Checks if the expression is equal to the boolean literal true. + * + * @return True iff the expression is equal to the boolean literal true. + */ + virtual bool isTrue() const; + + /*! + * Checks if the expression is equal to the boolean literal false. + * + * @return True iff the expression is equal to the boolean literal false. + */ + virtual bool isFalse() const; + + /*! + * Checks if the expression is a function application (of any sort). + * + * @return True iff the expression is a function application. + */ + virtual bool isFunctionApplication() const; + + /*! + * Retrieves the set of all variables that appear in the expression. + * + * @return The set of all variables that appear in the expression. + */ + virtual std::set<std::string> getVariables() const = 0; + + /*! + * Simplifies the expression according to some simple rules. + * + * @return A pointer to the simplified expression. + */ + virtual std::shared_ptr<BaseExpression const> simplify() const = 0; + + /*! + * Accepts the given visitor by calling its visit method. + * + * @param visitor The visitor that is to be accepted. + */ + virtual void accept(ExpressionVisitor* visitor) const = 0; + + /*! + * Retrieves whether the expression has a numerical return type, i.e., integer or double. + * + * @return True iff the expression has a numerical return type. + */ + bool hasNumericalReturnType() const; + + /*! + * Retrieves whether the expression has an integral return type, i.e., integer. + * + * @return True iff the expression has an integral return type. + */ + bool hasIntegralReturnType() const; + + /*! + * Retrieves whether the expression has a boolean return type. + * + * @return True iff the expression has a boolean return type. + */ + bool hasBooleanReturnType() const; + + /*! + * Retrieves a shared pointer to this expression. + * + * @return A shared pointer to this expression. + */ + std::shared_ptr<BaseExpression const> getSharedPointer() const; + + /*! + * Retrieves the return type of the expression. + * + * @return The return type of the expression. + */ + ExpressionReturnType getReturnType() const; + + friend std::ostream& operator<<(std::ostream& stream, BaseExpression const& expression); + protected: + /*! + * Prints the expression to the given stream. + * + * @param stream The stream to which to write the expression. + */ + virtual void printToStream(std::ostream& stream) const = 0; + + private: + // The return type of this expression. + ExpressionReturnType returnType; + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_BASEEXPRESSION_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/BinaryBooleanFunctionExpression.cpp b/src/storage/expressions/BinaryBooleanFunctionExpression.cpp new file mode 100644 index 000000000..061c231af --- /dev/null +++ b/src/storage/expressions/BinaryBooleanFunctionExpression.cpp @@ -0,0 +1,110 @@ +#include "src/storage/expressions/BinaryBooleanFunctionExpression.h" +#include "src/storage/expressions/BooleanLiteralExpression.h" +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidTypeException.h" + +namespace storm { + namespace expressions { + BinaryBooleanFunctionExpression::BinaryBooleanFunctionExpression(ExpressionReturnType returnType, std::shared_ptr<BaseExpression const> const& firstOperand, std::shared_ptr<BaseExpression const> const& secondOperand, OperatorType operatorType) : BinaryExpression(returnType, firstOperand, secondOperand), operatorType(operatorType) { + // Intentionally left empty. + } + + BinaryBooleanFunctionExpression::OperatorType BinaryBooleanFunctionExpression::getOperatorType() const { + return this->operatorType; + } + + storm::expressions::OperatorType BinaryBooleanFunctionExpression::getOperator() const { + switch (this->getOperatorType()) { + case OperatorType::And: return storm::expressions::OperatorType::And; break; + case OperatorType::Or: return storm::expressions::OperatorType::Or; break; + case OperatorType::Xor: return storm::expressions::OperatorType::Xor; break; + case OperatorType::Implies: return storm::expressions::OperatorType::Implies; break; + case OperatorType::Iff: return storm::expressions::OperatorType::Iff; break; + } + } + + bool BinaryBooleanFunctionExpression::evaluateAsBool(Valuation const* valuation) const { + LOG_THROW(this->hasBooleanReturnType(), storm::exceptions::InvalidTypeException, "Unable to evaluate expression as boolean."); + + bool firstOperandEvaluation = this->getFirstOperand()->evaluateAsBool(valuation); + bool secondOperandEvaluation = this->getSecondOperand()->evaluateAsBool(valuation); + + bool result; + switch (this->getOperatorType()) { + case OperatorType::And: result = firstOperandEvaluation && secondOperandEvaluation; break; + case OperatorType::Or: result = firstOperandEvaluation || secondOperandEvaluation; break; + case OperatorType::Xor: result = firstOperandEvaluation ^ secondOperandEvaluation; break; + case OperatorType::Implies: result = !firstOperandEvaluation || secondOperandEvaluation; break; + case OperatorType::Iff: result = (firstOperandEvaluation && secondOperandEvaluation) || (!firstOperandEvaluation && !secondOperandEvaluation); break; + } + + return result; + } + + std::shared_ptr<BaseExpression const> BinaryBooleanFunctionExpression::simplify() const { + std::shared_ptr<BaseExpression const> firstOperandSimplified = this->getFirstOperand()->simplify(); + std::shared_ptr<BaseExpression const> secondOperandSimplified = this->getSecondOperand()->simplify(); + + switch (this->getOperatorType()) { + case OperatorType::And: if (firstOperandSimplified->isTrue()) { + return secondOperandSimplified; + } else if (firstOperandSimplified->isFalse()) { + return firstOperandSimplified; + } else if (secondOperandSimplified->isTrue()) { + return firstOperandSimplified; + } else if (secondOperandSimplified->isFalse()) { + return secondOperandSimplified; + } + break; + case OperatorType::Or: if (firstOperandSimplified->isTrue()) { + return firstOperandSimplified; + } else if (firstOperandSimplified->isFalse()) { + return secondOperandSimplified; + } else if (secondOperandSimplified->isTrue()) { + return secondOperandSimplified; + } else if (secondOperandSimplified->isFalse()) { + return firstOperandSimplified; + } + break; + case OperatorType::Xor: break; + case OperatorType::Implies: if (firstOperandSimplified->isTrue()) { + return secondOperandSimplified; + } else if (firstOperandSimplified->isFalse()) { + return std::shared_ptr<BaseExpression>(new BooleanLiteralExpression(true)); + } else if (secondOperandSimplified->isTrue()) { + return std::shared_ptr<BaseExpression>(new BooleanLiteralExpression(true)); + } + break; + case OperatorType::Iff: if (firstOperandSimplified->isTrue() && secondOperandSimplified->isTrue()) { + return std::shared_ptr<BaseExpression>(new BooleanLiteralExpression(true)); + } else if (firstOperandSimplified->isFalse() && secondOperandSimplified->isFalse()) { + return std::shared_ptr<BaseExpression>(new BooleanLiteralExpression(true)); + } + break; + } + + // If the two successors remain unchanged, we can return a shared_ptr to this very object. + if (firstOperandSimplified.get() == this->getFirstOperand().get() && secondOperandSimplified.get() == this->getSecondOperand().get()) { + return this->shared_from_this(); + } else { + return std::shared_ptr<BaseExpression>(new BinaryBooleanFunctionExpression(this->getReturnType(), firstOperandSimplified, secondOperandSimplified, this->getOperatorType())); + } + } + + void BinaryBooleanFunctionExpression::accept(ExpressionVisitor* visitor) const { + visitor->visit(this); + } + + void BinaryBooleanFunctionExpression::printToStream(std::ostream& stream) const { + stream << "(" << *this->getFirstOperand(); + switch (this->getOperatorType()) { + case OperatorType::And: stream << " & "; break; + case OperatorType::Or: stream << " | "; break; + case OperatorType::Xor: stream << " != "; break; + case OperatorType::Implies: stream << " => "; break; + case OperatorType::Iff: stream << " = "; break; + } + stream << *this->getSecondOperand() << ")"; + } + } +} \ No newline at end of file diff --git a/src/storage/expressions/BinaryBooleanFunctionExpression.h b/src/storage/expressions/BinaryBooleanFunctionExpression.h new file mode 100644 index 000000000..2a1d2417d --- /dev/null +++ b/src/storage/expressions/BinaryBooleanFunctionExpression.h @@ -0,0 +1,59 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_BINARYBOOLEANFUNCTIONEXPRESSION_H_ +#define STORM_STORAGE_EXPRESSIONS_BINARYBOOLEANFUNCTIONEXPRESSION_H_ + +#include "src/storage/expressions/BinaryExpression.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace expressions { + class BinaryBooleanFunctionExpression : public BinaryExpression { + public: + /*! + * An enum type specifying the different operators applicable. + */ + enum class OperatorType {And, Or, Xor, Implies, Iff}; + + /*! + * Creates a binary boolean function expression with the given return type, operands and operator. + * + * @param returnType The return type of the expression. + * @param firstOperand The first operand of the expression. + * @param secondOperand The second operand of the expression. + * @param functionType The operator of the expression. + */ + BinaryBooleanFunctionExpression(ExpressionReturnType returnType, std::shared_ptr<BaseExpression const> const& firstOperand, std::shared_ptr<BaseExpression const> const& secondOperand, OperatorType operatorType); + + // Instantiate constructors and assignments with their default implementations. + BinaryBooleanFunctionExpression(BinaryBooleanFunctionExpression const& other) = default; + BinaryBooleanFunctionExpression& operator=(BinaryBooleanFunctionExpression const& other) = default; +#ifndef WINDOWS + BinaryBooleanFunctionExpression(BinaryBooleanFunctionExpression&&) = default; + BinaryBooleanFunctionExpression& operator=(BinaryBooleanFunctionExpression&&) = default; +#endif + virtual ~BinaryBooleanFunctionExpression() = default; + + // Override base class methods. + virtual storm::expressions::OperatorType getOperator() const override; + virtual bool evaluateAsBool(Valuation const* valuation = nullptr) const override; + virtual std::shared_ptr<BaseExpression const> simplify() const override; + virtual void accept(ExpressionVisitor* visitor) const override; + + /*! + * Retrieves the operator associated with the expression. + * + * @return The operator associated with the expression. + */ + OperatorType getOperatorType() const; + + protected: + // Override base class method. + virtual void printToStream(std::ostream& stream) const override; + + private: + // The operator of the expression. + OperatorType operatorType; + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_BINARYBOOLEANFUNCTIONEXPRESSION_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/BinaryExpression.cpp b/src/storage/expressions/BinaryExpression.cpp new file mode 100644 index 000000000..4d3c4be81 --- /dev/null +++ b/src/storage/expressions/BinaryExpression.cpp @@ -0,0 +1,48 @@ +#include "src/storage/expressions/BinaryExpression.h" + +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidAccessException.h" + +namespace storm { + namespace expressions { + BinaryExpression::BinaryExpression(ExpressionReturnType returnType, std::shared_ptr<BaseExpression const> const& firstOperand, std::shared_ptr<BaseExpression const> const& secondOperand) : BaseExpression(returnType), firstOperand(firstOperand), secondOperand(secondOperand) { + // Intentionally left empty. + } + + bool BinaryExpression::isFunctionApplication() const { + return true; + } + + bool BinaryExpression::containsVariables() const { + return this->getFirstOperand()->containsVariables() || this->getSecondOperand()->containsVariables(); + } + + std::set<std::string> BinaryExpression::getVariables() const { + std::set<std::string> firstVariableSet = this->getFirstOperand()->getVariables(); + std::set<std::string> secondVariableSet = this->getSecondOperand()->getVariables(); + firstVariableSet.insert(secondVariableSet.begin(), secondVariableSet.end()); + return firstVariableSet; + } + + std::shared_ptr<BaseExpression const> const& BinaryExpression::getFirstOperand() const { + return this->firstOperand; + } + + std::shared_ptr<BaseExpression const> const& BinaryExpression::getSecondOperand() const { + return this->secondOperand; + } + + uint_fast64_t BinaryExpression::getArity() const { + return 2; + } + + std::shared_ptr<BaseExpression const> BinaryExpression::getOperand(uint_fast64_t operandIndex) const { + LOG_THROW(operandIndex < 2, storm::exceptions::InvalidAccessException, "Unable to access operand " << operandIndex << " in expression of arity 2."); + if (operandIndex == 0) { + return this->getFirstOperand(); + } else { + return this->getSecondOperand(); + } + } + } +} \ No newline at end of file diff --git a/src/storage/expressions/BinaryExpression.h b/src/storage/expressions/BinaryExpression.h new file mode 100644 index 000000000..75c9283cb --- /dev/null +++ b/src/storage/expressions/BinaryExpression.h @@ -0,0 +1,63 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_BINARYEXPRESSION_H_ +#define STORM_STORAGE_EXPRESSIONS_BINARYEXPRESSION_H_ + +#include "src/storage/expressions/BaseExpression.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace expressions { + /*! + * The base class of all binary expressions. + */ + class BinaryExpression : public BaseExpression { + public: + /*! + * Constructs a binary expression with the given return type and operands. + * + * @param returnType The return type of the expression. + * @param firstOperand The first operand of the expression. + * @param secondOperand The second operand of the expression. + */ + BinaryExpression(ExpressionReturnType returnType, std::shared_ptr<BaseExpression const> const& firstOperand, std::shared_ptr<BaseExpression const> const& secondOperand); + + // Instantiate constructors and assignments with their default implementations. + BinaryExpression(BinaryExpression const& other) = default; + BinaryExpression& operator=(BinaryExpression const& other) = default; +#ifndef WINDOWS + BinaryExpression(BinaryExpression&&) = default; + BinaryExpression& operator=(BinaryExpression&&) = default; +#endif + virtual ~BinaryExpression() = default; + + // Override base class methods. + virtual bool isFunctionApplication() const override; + virtual bool containsVariables() const override; + virtual uint_fast64_t getArity() const override; + virtual std::shared_ptr<BaseExpression const> getOperand(uint_fast64_t operandIndex) const override; + virtual std::set<std::string> getVariables() const override; + + /*! + * Retrieves the first operand of the expression. + * + * @return The first operand of the expression. + */ + std::shared_ptr<BaseExpression const> const& getFirstOperand() const; + + /*! + * Retrieves the second operand of the expression. + * + * @return The second operand of the expression. + */ + std::shared_ptr<BaseExpression const> const& getSecondOperand() const; + + private: + // The first operand of the expression. + std::shared_ptr<BaseExpression const> firstOperand; + + // The second operand of the expression. + std::shared_ptr<BaseExpression const> secondOperand; + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_BINARYEXPRESSION_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/BinaryNumericalFunctionExpression.cpp b/src/storage/expressions/BinaryNumericalFunctionExpression.cpp new file mode 100644 index 000000000..7db4fa0cf --- /dev/null +++ b/src/storage/expressions/BinaryNumericalFunctionExpression.cpp @@ -0,0 +1,91 @@ +#include <algorithm> +#include <cmath> + +#include "src/storage/expressions/BinaryNumericalFunctionExpression.h" +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidTypeException.h" + +namespace storm { + namespace expressions { + BinaryNumericalFunctionExpression::BinaryNumericalFunctionExpression(ExpressionReturnType returnType, std::shared_ptr<BaseExpression const> const& firstOperand, std::shared_ptr<BaseExpression const> const& secondOperand, OperatorType operatorType) : BinaryExpression(returnType, firstOperand, secondOperand), operatorType(operatorType) { + // Intentionally left empty. + } + + BinaryNumericalFunctionExpression::OperatorType BinaryNumericalFunctionExpression::getOperatorType() const { + return this->operatorType; + } + + storm::expressions::OperatorType BinaryNumericalFunctionExpression::getOperator() const { + switch (this->getOperatorType()) { + case OperatorType::Plus: return storm::expressions::OperatorType::Plus; break; + case OperatorType::Minus: return storm::expressions::OperatorType::Minus; break; + case OperatorType::Times: return storm::expressions::OperatorType::Times; break; + case OperatorType::Divide: return storm::expressions::OperatorType::Divide; break; + case OperatorType::Min: return storm::expressions::OperatorType::Min; break; + case OperatorType::Max: return storm::expressions::OperatorType::Max; break; + case OperatorType::Power: return storm::expressions::OperatorType::Power; break; + } + } + + int_fast64_t BinaryNumericalFunctionExpression::evaluateAsInt(Valuation const* valuation) const { + LOG_THROW(this->hasIntegralReturnType(), storm::exceptions::InvalidTypeException, "Unable to evaluate expression as integer."); + + int_fast64_t firstOperandEvaluation = this->getFirstOperand()->evaluateAsInt(valuation); + int_fast64_t secondOperandEvaluation = this->getSecondOperand()->evaluateAsInt(valuation); + switch (this->getOperatorType()) { + case OperatorType::Plus: return firstOperandEvaluation + secondOperandEvaluation; break; + case OperatorType::Minus: return firstOperandEvaluation - secondOperandEvaluation; break; + case OperatorType::Times: return firstOperandEvaluation * secondOperandEvaluation; break; + case OperatorType::Divide: return firstOperandEvaluation / secondOperandEvaluation; break; + case OperatorType::Min: return std::min(firstOperandEvaluation, secondOperandEvaluation); break; + case OperatorType::Max: return std::max(firstOperandEvaluation, secondOperandEvaluation); break; + case OperatorType::Power: return static_cast<int_fast64_t>(std::pow(firstOperandEvaluation, secondOperandEvaluation)); break; + } + } + + double BinaryNumericalFunctionExpression::evaluateAsDouble(Valuation const* valuation) const { + LOG_THROW(this->hasNumericalReturnType(), storm::exceptions::InvalidTypeException, "Unable to evaluate expression as double."); + + double firstOperandEvaluation = this->getFirstOperand()->evaluateAsDouble(valuation); + double secondOperandEvaluation = this->getSecondOperand()->evaluateAsDouble(valuation); + switch (this->getOperatorType()) { + case OperatorType::Plus: return static_cast<double>(firstOperandEvaluation + secondOperandEvaluation); break; + case OperatorType::Minus: return static_cast<double>(firstOperandEvaluation - secondOperandEvaluation); break; + case OperatorType::Times: return static_cast<double>(firstOperandEvaluation * secondOperandEvaluation); break; + case OperatorType::Divide: return static_cast<double>(firstOperandEvaluation / secondOperandEvaluation); break; + case OperatorType::Min: return static_cast<double>(std::min(firstOperandEvaluation, secondOperandEvaluation)); break; + case OperatorType::Max: return static_cast<double>(std::max(firstOperandEvaluation, secondOperandEvaluation)); break; + case OperatorType::Power: return std::pow(firstOperandEvaluation, secondOperandEvaluation); break; + } + } + + std::shared_ptr<BaseExpression const> BinaryNumericalFunctionExpression::simplify() const { + std::shared_ptr<BaseExpression const> firstOperandSimplified = this->getFirstOperand()->simplify(); + std::shared_ptr<BaseExpression const> secondOperandSimplified = this->getSecondOperand()->simplify(); + + if (firstOperandSimplified.get() == this->getFirstOperand().get() && secondOperandSimplified.get() == this->getSecondOperand().get()) { + return this->shared_from_this(); + } else { + return std::shared_ptr<BaseExpression>(new BinaryNumericalFunctionExpression(this->getReturnType(), firstOperandSimplified, secondOperandSimplified, this->getOperatorType())); + } + } + + void BinaryNumericalFunctionExpression::accept(ExpressionVisitor* visitor) const { + visitor->visit(this); + } + + void BinaryNumericalFunctionExpression::printToStream(std::ostream& stream) const { + stream << "("; + switch (this->getOperatorType()) { + case OperatorType::Plus: stream << *this->getFirstOperand() << " + " << *this->getSecondOperand(); break; + case OperatorType::Minus: stream << *this->getFirstOperand() << " - " << *this->getSecondOperand(); break; + case OperatorType::Times: stream << *this->getFirstOperand() << " * " << *this->getSecondOperand(); break; + case OperatorType::Divide: stream << *this->getFirstOperand() << " / " << *this->getSecondOperand(); break; + case OperatorType::Min: stream << "min(" << *this->getFirstOperand() << ", " << *this->getSecondOperand() << ")"; break; + case OperatorType::Max: stream << "max(" << *this->getFirstOperand() << ", " << *this->getSecondOperand() << ")"; break; + case OperatorType::Power: stream << *this->getFirstOperand() << " ^ " << *this->getSecondOperand(); break; + } + stream << ")"; + } + } +} \ No newline at end of file diff --git a/src/storage/expressions/BinaryNumericalFunctionExpression.h b/src/storage/expressions/BinaryNumericalFunctionExpression.h new file mode 100644 index 000000000..77b8021a4 --- /dev/null +++ b/src/storage/expressions/BinaryNumericalFunctionExpression.h @@ -0,0 +1,60 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_BINARYNUMERICALFUNCTIONEXPRESSION_H_ +#define STORM_STORAGE_EXPRESSIONS_BINARYNUMERICALFUNCTIONEXPRESSION_H_ + +#include "src/storage/expressions/BinaryExpression.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace expressions { + class BinaryNumericalFunctionExpression : public BinaryExpression { + public: + /*! + * An enum type specifying the different operators applicable. + */ + enum class OperatorType {Plus, Minus, Times, Divide, Min, Max, Power}; + + /*! + * Constructs a binary numerical function expression with the given return type, operands and operator. + * + * @param returnType The return type of the expression. + * @param firstOperand The first operand of the expression. + * @param secondOperand The second operand of the expression. + * @param functionType The operator of the expression. + */ + BinaryNumericalFunctionExpression(ExpressionReturnType returnType, std::shared_ptr<BaseExpression const> const& firstOperand, std::shared_ptr<BaseExpression const> const& secondOperand, OperatorType operatorType); + + // Instantiate constructors and assignments with their default implementations. + BinaryNumericalFunctionExpression(BinaryNumericalFunctionExpression const& other) = default; + BinaryNumericalFunctionExpression& operator=(BinaryNumericalFunctionExpression const& other) = default; +#ifndef WINDOWS + BinaryNumericalFunctionExpression(BinaryNumericalFunctionExpression&&) = default; + BinaryNumericalFunctionExpression& operator=(BinaryNumericalFunctionExpression&&) = default; +#endif + virtual ~BinaryNumericalFunctionExpression() = default; + + // Override base class methods. + virtual storm::expressions::OperatorType getOperator() const override; + virtual int_fast64_t evaluateAsInt(Valuation const* valuation = nullptr) const override; + virtual double evaluateAsDouble(Valuation const* valuation = nullptr) const override; + virtual std::shared_ptr<BaseExpression const> simplify() const override; + virtual void accept(ExpressionVisitor* visitor) const override; + + /*! + * Retrieves the operator associated with the expression. + * + * @return The operator associated with the expression. + */ + OperatorType getOperatorType() const; + + protected: + // Override base class method. + virtual void printToStream(std::ostream& stream) const override; + + private: + // The operator of the expression. + OperatorType operatorType; + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_BINARYNUMERICALFUNCTIONEXPRESSION_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/BinaryRelationExpression.cpp b/src/storage/expressions/BinaryRelationExpression.cpp new file mode 100644 index 000000000..f31f97fce --- /dev/null +++ b/src/storage/expressions/BinaryRelationExpression.cpp @@ -0,0 +1,70 @@ +#include "src/storage/expressions/BinaryRelationExpression.h" + +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidTypeException.h" + +namespace storm { + namespace expressions { + BinaryRelationExpression::BinaryRelationExpression(ExpressionReturnType returnType, std::shared_ptr<BaseExpression const> const& firstOperand, std::shared_ptr<BaseExpression const> const& secondOperand, RelationType relationType) : BinaryExpression(returnType, firstOperand, secondOperand), relationType(relationType) { + // Intentionally left empty. + } + + storm::expressions::OperatorType BinaryRelationExpression::getOperator() const { + switch (this->getRelationType()) { + case RelationType::Equal: return storm::expressions::OperatorType::Equal; break; + case RelationType::NotEqual: return storm::expressions::OperatorType::NotEqual; break; + case RelationType::Less: return storm::expressions::OperatorType::Less; break; + case RelationType::LessOrEqual: return storm::expressions::OperatorType::LessOrEqual; break; + case RelationType::Greater: return storm::expressions::OperatorType::Greater; break; + case RelationType::GreaterOrEqual: return storm::expressions::OperatorType::GreaterOrEqual; break; + } + } + + bool BinaryRelationExpression::evaluateAsBool(Valuation const* valuation) const { + LOG_THROW(this->hasBooleanReturnType(), storm::exceptions::InvalidTypeException, "Unable to evaluate expression as boolean."); + + double firstOperandEvaluated = this->getFirstOperand()->evaluateAsDouble(valuation); + double secondOperandEvaluated = this->getSecondOperand()->evaluateAsDouble(valuation); + switch (this->getRelationType()) { + case RelationType::Equal: return firstOperandEvaluated == secondOperandEvaluated; break; + case RelationType::NotEqual: return firstOperandEvaluated != secondOperandEvaluated; break; + case RelationType::Greater: return firstOperandEvaluated > secondOperandEvaluated; break; + case RelationType::GreaterOrEqual: return firstOperandEvaluated >= secondOperandEvaluated; break; + case RelationType::Less: return firstOperandEvaluated < secondOperandEvaluated; break; + case RelationType::LessOrEqual: return firstOperandEvaluated <= secondOperandEvaluated; break; + } + } + + std::shared_ptr<BaseExpression const> BinaryRelationExpression::simplify() const { + std::shared_ptr<BaseExpression const> firstOperandSimplified = this->getFirstOperand()->simplify(); + std::shared_ptr<BaseExpression const> secondOperandSimplified = this->getSecondOperand()->simplify(); + + if (firstOperandSimplified.get() == this->getFirstOperand().get() && secondOperandSimplified.get() == this->getSecondOperand().get()) { + return this->shared_from_this(); + } else { + return std::shared_ptr<BaseExpression>(new BinaryRelationExpression(this->getReturnType(), firstOperandSimplified, secondOperandSimplified, this->getRelationType())); + } + } + + void BinaryRelationExpression::accept(ExpressionVisitor* visitor) const { + visitor->visit(this); + } + + BinaryRelationExpression::RelationType BinaryRelationExpression::getRelationType() const { + return this->relationType; + } + + void BinaryRelationExpression::printToStream(std::ostream& stream) const { + stream << "(" << *this->getFirstOperand(); + switch (this->getRelationType()) { + case RelationType::Equal: stream << " = "; break; + case RelationType::NotEqual: stream << " != "; break; + case RelationType::Greater: stream << " > "; break; + case RelationType::GreaterOrEqual: stream << " >= "; break; + case RelationType::Less: stream << " < "; break; + case RelationType::LessOrEqual: stream << " <= "; break; + } + stream << *this->getSecondOperand() << ")"; + } + } +} \ No newline at end of file diff --git a/src/storage/expressions/BinaryRelationExpression.h b/src/storage/expressions/BinaryRelationExpression.h new file mode 100644 index 000000000..4e03a598e --- /dev/null +++ b/src/storage/expressions/BinaryRelationExpression.h @@ -0,0 +1,59 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_BINARYRELATIONEXPRESSION_H_ +#define STORM_STORAGE_EXPRESSIONS_BINARYRELATIONEXPRESSION_H_ + +#include "src/storage/expressions/BinaryExpression.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace expressions { + class BinaryRelationExpression : public BinaryExpression { + public: + /*! + * An enum type specifying the different relations applicable. + */ + enum class RelationType {Equal, NotEqual, Less, LessOrEqual, Greater, GreaterOrEqual}; + + /*! + * Creates a binary relation expression with the given return type, operands and relation type. + * + * @param returnType The return type of the expression. + * @param firstOperand The first operand of the expression. + * @param secondOperand The second operand of the expression. + * @param relationType The operator of the expression. + */ + BinaryRelationExpression(ExpressionReturnType returnType, std::shared_ptr<BaseExpression const> const& firstOperand, std::shared_ptr<BaseExpression const> const& secondOperand, RelationType relationType); + + // Instantiate constructors and assignments with their default implementations. + BinaryRelationExpression(BinaryRelationExpression const& other) = default; + BinaryRelationExpression& operator=(BinaryRelationExpression const& other) = default; +#ifndef WINDOWS + BinaryRelationExpression(BinaryRelationExpression&&) = default; + BinaryRelationExpression& operator=(BinaryRelationExpression&&) = default; +#endif + virtual ~BinaryRelationExpression() = default; + + // Override base class methods. + virtual storm::expressions::OperatorType getOperator() const override; + virtual bool evaluateAsBool(Valuation const* valuation = nullptr) const override; + virtual std::shared_ptr<BaseExpression const> simplify() const override; + virtual void accept(ExpressionVisitor* visitor) const override; + + /*! + * Retrieves the relation associated with the expression. + * + * @return The relation associated with the expression. + */ + RelationType getRelationType() const; + + protected: + // Override base class method. + virtual void printToStream(std::ostream& stream) const override; + + private: + // The relation type of the expression. + RelationType relationType; + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_BINARYRELATIONEXPRESSION_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/BooleanLiteralExpression.cpp b/src/storage/expressions/BooleanLiteralExpression.cpp new file mode 100644 index 000000000..5112ec8a9 --- /dev/null +++ b/src/storage/expressions/BooleanLiteralExpression.cpp @@ -0,0 +1,45 @@ +#include "src/storage/expressions/BooleanLiteralExpression.h" + +namespace storm { + namespace expressions { + BooleanLiteralExpression::BooleanLiteralExpression(bool value) : BaseExpression(ExpressionReturnType::Bool), value(value) { + // Intentionally left empty. + } + + bool BooleanLiteralExpression::evaluateAsBool(Valuation const* valuation) const { + return this->getValue(); + } + + bool BooleanLiteralExpression::isLiteral() const { + return true; + } + + bool BooleanLiteralExpression::isTrue() const { + return this->getValue() == true; + } + + bool BooleanLiteralExpression::isFalse() const { + return this->getValue() == false; + } + + std::set<std::string> BooleanLiteralExpression::getVariables() const { + return std::set<std::string>(); + } + + std::shared_ptr<BaseExpression const> BooleanLiteralExpression::simplify() const { + return this->shared_from_this(); + } + + void BooleanLiteralExpression::accept(ExpressionVisitor* visitor) const { + visitor->visit(this); + } + + bool BooleanLiteralExpression::getValue() const { + return this->value; + } + + void BooleanLiteralExpression::printToStream(std::ostream& stream) const { + stream << (this->getValue() ? "true" : "false"); + } + } +} \ No newline at end of file diff --git a/src/storage/expressions/BooleanLiteralExpression.h b/src/storage/expressions/BooleanLiteralExpression.h new file mode 100644 index 000000000..fc299e60a --- /dev/null +++ b/src/storage/expressions/BooleanLiteralExpression.h @@ -0,0 +1,54 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_BOOLEANLITERALEXPRESSION_H_ +#define STORM_STORAGE_EXPRESSIONS_BOOLEANLITERALEXPRESSION_H_ + +#include "src/storage/expressions/BaseExpression.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace expressions { + class BooleanLiteralExpression : public BaseExpression { + public: + /*! + * Creates a boolean literal expression with the given value. + * + * @param value The value of the boolean literal. + */ + BooleanLiteralExpression(bool value); + + // Instantiate constructors and assignments with their default implementations. + BooleanLiteralExpression(BooleanLiteralExpression const& other) = default; + BooleanLiteralExpression& operator=(BooleanLiteralExpression const& other) = default; +#ifndef WINDOWS + BooleanLiteralExpression(BooleanLiteralExpression&&) = default; + BooleanLiteralExpression& operator=(BooleanLiteralExpression&&) = default; +#endif + virtual ~BooleanLiteralExpression() = default; + + // Override base class methods. + virtual bool evaluateAsBool(Valuation const* valuation = nullptr) const override; + virtual bool isLiteral() const override; + virtual bool isTrue() const override; + virtual bool isFalse() const override; + virtual std::set<std::string> getVariables() const override; + virtual std::shared_ptr<BaseExpression const> simplify() const override; + virtual void accept(ExpressionVisitor* visitor) const override; + + /*! + * Retrieves the value of the boolean literal. + * + * @return The value of the boolean literal. + */ + bool getValue() const; + + protected: + // Override base class method. + virtual void printToStream(std::ostream& stream) const override; + + private: + // The value of the boolean literal. + bool value; + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_BOOLEANLITERALEXPRESSION_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/DoubleLiteralExpression.cpp b/src/storage/expressions/DoubleLiteralExpression.cpp new file mode 100644 index 000000000..b780a8862 --- /dev/null +++ b/src/storage/expressions/DoubleLiteralExpression.cpp @@ -0,0 +1,37 @@ +#include "src/storage/expressions/DoubleLiteralExpression.h" + +namespace storm { + namespace expressions { + DoubleLiteralExpression::DoubleLiteralExpression(double value) : BaseExpression(ExpressionReturnType::Double), value(value) { + // Intentionally left empty. + } + + double DoubleLiteralExpression::evaluateAsDouble(Valuation const* valuation) const { + return this->getValue(); + } + + bool DoubleLiteralExpression::isLiteral() const { + return true; + } + + std::set<std::string> DoubleLiteralExpression::getVariables() const { + return std::set<std::string>(); + } + + std::shared_ptr<BaseExpression const> DoubleLiteralExpression::simplify() const { + return this->shared_from_this(); + } + + void DoubleLiteralExpression::accept(ExpressionVisitor* visitor) const { + visitor->visit(this); + } + + double DoubleLiteralExpression::getValue() const { + return this->value; + } + + void DoubleLiteralExpression::printToStream(std::ostream& stream) const { + stream << this->getValue(); + } + } +} \ No newline at end of file diff --git a/src/storage/expressions/DoubleLiteralExpression.h b/src/storage/expressions/DoubleLiteralExpression.h new file mode 100644 index 000000000..ad22ee3be --- /dev/null +++ b/src/storage/expressions/DoubleLiteralExpression.h @@ -0,0 +1,52 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_DOUBLELITERALEXPRESSION_H_ +#define STORM_STORAGE_EXPRESSIONS_DOUBLELITERALEXPRESSION_H_ + +#include "src/storage/expressions/BaseExpression.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace expressions { + class DoubleLiteralExpression : public BaseExpression { + public: + /*! + * Creates an double literal expression with the given value. + * + * @param value The value of the double literal. + */ + DoubleLiteralExpression(double value); + + // Instantiate constructors and assignments with their default implementations. + DoubleLiteralExpression(DoubleLiteralExpression const& other) = default; + DoubleLiteralExpression& operator=(DoubleLiteralExpression const& other) = default; +#ifndef WINDOWS + DoubleLiteralExpression(DoubleLiteralExpression&&) = default; + DoubleLiteralExpression& operator=(DoubleLiteralExpression&&) = default; +#endif + virtual ~DoubleLiteralExpression() = default; + + // Override base class methods. + virtual double evaluateAsDouble(Valuation const* valuation = nullptr) const override; + virtual bool isLiteral() const override; + virtual std::set<std::string> getVariables() const override; + virtual std::shared_ptr<BaseExpression const> simplify() const override; + virtual void accept(ExpressionVisitor* visitor) const override; + + /*! + * Retrieves the value of the double literal. + * + * @return The value of the double literal. + */ + double getValue() const; + + protected: + // Override base class method. + virtual void printToStream(std::ostream& stream) const override; + + private: + // The value of the double literal. + double value; + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_DOUBLELITERALEXPRESSION_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/Expression.cpp b/src/storage/expressions/Expression.cpp new file mode 100644 index 000000000..2c247f796 --- /dev/null +++ b/src/storage/expressions/Expression.cpp @@ -0,0 +1,293 @@ +#include <map> +#include <unordered_map> + +#include "src/storage/expressions/Expression.h" +#include "src/storage/expressions/SubstitutionVisitor.h" +#include "src/storage/expressions/IdentifierSubstitutionVisitor.h" +#include "src/storage/expressions/TypeCheckVisitor.h" +#include "src/storage/expressions/LinearityCheckVisitor.h" +#include "src/storage/expressions/Expressions.h" +#include "src/exceptions/InvalidTypeException.h" +#include "src/exceptions/ExceptionMacros.h" + +namespace storm { + namespace expressions { + Expression::Expression(std::shared_ptr<BaseExpression const> const& expressionPtr) : expressionPtr(expressionPtr) { + // Intentionally left empty. + } + + Expression Expression::substitute(std::map<std::string, Expression> const& identifierToExpressionMap) const { + return SubstitutionVisitor<std::map<std::string, Expression>>(identifierToExpressionMap).substitute(*this); + } + + Expression Expression::substitute(std::unordered_map<std::string, Expression> const& identifierToExpressionMap) const { + return SubstitutionVisitor<std::unordered_map<std::string, Expression>>(identifierToExpressionMap).substitute(*this); + } + + Expression Expression::substitute(std::map<std::string, std::string> const& identifierToIdentifierMap) const { + return IdentifierSubstitutionVisitor<std::map<std::string, std::string>>(identifierToIdentifierMap).substitute(*this); + } + + Expression Expression::substitute(std::unordered_map<std::string, std::string> const& identifierToIdentifierMap) const { + return IdentifierSubstitutionVisitor<std::unordered_map<std::string, std::string>>(identifierToIdentifierMap).substitute(*this); + } + + void Expression::check(std::map<std::string, storm::expressions::ExpressionReturnType> const& identifierToTypeMap) const { + return TypeCheckVisitor<std::map<std::string, storm::expressions::ExpressionReturnType>>(identifierToTypeMap).check(*this); + } + + void Expression::check(std::unordered_map<std::string, storm::expressions::ExpressionReturnType> const& identifierToTypeMap) const { + return TypeCheckVisitor<std::unordered_map<std::string, storm::expressions::ExpressionReturnType>>(identifierToTypeMap).check(*this); + } + + bool Expression::evaluateAsBool(Valuation const* valuation) const { + return this->getBaseExpression().evaluateAsBool(valuation); + } + + int_fast64_t Expression::evaluateAsInt(Valuation const* valuation) const { + return this->getBaseExpression().evaluateAsInt(valuation); + } + + double Expression::evaluateAsDouble(Valuation const* valuation) const { + return this->getBaseExpression().evaluateAsDouble(valuation); + } + + Expression Expression::simplify() { + return Expression(this->getBaseExpression().simplify()); + } + + OperatorType Expression::getOperator() const { + return this->getBaseExpression().getOperator(); + } + + bool Expression::isFunctionApplication() const { + return this->getBaseExpression().isFunctionApplication(); + } + + uint_fast64_t Expression::getArity() const { + return this->getBaseExpression().getArity(); + } + + Expression Expression::getOperand(uint_fast64_t operandIndex) const { + return Expression(this->getBaseExpression().getOperand(operandIndex)); + } + + std::string const& Expression::getIdentifier() const { + return this->getBaseExpression().getIdentifier(); + } + + bool Expression::containsVariables() const { + return this->getBaseExpression().containsVariables(); + } + + bool Expression::isLiteral() const { + return this->getBaseExpression().isLiteral(); + } + + bool Expression::isVariable() const { + return this->getBaseExpression().isVariable(); + } + + bool Expression::isTrue() const { + return this->getBaseExpression().isTrue(); + } + + bool Expression::isFalse() const { + return this->getBaseExpression().isFalse(); + } + + bool Expression::isRelationalExpression() const { + if (!this->isFunctionApplication()) { + return false; + } + + return this->getOperator() == OperatorType::Equal || this->getOperator() == OperatorType::NotEqual + || this->getOperator() == OperatorType::Less || this->getOperator() == OperatorType::LessOrEqual + || this->getOperator() == OperatorType::Greater || this->getOperator() == OperatorType::GreaterOrEqual; + } + + bool Expression::isLinear() const { + return LinearityCheckVisitor().check(*this); + } + + std::set<std::string> Expression::getVariables() const { + return this->getBaseExpression().getVariables(); + } + + BaseExpression const& Expression::getBaseExpression() const { + return *this->expressionPtr; + } + + std::shared_ptr<BaseExpression const> const& Expression::getBaseExpressionPointer() const { + return this->expressionPtr; + } + + ExpressionReturnType Expression::getReturnType() const { + return this->getBaseExpression().getReturnType(); + } + + bool Expression::hasNumericalReturnType() const { + return this->getReturnType() == ExpressionReturnType::Int || this->getReturnType() == ExpressionReturnType::Double; + } + + bool Expression::hasBooleanReturnType() const { + return this->getReturnType() == ExpressionReturnType::Bool; + } + + Expression Expression::createBooleanLiteral(bool value) { + return Expression(std::shared_ptr<BaseExpression>(new BooleanLiteralExpression(value))); + } + + Expression Expression::createTrue() { + return createBooleanLiteral(true); + } + + Expression Expression::createFalse() { + return createBooleanLiteral(false); + } + + Expression Expression::createIntegerLiteral(int_fast64_t value) { + return Expression(std::shared_ptr<BaseExpression>(new IntegerLiteralExpression(value))); + } + + Expression Expression::createDoubleLiteral(double value) { + return Expression(std::shared_ptr<BaseExpression>(new DoubleLiteralExpression(value))); + } + + Expression Expression::createBooleanVariable(std::string const& variableName) { + return Expression(std::shared_ptr<BaseExpression>(new VariableExpression(ExpressionReturnType::Bool, variableName))); + } + + Expression Expression::createIntegerVariable(std::string const& variableName) { + return Expression(std::shared_ptr<BaseExpression>(new VariableExpression(ExpressionReturnType::Int, variableName))); + } + + Expression Expression::createDoubleVariable(std::string const& variableName) { + return Expression(std::shared_ptr<BaseExpression>(new VariableExpression(ExpressionReturnType::Double, variableName))); + } + + Expression Expression::createUndefinedVariable(std::string const& variableName) { + return Expression(std::shared_ptr<BaseExpression>(new VariableExpression(ExpressionReturnType::Undefined, variableName))); + } + + Expression Expression::operator+(Expression const& other) const { + LOG_THROW(this->hasNumericalReturnType() && other.hasNumericalReturnType(), storm::exceptions::InvalidTypeException, "Operator '+' requires numerical operands."); + return Expression(std::shared_ptr<BaseExpression>(new BinaryNumericalFunctionExpression(this->getReturnType() == ExpressionReturnType::Int && other.getReturnType() == ExpressionReturnType::Int ? ExpressionReturnType::Int : ExpressionReturnType::Double, this->getBaseExpressionPointer(), other.getBaseExpressionPointer(), BinaryNumericalFunctionExpression::OperatorType::Plus))); + } + + Expression Expression::operator-(Expression const& other) const { + LOG_THROW(this->hasNumericalReturnType() && other.hasNumericalReturnType(), storm::exceptions::InvalidTypeException, "Operator '-' requires numerical operands."); + return Expression(std::shared_ptr<BaseExpression>(new BinaryNumericalFunctionExpression(this->getReturnType() == ExpressionReturnType::Int && other.getReturnType() == ExpressionReturnType::Int ? ExpressionReturnType::Int : ExpressionReturnType::Double, this->getBaseExpressionPointer(), other.getBaseExpressionPointer(), BinaryNumericalFunctionExpression::OperatorType::Minus))); + } + + Expression Expression::operator-() const { + LOG_THROW(this->hasNumericalReturnType(), storm::exceptions::InvalidTypeException, "Operator '-' requires numerical operand."); + return Expression(std::shared_ptr<BaseExpression>(new UnaryNumericalFunctionExpression(this->getReturnType(), this->getBaseExpressionPointer(), UnaryNumericalFunctionExpression::OperatorType::Minus))); + } + + Expression Expression::operator*(Expression const& other) const { + LOG_THROW(this->hasNumericalReturnType() && other.hasNumericalReturnType(), storm::exceptions::InvalidTypeException, "Operator '*' requires numerical operands."); + return Expression(std::shared_ptr<BaseExpression>(new BinaryNumericalFunctionExpression(this->getReturnType() == ExpressionReturnType::Int && other.getReturnType() == ExpressionReturnType::Int ? ExpressionReturnType::Int : ExpressionReturnType::Double, this->getBaseExpressionPointer(), other.getBaseExpressionPointer(), BinaryNumericalFunctionExpression::OperatorType::Times))); + } + + Expression Expression::operator/(Expression const& other) const { + LOG_THROW(this->hasNumericalReturnType() && other.hasNumericalReturnType(), storm::exceptions::InvalidTypeException, "Operator '/' requires numerical operands."); + return Expression(std::shared_ptr<BaseExpression>(new BinaryNumericalFunctionExpression(this->getReturnType() == ExpressionReturnType::Int && other.getReturnType() == ExpressionReturnType::Int ? ExpressionReturnType::Int : ExpressionReturnType::Double, this->getBaseExpressionPointer(), other.getBaseExpressionPointer(), BinaryNumericalFunctionExpression::OperatorType::Divide))); + } + + Expression Expression::operator^(Expression const& other) const { + LOG_THROW(this->hasNumericalReturnType() && other.hasNumericalReturnType(), storm::exceptions::InvalidTypeException, "Operator '^' requires numerical operands."); + return Expression(std::shared_ptr<BaseExpression>(new BinaryNumericalFunctionExpression(this->getReturnType() == ExpressionReturnType::Int && other.getReturnType() == ExpressionReturnType::Int ? ExpressionReturnType::Int : ExpressionReturnType::Double, this->getBaseExpressionPointer(), other.getBaseExpressionPointer(), BinaryNumericalFunctionExpression::OperatorType::Power))); + } + + Expression Expression::operator&&(Expression const& other) const { + LOG_THROW(this->hasBooleanReturnType() && other.hasBooleanReturnType(), storm::exceptions::InvalidTypeException, "Operator '&&' requires boolean operands."); + return Expression(std::shared_ptr<BaseExpression>(new BinaryBooleanFunctionExpression(ExpressionReturnType::Bool, this->getBaseExpressionPointer(), other.getBaseExpressionPointer(), BinaryBooleanFunctionExpression::OperatorType::And))); + } + + Expression Expression::operator||(Expression const& other) const { + LOG_THROW(this->hasBooleanReturnType() && other.hasBooleanReturnType(), storm::exceptions::InvalidTypeException, "Operator '||' requires numerical operands."); + return Expression(std::shared_ptr<BaseExpression>(new BinaryBooleanFunctionExpression(ExpressionReturnType::Bool, this->getBaseExpressionPointer(), other.getBaseExpressionPointer(), BinaryBooleanFunctionExpression::OperatorType::Or))); + } + + Expression Expression::operator!() const { + LOG_THROW(this->hasBooleanReturnType(), storm::exceptions::InvalidTypeException, "Operator '!' requires boolean operand."); + return Expression(std::shared_ptr<BaseExpression>(new UnaryBooleanFunctionExpression(ExpressionReturnType::Bool, this->getBaseExpressionPointer(), UnaryBooleanFunctionExpression::OperatorType::Not))); + } + + Expression Expression::operator==(Expression const& other) const { + LOG_THROW(this->hasNumericalReturnType() && other.hasNumericalReturnType(), storm::exceptions::InvalidTypeException, "Operator '==' requires numerical operands."); + return Expression(std::shared_ptr<BaseExpression>(new BinaryRelationExpression(ExpressionReturnType::Bool, this->getBaseExpressionPointer(), other.getBaseExpressionPointer(), BinaryRelationExpression::RelationType::Equal))); + } + + Expression Expression::operator!=(Expression const& other) const { + LOG_THROW((this->hasNumericalReturnType() && other.hasNumericalReturnType()) || (this->hasBooleanReturnType() && other.hasBooleanReturnType()), storm::exceptions::InvalidTypeException, "Operator '!=' requires operands of equal type."); + if (this->hasNumericalReturnType() && other.hasNumericalReturnType()) { + return Expression(std::shared_ptr<BaseExpression>(new BinaryRelationExpression(ExpressionReturnType::Bool, this->getBaseExpressionPointer(), other.getBaseExpressionPointer(), BinaryRelationExpression::RelationType::NotEqual))); + } else { + return Expression(std::shared_ptr<BaseExpression>(new BinaryBooleanFunctionExpression(ExpressionReturnType::Bool, this->getBaseExpressionPointer(), other.getBaseExpressionPointer(), BinaryBooleanFunctionExpression::OperatorType::Xor))); + } + } + + Expression Expression::operator>(Expression const& other) const { + LOG_THROW(this->hasNumericalReturnType() && other.hasNumericalReturnType(), storm::exceptions::InvalidTypeException, "Operator '>' requires numerical operands."); + return Expression(std::shared_ptr<BaseExpression>(new BinaryRelationExpression(ExpressionReturnType::Bool, this->getBaseExpressionPointer(), other.getBaseExpressionPointer(), BinaryRelationExpression::RelationType::Greater))); + } + + Expression Expression::operator>=(Expression const& other) const { + LOG_THROW(this->hasNumericalReturnType() && other.hasNumericalReturnType(), storm::exceptions::InvalidTypeException, "Operator '>=' requires numerical operands."); + return Expression(std::shared_ptr<BaseExpression>(new BinaryRelationExpression(ExpressionReturnType::Bool, this->getBaseExpressionPointer(), other.getBaseExpressionPointer(), BinaryRelationExpression::RelationType::GreaterOrEqual))); + } + + Expression Expression::operator<(Expression const& other) const { + LOG_THROW(this->hasNumericalReturnType() && other.hasNumericalReturnType(), storm::exceptions::InvalidTypeException, "Operator '<' requires numerical operands."); + return Expression(std::shared_ptr<BaseExpression>(new BinaryRelationExpression(ExpressionReturnType::Bool, this->getBaseExpressionPointer(), other.getBaseExpressionPointer(), BinaryRelationExpression::RelationType::Less))); + } + + Expression Expression::operator<=(Expression const& other) const { + LOG_THROW(this->hasNumericalReturnType() && other.hasNumericalReturnType(), storm::exceptions::InvalidTypeException, "Operator '<=' requires numerical operands."); + return Expression(std::shared_ptr<BaseExpression>(new BinaryRelationExpression(ExpressionReturnType::Bool, this->getBaseExpressionPointer(), other.getBaseExpressionPointer(), BinaryRelationExpression::RelationType::LessOrEqual))); + } + + Expression Expression::minimum(Expression const& lhs, Expression const& rhs) { + LOG_THROW(lhs.hasNumericalReturnType() && rhs.hasNumericalReturnType(), storm::exceptions::InvalidTypeException, "Operator 'min' requires numerical operands."); + return Expression(std::shared_ptr<BaseExpression>(new BinaryNumericalFunctionExpression(lhs.getReturnType() == ExpressionReturnType::Int && rhs.getReturnType() == ExpressionReturnType::Int ? ExpressionReturnType::Int : ExpressionReturnType::Double, lhs.getBaseExpressionPointer(), rhs.getBaseExpressionPointer(), BinaryNumericalFunctionExpression::OperatorType::Min))); + } + + Expression Expression::maximum(Expression const& lhs, Expression const& rhs) { + LOG_THROW(lhs.hasNumericalReturnType() && rhs.hasNumericalReturnType(), storm::exceptions::InvalidTypeException, "Operator 'max' requires numerical operands."); + return Expression(std::shared_ptr<BaseExpression>(new BinaryNumericalFunctionExpression(lhs.getReturnType() == ExpressionReturnType::Int && rhs.getReturnType() == ExpressionReturnType::Int ? ExpressionReturnType::Int : ExpressionReturnType::Double, lhs.getBaseExpressionPointer(), rhs.getBaseExpressionPointer(), BinaryNumericalFunctionExpression::OperatorType::Max))); + } + + Expression Expression::ite(Expression const& thenExpression, Expression const& elseExpression) { + LOG_THROW(this->hasBooleanReturnType(), storm::exceptions::InvalidTypeException, "Condition of if-then-else operator must be of boolean type."); + LOG_THROW(thenExpression.hasBooleanReturnType() && elseExpression.hasBooleanReturnType() || thenExpression.hasNumericalReturnType() && elseExpression.hasNumericalReturnType(), storm::exceptions::InvalidTypeException, "'then' and 'else' expression of if-then-else operator must have equal return type."); + return Expression(std::shared_ptr<BaseExpression>(new IfThenElseExpression(thenExpression.hasBooleanReturnType() && elseExpression.hasBooleanReturnType() ? ExpressionReturnType::Bool : (thenExpression.getReturnType() == ExpressionReturnType::Int && elseExpression.getReturnType() == ExpressionReturnType::Int ? ExpressionReturnType::Int : ExpressionReturnType::Double), this->getBaseExpressionPointer(), thenExpression.getBaseExpressionPointer(), elseExpression.getBaseExpressionPointer()))); + } + + Expression Expression::implies(Expression const& other) const { + LOG_THROW(this->hasBooleanReturnType() && other.hasBooleanReturnType(), storm::exceptions::InvalidTypeException, "Operator '&&' requires boolean operands."); + return Expression(std::shared_ptr<BaseExpression>(new BinaryBooleanFunctionExpression(ExpressionReturnType::Bool, this->getBaseExpressionPointer(), other.getBaseExpressionPointer(), BinaryBooleanFunctionExpression::OperatorType::Implies))); + } + + Expression Expression::iff(Expression const& other) const { + LOG_THROW(this->hasBooleanReturnType() && other.hasBooleanReturnType(), storm::exceptions::InvalidTypeException, "Operator '&&' requires boolean operands."); + return Expression(std::shared_ptr<BaseExpression>(new BinaryBooleanFunctionExpression(ExpressionReturnType::Bool, this->getBaseExpressionPointer(), other.getBaseExpressionPointer(), BinaryBooleanFunctionExpression::OperatorType::Iff))); + } + + Expression Expression::floor() const { + LOG_THROW(this->hasNumericalReturnType(), storm::exceptions::InvalidTypeException, "Operator 'floor' requires numerical operand."); + return Expression(std::shared_ptr<BaseExpression>(new UnaryNumericalFunctionExpression(ExpressionReturnType::Int, this->getBaseExpressionPointer(), UnaryNumericalFunctionExpression::OperatorType::Floor))); + } + + Expression Expression::ceil() const { + LOG_THROW(this->hasNumericalReturnType(), storm::exceptions::InvalidTypeException, "Operator 'ceil' requires numerical operand."); + return Expression(std::shared_ptr<BaseExpression>(new UnaryNumericalFunctionExpression(ExpressionReturnType::Int, this->getBaseExpressionPointer(), UnaryNumericalFunctionExpression::OperatorType::Ceil))); + } + + std::ostream& operator<<(std::ostream& stream, Expression const& expression) { + stream << expression.getBaseExpression(); + return stream; + } + } +} diff --git a/src/storage/expressions/Expression.h b/src/storage/expressions/Expression.h new file mode 100644 index 000000000..08c758f27 --- /dev/null +++ b/src/storage/expressions/Expression.h @@ -0,0 +1,308 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_EXPRESSION_H_ +#define STORM_STORAGE_EXPRESSIONS_EXPRESSION_H_ + +#include <memory> +#include <map> +#include <unordered_map> + +#include "src/storage/expressions/BaseExpression.h" +#include "src/storage/expressions/ExpressionVisitor.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace expressions { + class Expression { + public: + Expression() = default; + + /*! + * Creates an expression with the given underlying base expression. + * + * @param expressionPtr A pointer to the underlying base expression. + */ + Expression(std::shared_ptr<BaseExpression const> const& expressionPtr); + + // Instantiate constructors and assignments with their default implementations. + Expression(Expression const& other) = default; + Expression& operator=(Expression const& other) = default; +#ifndef WINDOWS + Expression(Expression&&) = default; + Expression& operator=(Expression&&) = default; +#endif + + // Static factory methods to create atomic expression parts. + static Expression createBooleanLiteral(bool value); + static Expression createTrue(); + static Expression createFalse(); + static Expression createIntegerLiteral(int_fast64_t value); + static Expression createDoubleLiteral(double value); + static Expression createBooleanVariable(std::string const& variableName); + static Expression createIntegerVariable(std::string const& variableName); + static Expression createDoubleVariable(std::string const& variableName); + static Expression createUndefinedVariable(std::string const& variableName); + + // Provide operator overloads to conveniently construct new expressions from other expressions. + Expression operator+(Expression const& other) const; + Expression operator-(Expression const& other) const; + Expression operator-() const; + Expression operator*(Expression const& other) const; + Expression operator/(Expression const& other) const; + Expression operator^(Expression const& other) const; + Expression operator&&(Expression const& other) const; + Expression operator||(Expression const& other) const; + Expression operator!() const; + Expression operator==(Expression const& other) const; + Expression operator!=(Expression const& other) const; + Expression operator>(Expression const& other) const; + Expression operator>=(Expression const& other) const; + Expression operator<(Expression const& other) const; + Expression operator<=(Expression const& other) const; + + Expression ite(Expression const& thenExpression, Expression const& elseExpression); + Expression implies(Expression const& other) const; + Expression iff(Expression const& other) const; + + Expression floor() const; + Expression ceil() const; + + static Expression minimum(Expression const& lhs, Expression const& rhs); + static Expression maximum(Expression const& lhs, Expression const& rhs); + + /*! + * Substitutes all occurrences of identifiers according to the given map. Note that this substitution is + * done simultaneously, i.e., identifiers appearing in the expressions that were "plugged in" are not + * substituted. + * + * @param identifierToExpressionMap A mapping from identifiers to the expression they are substituted with. + * @return An expression in which all identifiers in the key set of the mapping are replaced by the + * expression they are mapped to. + */ + Expression substitute(std::map<std::string, Expression> const& identifierToExpressionMap) const; + + /*! + * Substitutes all occurrences of identifiers according to the given map. Note that this substitution is + * done simultaneously, i.e., identifiers appearing in the expressions that were "plugged in" are not + * substituted. + * + * @param identifierToExpressionMap A mapping from identifiers to the expression they are substituted with. + * @return An expression in which all identifiers in the key set of the mapping are replaced by the + * expression they are mapped to. + */ + Expression substitute(std::unordered_map<std::string, Expression> const& identifierToExpressionMap) const; + + /*! + * Substitutes all occurrences of identifiers with different names given by a mapping. + * + * @param identifierToIdentifierMap A mapping from identifiers to identifiers they are substituted with. + * @return An expression in which all identifiers in the key set of the mapping are replaced by the + * identifiers they are mapped to. + */ + Expression substitute(std::map<std::string, std::string> const& identifierToIdentifierMap) const; + + /*! + * Substitutes all occurrences of identifiers with different names given by a mapping. + * + * @param identifierToIdentifierMap A mapping from identifiers to identifiers they are substituted with. + * @return An expression in which all identifiers in the key set of the mapping are replaced by the + * identifiers they are mapped to. + */ + Expression substitute(std::unordered_map<std::string, std::string> const& identifierToIdentifierMap) const; + + /*! + * Checks that all identifiers appearing in the expression have the types given by the map. An exception + * is thrown in case a violation is found. + * + * @param identifierToTypeMap A mapping from identifiers to the types that are supposed to have. + */ + void check(std::map<std::string, storm::expressions::ExpressionReturnType> const& identifierToTypeMap) const; + + /*! + * Checks that all identifiers appearing in the expression have the types given by the map. An exception + * is thrown in case a violation is found. + * + * @param identifierToTypeMap A mapping from identifiers to the types that are supposed to have. + */ + void check(std::unordered_map<std::string, storm::expressions::ExpressionReturnType> const& identifierToTypeMap) const; + + /*! + * Evaluates the expression under the valuation of variables given by the valuation and returns the + * resulting boolean value. If the return type of the expression is not a boolean an exception is thrown. + * + * @param valuation The valuation of unknowns under which to evaluate the expression. + * @return The boolean value of the expression under the given valuation. + */ + bool evaluateAsBool(Valuation const* valuation = nullptr) const; + + /*! + * Evaluates the expression under the valuation of variables given by the valuation and returns the + * resulting integer value. If the return type of the expression is not an integer an exception is thrown. + * + * @param valuation The valuation of unknowns under which to evaluate the expression. + * @return The integer value of the expression under the given valuation. + */ + int_fast64_t evaluateAsInt(Valuation const* valuation = nullptr) const; + + /*! + * Evaluates the expression under the valuation of variables given by the valuation and returns the + * resulting double value. If the return type of the expression is not a double an exception is thrown. + * + * @param valuation The valuation of unknowns under which to evaluate the expression. + * @return The double value of the expression under the given valuation. + */ + double evaluateAsDouble(Valuation const* valuation = nullptr) const; + + /*! + * Simplifies the expression according to some basic rules. + * + * @return The simplified expression. + */ + Expression simplify(); + + /*! + * Retrieves the operator of a function application. This is only legal to call if the expression is + * function application. + * + * @return The operator associated with the function application. + */ + OperatorType getOperator() const; + + /*! + * Checks if the expression is a function application (of any sort). + * + * @return True iff the expression is a function application. + */ + bool isFunctionApplication() const; + + /*! + * Retrieves the arity of the expression. + * + * @return The arity of the expression. + */ + uint_fast64_t getArity() const; + + /*! + * Retrieves the given operand from the expression. + * + * @param operandIndex The index of the operand to retrieve. This must be lower than the arity of the expression. + * @return The operand at the given index. + */ + Expression getOperand(uint_fast64_t operandIndex) const; + + /*! + * Retrieves the identifier associated with this expression. This is only legal to call if the expression + * is a variable. + * + * @return The identifier associated with this expression. + */ + std::string const& getIdentifier() const; + + /*! + * Retrieves whether the expression contains a variable. + * + * @return True iff the expression contains a variable. + */ + bool containsVariables() const; + + /*! + * Retrieves whether the expression is a literal. + * + * @return True iff the expression is a literal. + */ + bool isLiteral() const; + + /*! + * Retrieves whether the expression is a variable. + * + * @return True iff the expression is a variable. + */ + bool isVariable() const; + + /*! + * Checks if the expression is equal to the boolean literal true. + * + * @return True iff the expression is equal to the boolean literal true. + */ + bool isTrue() const; + + /*! + * Checks if the expression is equal to the boolean literal false. + * + * @return True iff the expression is equal to the boolean literal false. + */ + bool isFalse() const; + + /*! + * Retrieves whether this expression is a relation expression, i.e., an expression that has a relation + * (equal, not equal, less, less or equal, etc.) as its top-level operator. + * + * @return True iff the expression is a relation expression. + */ + bool isRelationalExpression() const; + + /*! + * Retrieves whether this expression is a linear expression. + * + * @return True iff the expression is linear. + */ + bool isLinear() const; + + /*! + * Retrieves the set of all variables that appear in the expression. + * + * @return The set of all variables that appear in the expression. + */ + std::set<std::string> getVariables() const; + + /*! + * Retrieves the base expression underlying this expression object. Note that prior to calling this, the + * expression object must be properly initialized. + * + * @return A reference to the underlying base expression. + */ + BaseExpression const& getBaseExpression() const; + + /*! + * Retrieves a pointer to the base expression underlying this expression object. + * + * @return A pointer to the underlying base expression. + */ + std::shared_ptr<BaseExpression const> const& getBaseExpressionPointer() const; + + /*! + * Retrieves the return type of the expression. + * + * @return The return type of the expression. + */ + ExpressionReturnType getReturnType() const; + + /*! + * Retrieves whether the expression has a numerical return type, i.e., integer or double. + * + * @return True iff the expression has a numerical return type. + */ + bool hasNumericalReturnType() const; + + /*! + * Retrieves whether the expression has a boolean return type. + * + * @return True iff the expression has a boolean return type. + */ + bool hasBooleanReturnType() const; + + /*! + * Accepts the given visitor. + * + * @param visitor The visitor to accept. + */ + void accept(ExpressionVisitor* visitor) const; + + friend std::ostream& operator<<(std::ostream& stream, Expression const& expression); + + private: + // A pointer to the underlying base expression. + std::shared_ptr<BaseExpression const> expressionPtr; + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_EXPRESSION_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/ExpressionReturnType.cpp b/src/storage/expressions/ExpressionReturnType.cpp new file mode 100644 index 000000000..c10810f6c --- /dev/null +++ b/src/storage/expressions/ExpressionReturnType.cpp @@ -0,0 +1,15 @@ +#include "src/storage/expressions/ExpressionReturnType.h" + +namespace storm { + namespace expressions { + std::ostream& operator<<(std::ostream& stream, ExpressionReturnType const& enumValue) { + switch (enumValue) { + case ExpressionReturnType::Undefined: stream << "undefined"; break; + case ExpressionReturnType::Bool: stream << "bool"; break; + case ExpressionReturnType::Int: stream << "int"; break; + case ExpressionReturnType::Double: stream << "double"; break; + } + return stream; + } + } +} \ No newline at end of file diff --git a/src/storage/expressions/ExpressionReturnType.h b/src/storage/expressions/ExpressionReturnType.h new file mode 100644 index 000000000..0cf928ed8 --- /dev/null +++ b/src/storage/expressions/ExpressionReturnType.h @@ -0,0 +1,17 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_EXPRESSIONRETURNTYPE_H_ +#define STORM_STORAGE_EXPRESSIONS_EXPRESSIONRETURNTYPE_H_ + +#include <iostream> + +namespace storm { + namespace expressions { + /*! + * Each node in an expression tree has a uniquely defined type from this enum. + */ + enum class ExpressionReturnType {Undefined, Bool, Int, Double}; + + std::ostream& operator<<(std::ostream& stream, ExpressionReturnType const& enumValue); + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_EXPRESSIONRETURNTYPE_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/ExpressionVisitor.h b/src/storage/expressions/ExpressionVisitor.h new file mode 100644 index 000000000..1b417f6ed --- /dev/null +++ b/src/storage/expressions/ExpressionVisitor.h @@ -0,0 +1,34 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_EXPRESSIONVISITOR_H_ +#define STORM_STORAGE_EXPRESSIONS_EXPRESSIONVISITOR_H_ + +namespace storm { + namespace expressions { + // Forward-declare all expression classes. + class IfThenElseExpression; + class BinaryBooleanFunctionExpression; + class BinaryNumericalFunctionExpression; + class BinaryRelationExpression; + class VariableExpression; + class UnaryBooleanFunctionExpression; + class UnaryNumericalFunctionExpression; + class BooleanLiteralExpression; + class IntegerLiteralExpression; + class DoubleLiteralExpression; + + class ExpressionVisitor { + public: + virtual void visit(IfThenElseExpression const* expression) = 0; + virtual void visit(BinaryBooleanFunctionExpression const* expression) = 0; + virtual void visit(BinaryNumericalFunctionExpression const* expression) = 0; + virtual void visit(BinaryRelationExpression const* expression) = 0; + virtual void visit(VariableExpression const* expression) = 0; + virtual void visit(UnaryBooleanFunctionExpression const* expression) = 0; + virtual void visit(UnaryNumericalFunctionExpression const* expression) = 0; + virtual void visit(BooleanLiteralExpression const* expression) = 0; + virtual void visit(IntegerLiteralExpression const* expression) = 0; + virtual void visit(DoubleLiteralExpression const* expression) = 0; + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_EXPRESSIONVISITOR_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/Expressions.h b/src/storage/expressions/Expressions.h new file mode 100644 index 000000000..d670c29e3 --- /dev/null +++ b/src/storage/expressions/Expressions.h @@ -0,0 +1,11 @@ +#include "src/storage/expressions/IfThenElseExpression.h" +#include "src/storage/expressions/BinaryBooleanFunctionExpression.h" +#include "src/storage/expressions/BinaryNumericalFunctionExpression.h" +#include "src/storage/expressions/BinaryRelationExpression.h" +#include "src/storage/expressions/BooleanLiteralExpression.h" +#include "src/storage/expressions/IntegerLiteralExpression.h" +#include "src/storage/expressions/DoubleLiteralExpression.h" +#include "src/storage/expressions/UnaryBooleanFunctionExpression.h" +#include "src/storage/expressions/UnaryNumericalFunctionExpression.h" +#include "src/storage/expressions/VariableExpression.h" +#include "src/storage/expressions/Expression.h" \ No newline at end of file diff --git a/src/storage/expressions/IdentifierSubstitutionVisitor.cpp b/src/storage/expressions/IdentifierSubstitutionVisitor.cpp new file mode 100644 index 000000000..19724dea1 --- /dev/null +++ b/src/storage/expressions/IdentifierSubstitutionVisitor.cpp @@ -0,0 +1,155 @@ +#include <map> +#include <unordered_map> +#include <string> + +#include "src/storage/expressions/IdentifierSubstitutionVisitor.h" +#include "src/storage/expressions/Expressions.h" + +namespace storm { + namespace expressions { + template<typename MapType> + IdentifierSubstitutionVisitor<MapType>::IdentifierSubstitutionVisitor(MapType const& identifierToIdentifierMap) : identifierToIdentifierMap(identifierToIdentifierMap) { + // Intentionally left empty. + } + + template<typename MapType> + Expression IdentifierSubstitutionVisitor<MapType>::substitute(Expression const& expression) { + expression.getBaseExpression().accept(this); + return Expression(this->expressionStack.top()); + } + + template<typename MapType> + void IdentifierSubstitutionVisitor<MapType>::visit(IfThenElseExpression const* expression) { + expression->getCondition()->accept(this); + std::shared_ptr<BaseExpression const> conditionExpression = expressionStack.top(); + expressionStack.pop(); + + expression->getThenExpression()->accept(this); + std::shared_ptr<BaseExpression const> thenExpression = expressionStack.top(); + expressionStack.pop(); + + expression->getElseExpression()->accept(this); + std::shared_ptr<BaseExpression const> elseExpression = expressionStack.top(); + expressionStack.pop(); + + // If the arguments did not change, we simply push the expression itself. + if (conditionExpression.get() == expression->getCondition().get() && thenExpression.get() == expression->getThenExpression().get() && elseExpression.get() == expression->getElseExpression().get()) { + this->expressionStack.push(expression->getSharedPointer()); + } else { + this->expressionStack.push(std::shared_ptr<BaseExpression>(new IfThenElseExpression(expression->getReturnType(), conditionExpression, thenExpression, elseExpression))); + } + } + + template<typename MapType> + void IdentifierSubstitutionVisitor<MapType>::visit(BinaryBooleanFunctionExpression const* expression) { + expression->getFirstOperand()->accept(this); + std::shared_ptr<BaseExpression const> firstExpression = expressionStack.top(); + expressionStack.pop(); + + expression->getSecondOperand()->accept(this); + std::shared_ptr<BaseExpression const> secondExpression = expressionStack.top(); + expressionStack.pop(); + + // If the arguments did not change, we simply push the expression itself. + if (firstExpression.get() == expression->getFirstOperand().get() && secondExpression.get() == expression->getSecondOperand().get()) { + this->expressionStack.push(expression->getSharedPointer()); + } else { + this->expressionStack.push(std::shared_ptr<BaseExpression>(new BinaryBooleanFunctionExpression(expression->getReturnType(), firstExpression, secondExpression, expression->getOperatorType()))); + } + } + + template<typename MapType> + void IdentifierSubstitutionVisitor<MapType>::visit(BinaryNumericalFunctionExpression const* expression) { + expression->getFirstOperand()->accept(this); + std::shared_ptr<BaseExpression const> firstExpression = expressionStack.top(); + expressionStack.pop(); + + expression->getSecondOperand()->accept(this); + std::shared_ptr<BaseExpression const> secondExpression = expressionStack.top(); + expressionStack.pop(); + + // If the arguments did not change, we simply push the expression itself. + if (firstExpression.get() == expression->getFirstOperand().get() && secondExpression.get() == expression->getSecondOperand().get()) { + this->expressionStack.push(expression->getSharedPointer()); + } else { + this->expressionStack.push(std::shared_ptr<BaseExpression>(new BinaryNumericalFunctionExpression(expression->getReturnType(), firstExpression, secondExpression, expression->getOperatorType()))); + } + } + + template<typename MapType> + void IdentifierSubstitutionVisitor<MapType>::visit(BinaryRelationExpression const* expression) { + expression->getFirstOperand()->accept(this); + std::shared_ptr<BaseExpression const> firstExpression = expressionStack.top(); + expressionStack.pop(); + + expression->getSecondOperand()->accept(this); + std::shared_ptr<BaseExpression const> secondExpression = expressionStack.top(); + expressionStack.pop(); + + // If the arguments did not change, we simply push the expression itself. + if (firstExpression.get() == expression->getFirstOperand().get() && secondExpression.get() == expression->getSecondOperand().get()) { + this->expressionStack.push(expression->getSharedPointer()); + } else { + this->expressionStack.push(std::shared_ptr<BaseExpression>(new BinaryRelationExpression(expression->getReturnType(), firstExpression, secondExpression, expression->getRelationType()))); + } + } + + template<typename MapType> + void IdentifierSubstitutionVisitor<MapType>::visit(VariableExpression const* expression) { + // If the variable is in the key set of the substitution, we need to replace it. + auto const& namePair = this->identifierToIdentifierMap.find(expression->getVariableName()); + if (namePair != this->identifierToIdentifierMap.end()) { + this->expressionStack.push(std::shared_ptr<BaseExpression>(new VariableExpression(expression->getReturnType(), namePair->second))); + } else { + this->expressionStack.push(expression->getSharedPointer()); + } + } + + template<typename MapType> + void IdentifierSubstitutionVisitor<MapType>::visit(UnaryBooleanFunctionExpression const* expression) { + expression->getOperand()->accept(this); + std::shared_ptr<BaseExpression const> operandExpression = expressionStack.top(); + expressionStack.pop(); + + // If the argument did not change, we simply push the expression itself. + if (operandExpression.get() == expression->getOperand().get()) { + expressionStack.push(expression->getSharedPointer()); + } else { + expressionStack.push(std::shared_ptr<BaseExpression>(new UnaryBooleanFunctionExpression(expression->getReturnType(), operandExpression, expression->getOperatorType()))); + } + } + + template<typename MapType> + void IdentifierSubstitutionVisitor<MapType>::visit(UnaryNumericalFunctionExpression const* expression) { + expression->getOperand()->accept(this); + std::shared_ptr<BaseExpression const> operandExpression = expressionStack.top(); + expressionStack.pop(); + + // If the argument did not change, we simply push the expression itself. + if (operandExpression.get() == expression->getOperand().get()) { + expressionStack.push(expression->getSharedPointer()); + } else { + expressionStack.push(std::shared_ptr<BaseExpression>(new UnaryNumericalFunctionExpression(expression->getReturnType(), operandExpression, expression->getOperatorType()))); + } + } + + template<typename MapType> + void IdentifierSubstitutionVisitor<MapType>::visit(BooleanLiteralExpression const* expression) { + this->expressionStack.push(expression->getSharedPointer()); + } + + template<typename MapType> + void IdentifierSubstitutionVisitor<MapType>::visit(IntegerLiteralExpression const* expression) { + this->expressionStack.push(expression->getSharedPointer()); + } + + template<typename MapType> + void IdentifierSubstitutionVisitor<MapType>::visit(DoubleLiteralExpression const* expression) { + this->expressionStack.push(expression->getSharedPointer()); + } + + // Explicitly instantiate the class with map and unordered_map. + template class IdentifierSubstitutionVisitor< std::map<std::string, std::string> >; + template class IdentifierSubstitutionVisitor< std::unordered_map<std::string, std::string> >; + } +} diff --git a/src/storage/expressions/IdentifierSubstitutionVisitor.h b/src/storage/expressions/IdentifierSubstitutionVisitor.h new file mode 100644 index 000000000..8e8723a70 --- /dev/null +++ b/src/storage/expressions/IdentifierSubstitutionVisitor.h @@ -0,0 +1,52 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_IDENTIFIERSUBSTITUTIONVISITOR_H_ +#define STORM_STORAGE_EXPRESSIONS_IDENTIFIERSUBSTITUTIONVISITOR_H_ + +#include <stack> + +#include "src/storage/expressions/Expression.h" +#include "src/storage/expressions/ExpressionVisitor.h" + +namespace storm { + namespace expressions { + template<typename MapType> + class IdentifierSubstitutionVisitor : public ExpressionVisitor { + public: + /*! + * Creates a new substitution visitor that uses the given map to replace identifiers. + * + * @param identifierToExpressionMap A mapping from identifiers to expressions. + */ + IdentifierSubstitutionVisitor(MapType const& identifierToExpressionMap); + + /*! + * Substitutes the identifiers in the given expression according to the previously given map and returns the + * resulting expression. + * + * @param expression The expression in which to substitute the identifiers. + * @return The expression in which all identifiers in the key set of the previously given mapping are + * substituted with the mapped-to expressions. + */ + Expression substitute(Expression const& expression); + + virtual void visit(IfThenElseExpression const* expression) override; + virtual void visit(BinaryBooleanFunctionExpression const* expression) override; + virtual void visit(BinaryNumericalFunctionExpression const* expression) override; + virtual void visit(BinaryRelationExpression const* expression) override; + virtual void visit(VariableExpression const* expression) override; + virtual void visit(UnaryBooleanFunctionExpression const* expression) override; + virtual void visit(UnaryNumericalFunctionExpression const* expression) override; + virtual void visit(BooleanLiteralExpression const* expression) override; + virtual void visit(IntegerLiteralExpression const* expression) override; + virtual void visit(DoubleLiteralExpression const* expression) override; + + private: + // A stack of expression used to pass the results to the higher levels. + std::stack<std::shared_ptr<BaseExpression const>> expressionStack; + + // A mapping of identifier names to expressions with which they shall be replaced. + MapType const& identifierToIdentifierMap; + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_IDENTIFIERSUBSTITUTIONVISITOR_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/IfThenElseExpression.cpp b/src/storage/expressions/IfThenElseExpression.cpp new file mode 100644 index 000000000..bbd0b28d2 --- /dev/null +++ b/src/storage/expressions/IfThenElseExpression.cpp @@ -0,0 +1,113 @@ +#include "src/storage/expressions/IfThenElseExpression.h" + +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidAccessException.h" + +namespace storm { + namespace expressions { + IfThenElseExpression::IfThenElseExpression(ExpressionReturnType returnType, std::shared_ptr<BaseExpression const> const& condition, std::shared_ptr<BaseExpression const> const& thenExpression, std::shared_ptr<BaseExpression const> const& elseExpression) : BaseExpression(returnType), condition(condition), thenExpression(thenExpression), elseExpression(elseExpression) { + // Intentionally left empty. + } + + std::shared_ptr<BaseExpression const> IfThenElseExpression::getOperand(uint_fast64_t operandIndex) const { + LOG_THROW(operandIndex < 3, storm::exceptions::InvalidAccessException, "Unable to access operand " << operandIndex << " in expression of arity 3."); + if (operandIndex == 0) { + return this->getCondition(); + } else if (operandIndex == 1) { + return this->getThenExpression(); + } else { + return this->getElseExpression(); + } + } + + OperatorType IfThenElseExpression::getOperator() const { + return OperatorType::Ite; + } + + bool IfThenElseExpression::isFunctionApplication() const { + return true; + } + + bool IfThenElseExpression::containsVariables() const { + return this->getCondition()->containsVariables() || this->getThenExpression()->containsVariables() || this->getElseExpression()->containsVariables(); + } + + uint_fast64_t IfThenElseExpression::getArity() const { + return 3; + } + + bool IfThenElseExpression::evaluateAsBool(Valuation const* valuation) const { + bool conditionValue = this->condition->evaluateAsBool(valuation); + if (conditionValue) { + return this->thenExpression->evaluateAsBool(valuation); + } else { + return this->elseExpression->evaluateAsBool(valuation); + } + } + + int_fast64_t IfThenElseExpression::evaluateAsInt(Valuation const* valuation) const { + bool conditionValue = this->condition->evaluateAsBool(valuation); + if (conditionValue) { + return this->thenExpression->evaluateAsInt(valuation); + } else { + return this->elseExpression->evaluateAsInt(valuation); + } + } + + double IfThenElseExpression::evaluateAsDouble(Valuation const* valuation) const { + bool conditionValue = this->condition->evaluateAsBool(valuation); + if (conditionValue) { + return this->thenExpression->evaluateAsDouble(valuation); + } else { + return this->elseExpression->evaluateAsDouble(valuation); + } + } + + std::set<std::string> IfThenElseExpression::getVariables() const { + std::set<std::string> result = this->condition->getVariables(); + std::set<std::string> tmp = this->thenExpression->getVariables(); + result.insert(tmp.begin(), tmp.end()); + tmp = this->elseExpression->getVariables(); + result.insert(tmp.begin(), tmp.end()); + return result; + } + + std::shared_ptr<BaseExpression const> IfThenElseExpression::simplify() const { + std::shared_ptr<BaseExpression const> conditionSimplified; + if (conditionSimplified->isTrue()) { + return this->thenExpression->simplify(); + } else if (conditionSimplified->isFalse()) { + return this->elseExpression->simplify(); + } else { + std::shared_ptr<BaseExpression const> thenExpressionSimplified = this->thenExpression->simplify(); + std::shared_ptr<BaseExpression const> elseExpressionSimplified = this->elseExpression->simplify(); + + if (conditionSimplified.get() == this->condition.get() && thenExpressionSimplified.get() == this->thenExpression.get() && elseExpressionSimplified.get() == this->elseExpression.get()) { + return this->shared_from_this(); + } else { + return std::shared_ptr<BaseExpression>(new IfThenElseExpression(this->getReturnType(), conditionSimplified, thenExpressionSimplified, elseExpressionSimplified)); + } + } + } + + void IfThenElseExpression::accept(ExpressionVisitor* visitor) const { + visitor->visit(this); + } + + std::shared_ptr<BaseExpression const> IfThenElseExpression::getCondition() const { + return this->condition; + } + + std::shared_ptr<BaseExpression const> IfThenElseExpression::getThenExpression() const { + return this->thenExpression; + } + + std::shared_ptr<BaseExpression const> IfThenElseExpression::getElseExpression() const { + return this->elseExpression; + } + + void IfThenElseExpression::printToStream(std::ostream& stream) const { + stream << "(" << *this->condition << " ? " << *this->thenExpression << " : " << *this->elseExpression << ")"; + } + } +} \ No newline at end of file diff --git a/src/storage/expressions/IfThenElseExpression.h b/src/storage/expressions/IfThenElseExpression.h new file mode 100644 index 000000000..425633850 --- /dev/null +++ b/src/storage/expressions/IfThenElseExpression.h @@ -0,0 +1,80 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_IFTHENELSEEXPRESSION_H_ +#define STORM_STORAGE_EXPRESSIONS_IFTHENELSEEXPRESSION_H_ + +#include "src/storage/expressions/BaseExpression.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace expressions { + class IfThenElseExpression : public BaseExpression { + public: + /*! + * Creates an if-then-else expression with the given return type, condition and operands. + * + * @param returnType The return type of the expression. + * @param thenExpression The expression evaluated if the condition evaluates true. + * @param elseExpression The expression evaluated if the condition evaluates false. + */ + IfThenElseExpression(ExpressionReturnType returnType, std::shared_ptr<BaseExpression const> const& condition, std::shared_ptr<BaseExpression const> const& thenExpression, std::shared_ptr<BaseExpression const> const& elseExpression); + + // Instantiate constructors and assignments with their default implementations. + IfThenElseExpression(IfThenElseExpression const& other) = default; + IfThenElseExpression& operator=(IfThenElseExpression const& other) = default; +#ifndef WINDOWS + IfThenElseExpression(IfThenElseExpression&&) = default; + IfThenElseExpression& operator=(IfThenElseExpression&&) = default; +#endif + virtual ~IfThenElseExpression() = default; + + // Override base class methods. + virtual std::shared_ptr<BaseExpression const> getOperand(uint_fast64_t operandIndex) const override; + virtual OperatorType getOperator() const override; + virtual bool isFunctionApplication() const override; + virtual bool containsVariables() const override; + virtual uint_fast64_t getArity() const override; + virtual bool evaluateAsBool(Valuation const* valuation = nullptr) const override; + virtual int_fast64_t evaluateAsInt(Valuation const* valuation = nullptr) const override; + virtual double evaluateAsDouble(Valuation const* valuation = nullptr) const override; + virtual std::set<std::string> getVariables() const override; + virtual std::shared_ptr<BaseExpression const> simplify() const override; + virtual void accept(ExpressionVisitor* visitor) const override; + + /*! + * Retrieves the condition expression of the if-then-else expression. + * + * @return The condition expression of the if-then-else expression. + */ + std::shared_ptr<BaseExpression const> getCondition() const; + + /*! + * Retrieves the then expression of the if-then-else expression. + * + * @return The then expression of the if-then-else expression. + */ + std::shared_ptr<BaseExpression const> getThenExpression() const; + + /*! + * Retrieves the else expression of the if-then-else expression. + * + * @return The else expression of the if-then-else expression. + */ + std::shared_ptr<BaseExpression const> getElseExpression() const; + + protected: + // Override base class method. + virtual void printToStream(std::ostream& stream) const override; + + private: + // The condition of the if-then-else. + std::shared_ptr<BaseExpression const> condition; + + // The return expression of the if-part. + std::shared_ptr<BaseExpression const> thenExpression; + + // The return expression of the else-part. + std::shared_ptr<BaseExpression const> elseExpression; + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_IFTHENELSEEXPRESSION_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/IntegerLiteralExpression.cpp b/src/storage/expressions/IntegerLiteralExpression.cpp new file mode 100644 index 000000000..47341a771 --- /dev/null +++ b/src/storage/expressions/IntegerLiteralExpression.cpp @@ -0,0 +1,41 @@ +#include "src/storage/expressions/IntegerLiteralExpression.h" + +namespace storm { + namespace expressions { + IntegerLiteralExpression::IntegerLiteralExpression(int_fast64_t value) : BaseExpression(ExpressionReturnType::Int), value(value) { + // Intentionally left empty. + } + + int_fast64_t IntegerLiteralExpression::evaluateAsInt(Valuation const* valuation) const { + return this->getValue(); + } + + double IntegerLiteralExpression::evaluateAsDouble(Valuation const* valuation) const { + return static_cast<double>(this->evaluateAsInt(valuation)); + } + + bool IntegerLiteralExpression::isLiteral() const { + return true; + } + + std::set<std::string> IntegerLiteralExpression::getVariables() const { + return std::set<std::string>(); + } + + std::shared_ptr<BaseExpression const> IntegerLiteralExpression::simplify() const { + return this->shared_from_this(); + } + + void IntegerLiteralExpression::accept(ExpressionVisitor* visitor) const { + visitor->visit(this); + } + + int_fast64_t IntegerLiteralExpression::getValue() const { + return this->value; + } + + void IntegerLiteralExpression::printToStream(std::ostream& stream) const { + stream << this->getValue(); + } + } +} \ No newline at end of file diff --git a/src/storage/expressions/IntegerLiteralExpression.h b/src/storage/expressions/IntegerLiteralExpression.h new file mode 100644 index 000000000..4a9e8882f --- /dev/null +++ b/src/storage/expressions/IntegerLiteralExpression.h @@ -0,0 +1,53 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_INTEGERLITERALEXPRESSION_H_ +#define STORM_STORAGE_EXPRESSIONS_INTEGERLITERALEXPRESSION_H_ + +#include "src/storage/expressions/BaseExpression.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace expressions { + class IntegerLiteralExpression : public BaseExpression { + public: + /*! + * Creates an integer literal expression with the given value. + * + * @param value The value of the integer literal. + */ + IntegerLiteralExpression(int_fast64_t value); + + // Instantiate constructors and assignments with their default implementations. + IntegerLiteralExpression(IntegerLiteralExpression const& other) = default; + IntegerLiteralExpression& operator=(IntegerLiteralExpression const& other) = default; +#ifndef WINDOWS + IntegerLiteralExpression(IntegerLiteralExpression&&) = default; + IntegerLiteralExpression& operator=(IntegerLiteralExpression&&) = default; +#endif + virtual ~IntegerLiteralExpression() = default; + + // Override base class methods. + virtual int_fast64_t evaluateAsInt(Valuation const* valuation = nullptr) const override; + virtual double evaluateAsDouble(Valuation const* valuation = nullptr) const override; + virtual bool isLiteral() const override; + virtual std::set<std::string> getVariables() const override; + virtual std::shared_ptr<BaseExpression const> simplify() const override; + virtual void accept(ExpressionVisitor* visitor) const override; + + /*! + * Retrieves the value of the integer literal. + * + * @return The value of the integer literal. + */ + int_fast64_t getValue() const; + + protected: + // Override base class method. + virtual void printToStream(std::ostream& stream) const override; + + private: + // The value of the integer literal. + int_fast64_t value; + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_INTEGERLITERALEXPRESSION_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/LinearCoefficientVisitor.cpp b/src/storage/expressions/LinearCoefficientVisitor.cpp new file mode 100644 index 000000000..7525d6dde --- /dev/null +++ b/src/storage/expressions/LinearCoefficientVisitor.cpp @@ -0,0 +1,151 @@ +#include "src/storage/expressions/LinearCoefficientVisitor.h" + +#include "src/storage/expressions/Expressions.h" +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidArgumentException.h" + +namespace storm { + namespace expressions { + std::pair<SimpleValuation, double> LinearCoefficientVisitor::getLinearCoefficients(Expression const& expression) { + expression.getBaseExpression().accept(this); + return resultStack.top(); + } + + void LinearCoefficientVisitor::visit(IfThenElseExpression const* expression) { + LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); + } + + void LinearCoefficientVisitor::visit(BinaryBooleanFunctionExpression const* expression) { + LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); + } + + void LinearCoefficientVisitor::visit(BinaryNumericalFunctionExpression const* expression) { + if (expression->getOperatorType() == BinaryNumericalFunctionExpression::OperatorType::Plus) { + expression->getFirstOperand()->accept(this); + std::pair<SimpleValuation, double> leftResult = resultStack.top(); + resultStack.pop(); + expression->getSecondOperand()->accept(this); + std::pair<SimpleValuation, double>& rightResult = resultStack.top(); + + // Now add the left result to the right result. + for (auto const& identifier : leftResult.first.getDoubleIdentifiers()) { + if (rightResult.first.containsDoubleIdentifier(identifier)) { + rightResult.first.setDoubleValue(identifier, leftResult.first.getDoubleValue(identifier) + rightResult.first.getDoubleValue(identifier)); + } else { + rightResult.first.setDoubleValue(identifier, leftResult.first.getDoubleValue(identifier)); + } + } + rightResult.second += leftResult.second; + return; + } else if (expression->getOperatorType() == BinaryNumericalFunctionExpression::OperatorType::Minus) { + expression->getFirstOperand()->accept(this); + std::pair<SimpleValuation, double> leftResult = resultStack.top(); + resultStack.pop(); + expression->getSecondOperand()->accept(this); + std::pair<SimpleValuation, double>& rightResult = resultStack.top(); + + // Now subtract the right result from the left result. + for (auto const& identifier : leftResult.first.getDoubleIdentifiers()) { + if (rightResult.first.containsDoubleIdentifier(identifier)) { + rightResult.first.setDoubleValue(identifier, leftResult.first.getDoubleValue(identifier) - rightResult.first.getDoubleValue(identifier)); + } else { + rightResult.first.setDoubleValue(identifier, leftResult.first.getDoubleValue(identifier)); + } + } + for (auto const& identifier : rightResult.first.getDoubleIdentifiers()) { + if (!leftResult.first.containsDoubleIdentifier(identifier)) { + rightResult.first.setDoubleValue(identifier, -rightResult.first.getDoubleValue(identifier)); + } + } + rightResult.second = leftResult.second - rightResult.second; + return; + } else if (expression->getOperatorType() == BinaryNumericalFunctionExpression::OperatorType::Times) { + expression->getFirstOperand()->accept(this); + std::pair<SimpleValuation, double> leftResult = resultStack.top(); + resultStack.pop(); + expression->getSecondOperand()->accept(this); + std::pair<SimpleValuation, double>& rightResult = resultStack.top(); + + // If the expression is linear, either the left or the right side must not contain variables. + LOG_THROW(leftResult.first.getNumberOfIdentifiers() == 0 || rightResult.first.getNumberOfIdentifiers() == 0, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); + if (leftResult.first.getNumberOfIdentifiers() == 0) { + for (auto const& identifier : rightResult.first.getDoubleIdentifiers()) { + rightResult.first.setDoubleValue(identifier, leftResult.second * rightResult.first.getDoubleValue(identifier)); + } + } else { + for (auto const& identifier : leftResult.first.getDoubleIdentifiers()) { + rightResult.first.addDoubleIdentifier(identifier, rightResult.second * leftResult.first.getDoubleValue(identifier)); + } + } + rightResult.second *= leftResult.second; + return; + } else if (expression->getOperatorType() == BinaryNumericalFunctionExpression::OperatorType::Divide) { + expression->getFirstOperand()->accept(this); + std::pair<SimpleValuation, double> leftResult = resultStack.top(); + resultStack.pop(); + expression->getSecondOperand()->accept(this); + std::pair<SimpleValuation, double>& rightResult = resultStack.top(); + + // If the expression is linear, either the left or the right side must not contain variables. + LOG_THROW(leftResult.first.getNumberOfIdentifiers() == 0 || rightResult.first.getNumberOfIdentifiers() == 0, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); + if (leftResult.first.getNumberOfIdentifiers() == 0) { + for (auto const& identifier : rightResult.first.getDoubleIdentifiers()) { + rightResult.first.setDoubleValue(identifier, leftResult.second / rightResult.first.getDoubleValue(identifier)); + } + } else { + for (auto const& identifier : leftResult.first.getDoubleIdentifiers()) { + rightResult.first.addDoubleIdentifier(identifier, leftResult.first.getDoubleValue(identifier) / rightResult.second); + } + } + rightResult.second = leftResult.second / leftResult.second; + return; + } else { + LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); + } + } + + void LinearCoefficientVisitor::visit(BinaryRelationExpression const* expression) { + LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); + } + + void LinearCoefficientVisitor::visit(VariableExpression const* expression) { + SimpleValuation valuation; + switch (expression->getReturnType()) { + case ExpressionReturnType::Bool: LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); break; + case ExpressionReturnType::Int: + case ExpressionReturnType::Double: valuation.addDoubleIdentifier(expression->getVariableName(), 1); break; + case ExpressionReturnType::Undefined: LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Illegal expression return type."); break; + } + + resultStack.push(std::make_pair(valuation, 0)); + } + + void LinearCoefficientVisitor::visit(UnaryBooleanFunctionExpression const* expression) { + LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); + } + + void LinearCoefficientVisitor::visit(UnaryNumericalFunctionExpression const* expression) { + if (expression->getOperatorType() == UnaryNumericalFunctionExpression::OperatorType::Minus) { + // Here, we need to negate all double identifiers. + std::pair<SimpleValuation, double>& valuationConstantPair = resultStack.top(); + for (auto const& identifier : valuationConstantPair.first.getDoubleIdentifiers()) { + valuationConstantPair.first.setDoubleValue(identifier, -valuationConstantPair.first.getDoubleValue(identifier)); + } + } else { + LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); + } + } + + void LinearCoefficientVisitor::visit(BooleanLiteralExpression const* expression) { + LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); + } + + void LinearCoefficientVisitor::visit(IntegerLiteralExpression const* expression) { + resultStack.push(std::make_pair(SimpleValuation(), static_cast<double>(expression->getValue()))); + } + + void LinearCoefficientVisitor::visit(DoubleLiteralExpression const* expression) { + resultStack.push(std::make_pair(SimpleValuation(), expression->getValue())); + } + } +} \ No newline at end of file diff --git a/src/storage/expressions/LinearCoefficientVisitor.h b/src/storage/expressions/LinearCoefficientVisitor.h new file mode 100644 index 000000000..263e752c8 --- /dev/null +++ b/src/storage/expressions/LinearCoefficientVisitor.h @@ -0,0 +1,46 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_LINEARCOEFFICIENTVISITOR_H_ +#define STORM_STORAGE_EXPRESSIONS_LINEARCOEFFICIENTVISITOR_H_ + +#include <stack> + +#include "src/storage/expressions/Expression.h" +#include "src/storage/expressions/ExpressionVisitor.h" +#include "src/storage/expressions/SimpleValuation.h" + +namespace storm { + namespace expressions { + class LinearCoefficientVisitor : public ExpressionVisitor { + public: + /*! + * Creates a linear coefficient visitor. + */ + LinearCoefficientVisitor() = default; + + /*! + * Computes the (double) coefficients of all identifiers appearing in the expression if the expression + * was rewritten as a sum of atoms.. If the expression is not linear, an exception is thrown. + * + * @param expression The expression for which to compute the coefficients. + * @return A pair consisting of a mapping from identifiers to their coefficients and the coefficient of + * the constant atom. + */ + std::pair<SimpleValuation, double> getLinearCoefficients(Expression const& expression); + + virtual void visit(IfThenElseExpression const* expression) override; + virtual void visit(BinaryBooleanFunctionExpression const* expression) override; + virtual void visit(BinaryNumericalFunctionExpression const* expression) override; + virtual void visit(BinaryRelationExpression const* expression) override; + virtual void visit(VariableExpression const* expression) override; + virtual void visit(UnaryBooleanFunctionExpression const* expression) override; + virtual void visit(UnaryNumericalFunctionExpression const* expression) override; + virtual void visit(BooleanLiteralExpression const* expression) override; + virtual void visit(IntegerLiteralExpression const* expression) override; + virtual void visit(DoubleLiteralExpression const* expression) override; + + private: + std::stack<std::pair<SimpleValuation, double>> resultStack; + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_LINEARCOEFFICIENTVISITOR_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/LinearityCheckVisitor.cpp b/src/storage/expressions/LinearityCheckVisitor.cpp new file mode 100644 index 000000000..6f595900a --- /dev/null +++ b/src/storage/expressions/LinearityCheckVisitor.cpp @@ -0,0 +1,113 @@ +#include "src/storage/expressions/LinearityCheckVisitor.h" +#include "src/storage/expressions/Expressions.h" + +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidTypeException.h" + +namespace storm { + namespace expressions { + LinearityCheckVisitor::LinearityCheckVisitor() : resultStack() { + // Intentionally left empty. + } + + bool LinearityCheckVisitor::check(Expression const& expression) { + expression.getBaseExpression().accept(this); + return resultStack.top() == LinearityStatus::LinearWithoutVariables || resultStack.top() == LinearityStatus::LinearContainsVariables; + } + + void LinearityCheckVisitor::visit(IfThenElseExpression const* expression) { + // An if-then-else expression is never linear. + resultStack.push(LinearityStatus::NonLinear); + } + + void LinearityCheckVisitor::visit(BinaryBooleanFunctionExpression const* expression) { + // Boolean function applications are not allowed in linear expressions. + resultStack.push(LinearityStatus::NonLinear); + } + + void LinearityCheckVisitor::visit(BinaryNumericalFunctionExpression const* expression) { + LinearityStatus leftResult; + LinearityStatus rightResult; + switch (expression->getOperatorType()) { + case BinaryNumericalFunctionExpression::OperatorType::Plus: + case BinaryNumericalFunctionExpression::OperatorType::Minus: + expression->getFirstOperand()->accept(this); + leftResult = resultStack.top(); + + if (leftResult == LinearityStatus::NonLinear) { + return; + } else { + resultStack.pop(); + expression->getSecondOperand()->accept(this); + rightResult = resultStack.top(); + if (rightResult == LinearityStatus::NonLinear) { + return; + } + resultStack.pop(); + } + + resultStack.push(leftResult == LinearityStatus::LinearContainsVariables || rightResult == LinearityStatus::LinearContainsVariables ? LinearityStatus::LinearContainsVariables : LinearityStatus::LinearWithoutVariables); + break; + case BinaryNumericalFunctionExpression::OperatorType::Times: + case BinaryNumericalFunctionExpression::OperatorType::Divide: + expression->getFirstOperand()->accept(this); + leftResult = resultStack.top(); + + if (leftResult == LinearityStatus::NonLinear) { + return; + } else { + resultStack.pop(); + expression->getSecondOperand()->accept(this); + rightResult = resultStack.top(); + if (rightResult == LinearityStatus::NonLinear) { + return; + } + resultStack.pop(); + } + + if (leftResult == LinearityStatus::LinearContainsVariables && rightResult == LinearityStatus::LinearContainsVariables) { + resultStack.push(LinearityStatus::NonLinear); + } + + resultStack.push(leftResult == LinearityStatus::LinearContainsVariables || rightResult == LinearityStatus::LinearContainsVariables ? LinearityStatus::LinearContainsVariables : LinearityStatus::LinearWithoutVariables); + break; + case BinaryNumericalFunctionExpression::OperatorType::Min: resultStack.push(LinearityStatus::NonLinear); break; + case BinaryNumericalFunctionExpression::OperatorType::Max: resultStack.push(LinearityStatus::NonLinear); break; + case BinaryNumericalFunctionExpression::OperatorType::Power: resultStack.push(LinearityStatus::NonLinear); break; + } + } + + void LinearityCheckVisitor::visit(BinaryRelationExpression const* expression) { + resultStack.push(LinearityStatus::NonLinear); + } + + void LinearityCheckVisitor::visit(VariableExpression const* expression) { + resultStack.push(LinearityStatus::LinearContainsVariables); + } + + void LinearityCheckVisitor::visit(UnaryBooleanFunctionExpression const* expression) { + // Boolean function applications are not allowed in linear expressions. + resultStack.push(LinearityStatus::NonLinear); + } + + void LinearityCheckVisitor::visit(UnaryNumericalFunctionExpression const* expression) { + switch (expression->getOperatorType()) { + case UnaryNumericalFunctionExpression::OperatorType::Minus: break; + case UnaryNumericalFunctionExpression::OperatorType::Floor: + case UnaryNumericalFunctionExpression::OperatorType::Ceil: resultStack.pop(); resultStack.push(LinearityStatus::NonLinear); break; + } + } + + void LinearityCheckVisitor::visit(BooleanLiteralExpression const* expression) { + resultStack.push(LinearityStatus::NonLinear); + } + + void LinearityCheckVisitor::visit(IntegerLiteralExpression const* expression) { + resultStack.push(LinearityStatus::LinearWithoutVariables); + } + + void LinearityCheckVisitor::visit(DoubleLiteralExpression const* expression) { + resultStack.push(LinearityStatus::LinearWithoutVariables); + } + } +} \ No newline at end of file diff --git a/src/storage/expressions/LinearityCheckVisitor.h b/src/storage/expressions/LinearityCheckVisitor.h new file mode 100644 index 000000000..d76b658c8 --- /dev/null +++ b/src/storage/expressions/LinearityCheckVisitor.h @@ -0,0 +1,45 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_LINEARITYCHECKVISITOR_H_ +#define STORM_STORAGE_EXPRESSIONS_LINEARITYCHECKVISITOR_H_ + +#include <stack> + +#include "src/storage/expressions/Expression.h" +#include "src/storage/expressions/ExpressionVisitor.h" + +namespace storm { + namespace expressions { + class LinearityCheckVisitor : public ExpressionVisitor { + public: + /*! + * Creates a linearity check visitor. + */ + LinearityCheckVisitor(); + + /*! + * Checks that the given expression is linear. + * + * @param expression The expression to check for linearity. + */ + bool check(Expression const& expression); + + virtual void visit(IfThenElseExpression const* expression) override; + virtual void visit(BinaryBooleanFunctionExpression const* expression) override; + virtual void visit(BinaryNumericalFunctionExpression const* expression) override; + virtual void visit(BinaryRelationExpression const* expression) override; + virtual void visit(VariableExpression const* expression) override; + virtual void visit(UnaryBooleanFunctionExpression const* expression) override; + virtual void visit(UnaryNumericalFunctionExpression const* expression) override; + virtual void visit(BooleanLiteralExpression const* expression) override; + virtual void visit(IntegerLiteralExpression const* expression) override; + virtual void visit(DoubleLiteralExpression const* expression) override; + + private: + enum class LinearityStatus { NonLinear, LinearContainsVariables, LinearWithoutVariables }; + + // A stack for communicating the results of the subexpressions. + std::stack<LinearityStatus> resultStack; + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_LINEARITYCHECKVISITOR_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/OperatorType.h b/src/storage/expressions/OperatorType.h new file mode 100644 index 000000000..8968cf105 --- /dev/null +++ b/src/storage/expressions/OperatorType.h @@ -0,0 +1,34 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_OPERATORTYPE_H_ +#define STORM_STORAGE_EXPRESSIONS_OPERATORTYPE_H_ + +namespace storm { + namespace expressions { + // An enum representing all possible operator types. + enum class OperatorType { + And, + Or, + Xor, + Implies, + Iff, + Plus, + Minus, + Times, + Divide, + Min, + Max, + Power, + Equal, + NotEqual, + Less, + LessOrEqual, + Greater, + GreaterOrEqual, + Not, + Floor, + Ceil, + Ite + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_OPERATORTYPE_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/SimpleValuation.cpp b/src/storage/expressions/SimpleValuation.cpp new file mode 100644 index 000000000..923bbe84b --- /dev/null +++ b/src/storage/expressions/SimpleValuation.cpp @@ -0,0 +1,177 @@ +#include "src/storage/expressions/SimpleValuation.h" + +#include <set> + +#include <boost/functional/hash.hpp> +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidArgumentException.h" +#include "src/exceptions/InvalidAccessException.h" + +namespace storm { + namespace expressions { + bool SimpleValuation::operator==(SimpleValuation const& other) const { + return this->identifierToValueMap == other.identifierToValueMap; + } + + void SimpleValuation::addBooleanIdentifier(std::string const& name, bool initialValue) { + LOG_THROW(this->identifierToValueMap.find(name) == this->identifierToValueMap.end(), storm::exceptions::InvalidArgumentException, "Identifier '" << name << "' already registered."); + this->identifierToValueMap.emplace(name, initialValue); + } + + void SimpleValuation::addIntegerIdentifier(std::string const& name, int_fast64_t initialValue) { + LOG_THROW(this->identifierToValueMap.find(name) == this->identifierToValueMap.end(), storm::exceptions::InvalidArgumentException, "Identifier '" << name << "' already registered."); + this->identifierToValueMap.emplace(name, initialValue); + } + + void SimpleValuation::addDoubleIdentifier(std::string const& name, double initialValue) { + LOG_THROW(this->identifierToValueMap.find(name) == this->identifierToValueMap.end(), storm::exceptions::InvalidArgumentException, "Identifier '" << name << "' already registered."); + this->identifierToValueMap.emplace(name, initialValue); + } + + void SimpleValuation::setBooleanValue(std::string const& name, bool value) { + this->identifierToValueMap[name] = value; + } + + void SimpleValuation::setIntegerValue(std::string const& name, int_fast64_t value) { + this->identifierToValueMap[name] = value; + } + + void SimpleValuation::setDoubleValue(std::string const& name, double value) { + this->identifierToValueMap[name] = value; + } + + void SimpleValuation::removeIdentifier(std::string const& name) { + auto nameValuePair = this->identifierToValueMap.find(name); + LOG_THROW(nameValuePair != this->identifierToValueMap.end(), storm::exceptions::InvalidArgumentException, "Deleting unknown identifier '" << name << "'."); + this->identifierToValueMap.erase(nameValuePair); + } + + ExpressionReturnType SimpleValuation::getIdentifierType(std::string const& name) const { + auto nameValuePair = this->identifierToValueMap.find(name); + LOG_THROW(nameValuePair != this->identifierToValueMap.end(), storm::exceptions::InvalidAccessException, "Access to unkown identifier '" << name << "'."); + if (nameValuePair->second.type() == typeid(bool)) { + return ExpressionReturnType::Bool; + } else if (nameValuePair->second.type() == typeid(int_fast64_t)) { + return ExpressionReturnType::Int; + } else { + return ExpressionReturnType::Double; + } + } + + bool SimpleValuation::containsBooleanIdentifier(std::string const& name) const { + auto nameValuePair = this->identifierToValueMap.find(name); + if (nameValuePair == this->identifierToValueMap.end()) { + return false; + } + return nameValuePair->second.type() == typeid(bool); + } + + bool SimpleValuation::containsIntegerIdentifier(std::string const& name) const { + auto nameValuePair = this->identifierToValueMap.find(name); + if (nameValuePair == this->identifierToValueMap.end()) { + return false; + } + return nameValuePair->second.type() == typeid(int_fast64_t); + } + + bool SimpleValuation::containsDoubleIdentifier(std::string const& name) const { + auto nameValuePair = this->identifierToValueMap.find(name); + if (nameValuePair == this->identifierToValueMap.end()) { + return false; + } + return nameValuePair->second.type() == typeid(double); + } + + bool SimpleValuation::getBooleanValue(std::string const& name) const { + auto nameValuePair = this->identifierToValueMap.find(name); + LOG_THROW(nameValuePair != this->identifierToValueMap.end(), storm::exceptions::InvalidAccessException, "Access to unkown identifier '" << name << "'."); + return boost::get<bool>(nameValuePair->second); + } + + int_fast64_t SimpleValuation::getIntegerValue(std::string const& name) const { + auto nameValuePair = this->identifierToValueMap.find(name); + LOG_THROW(nameValuePair != this->identifierToValueMap.end(), storm::exceptions::InvalidAccessException, "Access to unkown identifier '" << name << "'."); + return boost::get<int_fast64_t>(nameValuePair->second); + } + + double SimpleValuation::getDoubleValue(std::string const& name) const { + auto nameValuePair = this->identifierToValueMap.find(name); + LOG_THROW(nameValuePair != this->identifierToValueMap.end(), storm::exceptions::InvalidAccessException, "Access to unkown identifier '" << name << "'."); + return boost::get<double>(nameValuePair->second); + } + + std::size_t SimpleValuation::getNumberOfIdentifiers() const { + return this->identifierToValueMap.size(); + } + + std::set<std::string> SimpleValuation::getIdentifiers() const { + std::set<std::string> result; + for (auto const& nameValuePair : this->identifierToValueMap) { + result.insert(nameValuePair.first); + } + return result; + } + + std::set<std::string> SimpleValuation::getBooleanIdentifiers() const { + std::set<std::string> result; + for (auto const& nameValuePair : this->identifierToValueMap) { + if (nameValuePair.second.type() == typeid(bool)) { + result.insert(nameValuePair.first); + } + } + return result; + } + + std::set<std::string> SimpleValuation::getIntegerIdentifiers() const { + std::set<std::string> result; + for (auto const& nameValuePair : this->identifierToValueMap) { + if (nameValuePair.second.type() == typeid(int_fast64_t)) { + result.insert(nameValuePair.first); + } + } + return result; + } + + std::set<std::string> SimpleValuation::getDoubleIdentifiers() const { + std::set<std::string> result; + for (auto const& nameValuePair : this->identifierToValueMap) { + if (nameValuePair.second.type() == typeid(double)) { + result.insert(nameValuePair.first); + } + } + return result; + } + + std::ostream& operator<<(std::ostream& stream, SimpleValuation const& valuation) { + stream << "{ "; + uint_fast64_t elementIndex = 0; + for (auto const& nameValuePair : valuation.identifierToValueMap) { + stream << nameValuePair.first << " -> " << nameValuePair.second << " "; + ++elementIndex; + if (elementIndex < valuation.identifierToValueMap.size()) { + stream << ", "; + } + } + stream << "}"; + + return stream; + } + + std::size_t SimpleValuationPointerHash::operator()(SimpleValuation* valuation) const { + size_t seed = 0; + for (auto const& nameValuePair : valuation->identifierToValueMap) { + boost::hash_combine(seed, nameValuePair.first); + boost::hash_combine(seed, nameValuePair.second); + } + return seed; + } + + bool SimpleValuationPointerCompare::operator()(SimpleValuation* valuation1, SimpleValuation* valuation2) const { + return *valuation1 == *valuation2; + } + + bool SimpleValuationPointerLess::operator()(SimpleValuation* valuation1, SimpleValuation* valuation2) const { + return valuation1->identifierToValueMap < valuation2->identifierToValueMap; + } + } +} \ No newline at end of file diff --git a/src/storage/expressions/SimpleValuation.h b/src/storage/expressions/SimpleValuation.h new file mode 100644 index 000000000..fccfe2faa --- /dev/null +++ b/src/storage/expressions/SimpleValuation.h @@ -0,0 +1,148 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_SIMPLEVALUATION_H_ +#define STORM_STORAGE_EXPRESSIONS_SIMPLEVALUATION_H_ + +#include <boost/container/flat_map.hpp> +#include <boost/variant.hpp> +#include <iostream> + +#include "src/storage/expressions/Valuation.h" +#include "src/storage/expressions/ExpressionReturnType.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace expressions { + class SimpleValuation : public Valuation { + public: + friend class SimpleValuationPointerHash; + friend class SimpleValuationPointerLess; + + // Instantiate some constructors and assignments with their default implementations. + SimpleValuation() = default; + SimpleValuation(SimpleValuation const&) = default; + SimpleValuation& operator=(SimpleValuation const&) = default; +#ifndef WINDOWS + SimpleValuation(SimpleValuation&&) = default; + SimpleValuation& operator=(SimpleValuation&&) = default; +#endif + virtual ~SimpleValuation() = default; + + /*! + * Compares two simple valuations wrt. equality. + */ + bool operator==(SimpleValuation const& other) const; + + /*! + * Adds a boolean identifier with the given name. + * + * @param name The name of the boolean identifier to add. + * @param initialValue The initial value of the identifier. + */ + void addBooleanIdentifier(std::string const& name, bool initialValue = false); + + /*! + * Adds a integer identifier with the given name. + * + * @param name The name of the integer identifier to add. + * @param initialValue The initial value of the identifier. + */ + void addIntegerIdentifier(std::string const& name, int_fast64_t initialValue = 0); + + /*! + * Adds a double identifier with the given name. + * + * @param name The name of the double identifier to add. + * @param initialValue The initial value of the identifier. + */ + void addDoubleIdentifier(std::string const& name, double initialValue = 0); + + /*! + * Sets the value of the boolean identifier with the given name to the given value. + * + * @param name The name of the boolean identifier whose value to set. + * @param value The new value of the boolean identifier. + */ + void setBooleanValue(std::string const& name, bool value); + + /*! + * Sets the value of the integer identifier with the given name to the given value. + * + * @param name The name of the integer identifier whose value to set. + * @param value The new value of the integer identifier. + */ + void setIntegerValue(std::string const& name, int_fast64_t value); + + /*! + * Sets the value of the double identifier with the given name to the given value. + * + * @param name The name of the double identifier whose value to set. + * @param value The new value of the double identifier. + */ + void setDoubleValue(std::string const& name, double value); + + /*! + * Removes the given identifier from this valuation. + * + * @param name The name of the identifier that is to be removed. + */ + void removeIdentifier(std::string const& name); + + /*! + * Retrieves the type of the identifier with the given name. + * + * @param name The name of the identifier whose type to retrieve. + * @return The type of the identifier with the given name. + */ + ExpressionReturnType getIdentifierType(std::string const& name) const; + + // Override base class methods. + virtual bool containsBooleanIdentifier(std::string const& name) const override; + virtual bool containsIntegerIdentifier(std::string const& name) const override; + virtual bool containsDoubleIdentifier(std::string const& name) const override; + virtual std::size_t getNumberOfIdentifiers() const override; + virtual std::set<std::string> getIdentifiers() const override; + virtual std::set<std::string> getBooleanIdentifiers() const override; + virtual std::set<std::string> getIntegerIdentifiers() const override; + virtual std::set<std::string> getDoubleIdentifiers() const override; + virtual bool getBooleanValue(std::string const& name) const override; + virtual int_fast64_t getIntegerValue(std::string const& name) const override; + virtual double getDoubleValue(std::string const& name) const override; + + friend std::ostream& operator<<(std::ostream& stream, SimpleValuation const& valuation); + + private: + // A mapping of boolean identifiers to their local indices in the value container. + boost::container::flat_map<std::string, boost::variant<bool, int_fast64_t, double>> identifierToValueMap; + }; + + /*! + * A helper class that can pe used as the hash functor for data structures that need to hash a simple valuations + * given via pointers. + */ + class SimpleValuationPointerHash { + public: + std::size_t operator()(SimpleValuation* valuation) const; + }; + + /*! + * A helper class that can be used as the comparison functor wrt. equality for data structures that need to + * store pointers to a simple valuations and need to compare the elements wrt. their content (rather than + * pointer equality). + */ + class SimpleValuationPointerCompare { + public: + bool operator()(SimpleValuation* valuation1, SimpleValuation* valuation2) const; + }; + + /*! + * A helper class that can be used as the comparison functor wrt. "<" for data structures that need to + * store pointers to a simple valuations and need to compare the elements wrt. their content (rather than + * pointer equality). + */ + class SimpleValuationPointerLess { + public: + bool operator()(SimpleValuation* valuation1, SimpleValuation* valuation2) const; + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_SIMPLEVALUATION_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/SubstitutionVisitor.cpp b/src/storage/expressions/SubstitutionVisitor.cpp new file mode 100644 index 000000000..43aa01ab3 --- /dev/null +++ b/src/storage/expressions/SubstitutionVisitor.cpp @@ -0,0 +1,155 @@ +#include <map> +#include <unordered_map> +#include <string> + +#include "src/storage/expressions/SubstitutionVisitor.h" +#include "src/storage/expressions/Expressions.h" + +namespace storm { + namespace expressions { + template<typename MapType> + SubstitutionVisitor<MapType>::SubstitutionVisitor(MapType const& identifierToExpressionMap) : identifierToExpressionMap(identifierToExpressionMap) { + // Intentionally left empty. + } + + template<typename MapType> + Expression SubstitutionVisitor<MapType>::substitute(Expression const& expression) { + expression.getBaseExpression().accept(this); + return Expression(this->expressionStack.top()); + } + + template<typename MapType> + void SubstitutionVisitor<MapType>::visit(IfThenElseExpression const* expression) { + expression->getCondition()->accept(this); + std::shared_ptr<BaseExpression const> conditionExpression = expressionStack.top(); + expressionStack.pop(); + + expression->getThenExpression()->accept(this); + std::shared_ptr<BaseExpression const> thenExpression = expressionStack.top(); + expressionStack.pop(); + + expression->getElseExpression()->accept(this); + std::shared_ptr<BaseExpression const> elseExpression = expressionStack.top(); + expressionStack.pop(); + + // If the arguments did not change, we simply push the expression itself. + if (conditionExpression.get() == expression->getCondition().get() && thenExpression.get() == expression->getThenExpression().get() && elseExpression.get() == expression->getElseExpression().get()) { + this->expressionStack.push(expression->getSharedPointer()); + } else { + this->expressionStack.push(std::shared_ptr<BaseExpression>(new IfThenElseExpression(expression->getReturnType(), conditionExpression, thenExpression, elseExpression))); + } + } + + template<typename MapType> + void SubstitutionVisitor<MapType>::visit(BinaryBooleanFunctionExpression const* expression) { + expression->getFirstOperand()->accept(this); + std::shared_ptr<BaseExpression const> firstExpression = expressionStack.top(); + expressionStack.pop(); + + expression->getSecondOperand()->accept(this); + std::shared_ptr<BaseExpression const> secondExpression = expressionStack.top(); + expressionStack.pop(); + + // If the arguments did not change, we simply push the expression itself. + if (firstExpression.get() == expression->getFirstOperand().get() && secondExpression.get() == expression->getSecondOperand().get()) { + this->expressionStack.push(expression->getSharedPointer()); + } else { + this->expressionStack.push(std::shared_ptr<BaseExpression>(new BinaryBooleanFunctionExpression(expression->getReturnType(), firstExpression, secondExpression, expression->getOperatorType()))); + } + } + + template<typename MapType> + void SubstitutionVisitor<MapType>::visit(BinaryNumericalFunctionExpression const* expression) { + expression->getFirstOperand()->accept(this); + std::shared_ptr<BaseExpression const> firstExpression = expressionStack.top(); + expressionStack.pop(); + + expression->getSecondOperand()->accept(this); + std::shared_ptr<BaseExpression const> secondExpression = expressionStack.top(); + expressionStack.pop(); + + // If the arguments did not change, we simply push the expression itself. + if (firstExpression.get() == expression->getFirstOperand().get() && secondExpression.get() == expression->getSecondOperand().get()) { + this->expressionStack.push(expression->getSharedPointer()); + } else { + this->expressionStack.push(std::shared_ptr<BaseExpression>(new BinaryNumericalFunctionExpression(expression->getReturnType(), firstExpression, secondExpression, expression->getOperatorType()))); + } + } + + template<typename MapType> + void SubstitutionVisitor<MapType>::visit(BinaryRelationExpression const* expression) { + expression->getFirstOperand()->accept(this); + std::shared_ptr<BaseExpression const> firstExpression = expressionStack.top(); + expressionStack.pop(); + + expression->getSecondOperand()->accept(this); + std::shared_ptr<BaseExpression const> secondExpression = expressionStack.top(); + expressionStack.pop(); + + // If the arguments did not change, we simply push the expression itself. + if (firstExpression.get() == expression->getFirstOperand().get() && secondExpression.get() == expression->getSecondOperand().get()) { + this->expressionStack.push(expression->getSharedPointer()); + } else { + this->expressionStack.push(std::shared_ptr<BaseExpression>(new BinaryRelationExpression(expression->getReturnType(), firstExpression, secondExpression, expression->getRelationType()))); + } + } + + template<typename MapType> + void SubstitutionVisitor<MapType>::visit(VariableExpression const* expression) { + // If the variable is in the key set of the substitution, we need to replace it. + auto const& nameExpressionPair = this->identifierToExpressionMap.find(expression->getVariableName()); + if (nameExpressionPair != this->identifierToExpressionMap.end()) { + this->expressionStack.push(nameExpressionPair->second.getBaseExpressionPointer()); + } else { + this->expressionStack.push(expression->getSharedPointer()); + } + } + + template<typename MapType> + void SubstitutionVisitor<MapType>::visit(UnaryBooleanFunctionExpression const* expression) { + expression->getOperand()->accept(this); + std::shared_ptr<BaseExpression const> operandExpression = expressionStack.top(); + expressionStack.pop(); + + // If the argument did not change, we simply push the expression itself. + if (operandExpression.get() == expression->getOperand().get()) { + expressionStack.push(expression->getSharedPointer()); + } else { + expressionStack.push(std::shared_ptr<BaseExpression>(new UnaryBooleanFunctionExpression(expression->getReturnType(), operandExpression, expression->getOperatorType()))); + } + } + + template<typename MapType> + void SubstitutionVisitor<MapType>::visit(UnaryNumericalFunctionExpression const* expression) { + expression->getOperand()->accept(this); + std::shared_ptr<BaseExpression const> operandExpression = expressionStack.top(); + expressionStack.pop(); + + // If the argument did not change, we simply push the expression itself. + if (operandExpression.get() == expression->getOperand().get()) { + expressionStack.push(expression->getSharedPointer()); + } else { + expressionStack.push(std::shared_ptr<BaseExpression>(new UnaryNumericalFunctionExpression(expression->getReturnType(), operandExpression, expression->getOperatorType()))); + } + } + + template<typename MapType> + void SubstitutionVisitor<MapType>::visit(BooleanLiteralExpression const* expression) { + this->expressionStack.push(expression->getSharedPointer()); + } + + template<typename MapType> + void SubstitutionVisitor<MapType>::visit(IntegerLiteralExpression const* expression) { + this->expressionStack.push(expression->getSharedPointer()); + } + + template<typename MapType> + void SubstitutionVisitor<MapType>::visit(DoubleLiteralExpression const* expression) { + this->expressionStack.push(expression->getSharedPointer()); + } + + // Explicitly instantiate the class with map and unordered_map. + template class SubstitutionVisitor<std::map<std::string, Expression>>; + template class SubstitutionVisitor<std::unordered_map<std::string, Expression>>; + } +} diff --git a/src/storage/expressions/SubstitutionVisitor.h b/src/storage/expressions/SubstitutionVisitor.h new file mode 100644 index 000000000..0ebc0941e --- /dev/null +++ b/src/storage/expressions/SubstitutionVisitor.h @@ -0,0 +1,52 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_SUBSTITUTIONVISITOR_H_ +#define STORM_STORAGE_EXPRESSIONS_SUBSTITUTIONVISITOR_H_ + +#include <stack> + +#include "src/storage/expressions/Expression.h" +#include "src/storage/expressions/ExpressionVisitor.h" + +namespace storm { + namespace expressions { + template<typename MapType> + class SubstitutionVisitor : public ExpressionVisitor { + public: + /*! + * Creates a new substitution visitor that uses the given map to replace identifiers. + * + * @param identifierToExpressionMap A mapping from identifiers to expressions. + */ + SubstitutionVisitor(MapType const& identifierToExpressionMap); + + /*! + * Substitutes the identifiers in the given expression according to the previously given map and returns the + * resulting expression. + * + * @param expression The expression in which to substitute the identifiers. + * @return The expression in which all identifiers in the key set of the previously given mapping are + * substituted with the mapped-to expressions. + */ + Expression substitute(Expression const& expression); + + virtual void visit(IfThenElseExpression const* expression) override; + virtual void visit(BinaryBooleanFunctionExpression const* expression) override; + virtual void visit(BinaryNumericalFunctionExpression const* expression) override; + virtual void visit(BinaryRelationExpression const* expression) override; + virtual void visit(VariableExpression const* expression) override; + virtual void visit(UnaryBooleanFunctionExpression const* expression) override; + virtual void visit(UnaryNumericalFunctionExpression const* expression) override; + virtual void visit(BooleanLiteralExpression const* expression) override; + virtual void visit(IntegerLiteralExpression const* expression) override; + virtual void visit(DoubleLiteralExpression const* expression) override; + + private: + // A stack of expression used to pass the results to the higher levels. + std::stack<std::shared_ptr<BaseExpression const>> expressionStack; + + // A mapping of identifier names to expressions with which they shall be replaced. + MapType const& identifierToExpressionMap; + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_SUBSTITUTIONVISITOR_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/TypeCheckVisitor.cpp b/src/storage/expressions/TypeCheckVisitor.cpp new file mode 100644 index 000000000..5ab80e141 --- /dev/null +++ b/src/storage/expressions/TypeCheckVisitor.cpp @@ -0,0 +1,80 @@ +#include "src/storage/expressions/TypeCheckVisitor.h" +#include "src/storage/expressions/Expressions.h" + +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidTypeException.h" + +namespace storm { + namespace expressions { + template<typename MapType> + TypeCheckVisitor<MapType>::TypeCheckVisitor(MapType const& identifierToTypeMap) : identifierToTypeMap(identifierToTypeMap) { + // Intentionally left empty. + } + + template<typename MapType> + void TypeCheckVisitor<MapType>::check(Expression const& expression) { + expression.getBaseExpression().accept(this); + } + + template<typename MapType> + void TypeCheckVisitor<MapType>::visit(IfThenElseExpression const* expression) { + expression->getCondition()->accept(this); + expression->getThenExpression()->accept(this); + expression->getElseExpression()->accept(this); + } + + template<typename MapType> + void TypeCheckVisitor<MapType>::visit(BinaryBooleanFunctionExpression const* expression) { + expression->getFirstOperand()->accept(this); + expression->getSecondOperand()->accept(this); + } + + template<typename MapType> + void TypeCheckVisitor<MapType>::visit(BinaryNumericalFunctionExpression const* expression) { + expression->getFirstOperand()->accept(this); + expression->getSecondOperand()->accept(this); + } + + template<typename MapType> + void TypeCheckVisitor<MapType>::visit(BinaryRelationExpression const* expression) { + expression->getFirstOperand()->accept(this); + expression->getSecondOperand()->accept(this); + } + + template<typename MapType> + void TypeCheckVisitor<MapType>::visit(VariableExpression const* expression) { + auto identifierTypePair = this->identifierToTypeMap.find(expression->getVariableName()); + LOG_THROW(identifierTypePair != this->identifierToTypeMap.end(), storm::exceptions::InvalidArgumentException, "No type available for identifier '" << expression->getVariableName() << "'."); + LOG_THROW(identifierTypePair->second == expression->getReturnType(), storm::exceptions::InvalidTypeException, "Type mismatch for variable '" << expression->getVariableName() << "': expected '" << identifierTypePair->first << "', but found '" << expression->getReturnType() << "'."); + } + + template<typename MapType> + void TypeCheckVisitor<MapType>::visit(UnaryBooleanFunctionExpression const* expression) { + expression->getOperand()->accept(this); + } + + template<typename MapType> + void TypeCheckVisitor<MapType>::visit(UnaryNumericalFunctionExpression const* expression) { + expression->getOperand()->accept(this); + } + + template<typename MapType> + void TypeCheckVisitor<MapType>::visit(BooleanLiteralExpression const* expression) { + // Intentionally left empty. + } + + template<typename MapType> + void TypeCheckVisitor<MapType>::visit(IntegerLiteralExpression const* expression) { + // Intentionally left empty. + } + + template<typename MapType> + void TypeCheckVisitor<MapType>::visit(DoubleLiteralExpression const* expression) { + // Intentionally left empty. + } + + // Explicitly instantiate the class with map and unordered_map. + template class TypeCheckVisitor<std::map<std::string, ExpressionReturnType>>; + template class TypeCheckVisitor<std::unordered_map<std::string, ExpressionReturnType>>; + } +} \ No newline at end of file diff --git a/src/storage/expressions/TypeCheckVisitor.h b/src/storage/expressions/TypeCheckVisitor.h new file mode 100644 index 000000000..0cbf40f92 --- /dev/null +++ b/src/storage/expressions/TypeCheckVisitor.h @@ -0,0 +1,47 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_TYPECHECKVISITOR_H_ +#define STORM_STORAGE_EXPRESSIONS_TYPECHECKVISITOR_H_ + +#include <stack> + +#include "src/storage/expressions/Expression.h" +#include "src/storage/expressions/ExpressionVisitor.h" + +namespace storm { + namespace expressions { + template<typename MapType> + class TypeCheckVisitor : public ExpressionVisitor { + public: + /*! + * Creates a new type check visitor that uses the given map to check the types of variables and constants. + * + * @param identifierToTypeMap A mapping from identifiers to expressions. + */ + TypeCheckVisitor(MapType const& identifierToTypeMap); + + /*! + * Checks that the types of the identifiers in the given expression match the ones in the previously given + * map. + * + * @param expression The expression in which to check the types. + */ + void check(Expression const& expression); + + virtual void visit(IfThenElseExpression const* expression) override; + virtual void visit(BinaryBooleanFunctionExpression const* expression) override; + virtual void visit(BinaryNumericalFunctionExpression const* expression) override; + virtual void visit(BinaryRelationExpression const* expression) override; + virtual void visit(VariableExpression const* expression) override; + virtual void visit(UnaryBooleanFunctionExpression const* expression) override; + virtual void visit(UnaryNumericalFunctionExpression const* expression) override; + virtual void visit(BooleanLiteralExpression const* expression) override; + virtual void visit(IntegerLiteralExpression const* expression) override; + virtual void visit(DoubleLiteralExpression const* expression) override; + + private: + // A mapping of identifier names to expressions with which they shall be replaced. + MapType const& identifierToTypeMap; + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_TYPECHECKVISITOR_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/UnaryBooleanFunctionExpression.cpp b/src/storage/expressions/UnaryBooleanFunctionExpression.cpp new file mode 100644 index 000000000..1d9e59119 --- /dev/null +++ b/src/storage/expressions/UnaryBooleanFunctionExpression.cpp @@ -0,0 +1,56 @@ +#include "src/storage/expressions/UnaryBooleanFunctionExpression.h" +#include "src/storage/expressions/BooleanLiteralExpression.h" +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidTypeException.h" + +namespace storm { + namespace expressions { + UnaryBooleanFunctionExpression::UnaryBooleanFunctionExpression(ExpressionReturnType returnType, std::shared_ptr<BaseExpression const> const& operand, OperatorType operatorType) : UnaryExpression(returnType, operand), operatorType(operatorType) { + // Intentionally left empty. + } + + UnaryBooleanFunctionExpression::OperatorType UnaryBooleanFunctionExpression::getOperatorType() const { + return this->operatorType; + } + + storm::expressions::OperatorType UnaryBooleanFunctionExpression::getOperator() const { + switch (this->getOperatorType()) { + case OperatorType::Not: return storm::expressions::OperatorType::Not; + } + } + + bool UnaryBooleanFunctionExpression::evaluateAsBool(Valuation const* valuation) const { + LOG_THROW(this->hasBooleanReturnType(), storm::exceptions::InvalidTypeException, "Unable to evaluate expression as boolean."); + + bool operandEvaluated = this->getOperand()->evaluateAsBool(valuation); + switch (this->getOperatorType()) { + case OperatorType::Not: return !operandEvaluated; break; + } + } + + std::shared_ptr<BaseExpression const> UnaryBooleanFunctionExpression::simplify() const { + std::shared_ptr<BaseExpression const> operandSimplified = this->getOperand()->simplify(); + switch (this->getOperatorType()) { + case OperatorType::Not: if (operandSimplified->isTrue()) { + return std::shared_ptr<BaseExpression>(new BooleanLiteralExpression(false)); + } else { + return std::shared_ptr<BaseExpression>(new BooleanLiteralExpression(true)); + } + } + + if (operandSimplified.get() == this->getOperand().get()) { + return this->shared_from_this(); + } else { + return std::shared_ptr<BaseExpression>(new UnaryBooleanFunctionExpression(this->getReturnType(), operandSimplified, this->getOperatorType())); + } + } + + void UnaryBooleanFunctionExpression::accept(ExpressionVisitor* visitor) const { + visitor->visit(this); + } + + void UnaryBooleanFunctionExpression::printToStream(std::ostream& stream) const { + stream << "!(" << *this->getOperand() << ")"; + } + } +} \ No newline at end of file diff --git a/src/storage/expressions/UnaryBooleanFunctionExpression.h b/src/storage/expressions/UnaryBooleanFunctionExpression.h new file mode 100644 index 000000000..c69c6f886 --- /dev/null +++ b/src/storage/expressions/UnaryBooleanFunctionExpression.h @@ -0,0 +1,58 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_UNARYBOOLEANFUNCTIONEXPRESSION_H_ +#define STORM_STORAGE_EXPRESSIONS_UNARYBOOLEANFUNCTIONEXPRESSION_H_ + +#include "src/storage/expressions/UnaryExpression.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace expressions { + class UnaryBooleanFunctionExpression : public UnaryExpression { + public: + /*! + * An enum type specifying the different functions applicable. + */ + enum class OperatorType { Not }; + + /*! + * Creates a unary boolean function expression with the given return type, operand and operator. + * + * @param returnType The return type of the expression. + * @param operand The operand of the expression. + * @param operatorType The operator of the expression. + */ + UnaryBooleanFunctionExpression(ExpressionReturnType returnType, std::shared_ptr<BaseExpression const> const& operand, OperatorType operatorType); + + // Instantiate constructors and assignments with their default implementations. + UnaryBooleanFunctionExpression(UnaryBooleanFunctionExpression const& other) = default; + UnaryBooleanFunctionExpression& operator=(UnaryBooleanFunctionExpression const& other) = default; +#ifndef WINDOWS + UnaryBooleanFunctionExpression(UnaryBooleanFunctionExpression&&) = default; + UnaryBooleanFunctionExpression& operator=(UnaryBooleanFunctionExpression&&) = default; +#endif + virtual ~UnaryBooleanFunctionExpression() = default; + + // Override base class methods. + virtual storm::expressions::OperatorType getOperator() const override; + virtual bool evaluateAsBool(Valuation const* valuation = nullptr) const override; + virtual std::shared_ptr<BaseExpression const> simplify() const override; + virtual void accept(ExpressionVisitor* visitor) const override; + + /*! + * Retrieves the operator associated with this expression. + * + * @return The operator associated with this expression. + */ + OperatorType getOperatorType() const; + + protected: + // Override base class method. + virtual void printToStream(std::ostream& stream) const override; + + private: + // The operator of this expression. + OperatorType operatorType; + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_UNARYBOOLEANFUNCTIONEXPRESSION_H_ */ diff --git a/src/storage/expressions/UnaryExpression.cpp b/src/storage/expressions/UnaryExpression.cpp new file mode 100644 index 000000000..5d8262766 --- /dev/null +++ b/src/storage/expressions/UnaryExpression.cpp @@ -0,0 +1,37 @@ +#include "src/storage/expressions/UnaryExpression.h" + +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidAccessException.h" + +namespace storm { + namespace expressions { + UnaryExpression::UnaryExpression(ExpressionReturnType returnType, std::shared_ptr<BaseExpression const> const& operand) : BaseExpression(returnType), operand(operand) { + // Intentionally left empty. + } + + bool UnaryExpression::isFunctionApplication() const { + return true; + } + + bool UnaryExpression::containsVariables() const { + return this->getOperand()->containsVariables(); + } + + std::set<std::string> UnaryExpression::getVariables() const { + return this->getOperand()->getVariables(); + } + + std::shared_ptr<BaseExpression const> const& UnaryExpression::getOperand() const { + return this->operand; + } + + uint_fast64_t UnaryExpression::getArity() const { + return 1; + } + + std::shared_ptr<BaseExpression const> UnaryExpression::getOperand(uint_fast64_t operandIndex) const { + LOG_THROW(operandIndex == 0, storm::exceptions::InvalidAccessException, "Unable to access operand " << operandIndex << " in expression of arity 2."); + return this->getOperand(); + } + } +} \ No newline at end of file diff --git a/src/storage/expressions/UnaryExpression.h b/src/storage/expressions/UnaryExpression.h new file mode 100644 index 000000000..a387ad8d5 --- /dev/null +++ b/src/storage/expressions/UnaryExpression.h @@ -0,0 +1,49 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_UNARYEXPRESSION_H_ +#define STORM_STORAGE_EXPRESSIONS_UNARYEXPRESSION_H_ + +#include "src/storage/expressions/BaseExpression.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace expressions { + class UnaryExpression : public BaseExpression { + public: + /*! + * Creates a unary expression with the given return type and operand. + * + * @param returnType The return type of the expression. + * @param operand The operand of the unary expression. + */ + UnaryExpression(ExpressionReturnType returnType, std::shared_ptr<BaseExpression const> const& operand); + + // Instantiate constructors and assignments with their default implementations. + UnaryExpression(UnaryExpression const& other); + UnaryExpression& operator=(UnaryExpression const& other); +#ifndef WINDOWS + UnaryExpression(UnaryExpression&&) = default; + UnaryExpression& operator=(UnaryExpression&&) = default; +#endif + virtual ~UnaryExpression() = default; + + // Override base class methods. + virtual bool isFunctionApplication() const override; + virtual bool containsVariables() const override; + virtual uint_fast64_t getArity() const override; + virtual std::shared_ptr<BaseExpression const> getOperand(uint_fast64_t operandIndex) const override; + virtual std::set<std::string> getVariables() const override; + + /*! + * Retrieves the operand of the unary expression. + * + * @return The operand of the unary expression. + */ + std::shared_ptr<BaseExpression const> const& getOperand() const; + + private: + // The operand of the unary expression. + std::shared_ptr<BaseExpression const> operand; + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_UNARYEXPRESSION_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/UnaryNumericalFunctionExpression.cpp b/src/storage/expressions/UnaryNumericalFunctionExpression.cpp new file mode 100644 index 000000000..972f2a24a --- /dev/null +++ b/src/storage/expressions/UnaryNumericalFunctionExpression.cpp @@ -0,0 +1,70 @@ +#include <cmath> + +#include "src/storage/expressions/UnaryNumericalFunctionExpression.h" +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidTypeException.h" + +namespace storm { + namespace expressions { + UnaryNumericalFunctionExpression::UnaryNumericalFunctionExpression(ExpressionReturnType returnType, std::shared_ptr<BaseExpression const> const& operand, OperatorType operatorType) : UnaryExpression(returnType, operand), operatorType(operatorType) { + // Intentionally left empty. + } + + UnaryNumericalFunctionExpression::OperatorType UnaryNumericalFunctionExpression::getOperatorType() const { + return this->operatorType; + } + + storm::expressions::OperatorType UnaryNumericalFunctionExpression::getOperator() const { + switch (this->getOperatorType()) { + case OperatorType::Minus: return storm::expressions::OperatorType::Minus; break; + case OperatorType::Floor: return storm::expressions::OperatorType::Floor; break; + case OperatorType::Ceil: return storm::expressions::OperatorType::Ceil; break; + } + } + + int_fast64_t UnaryNumericalFunctionExpression::evaluateAsInt(Valuation const* valuation) const { + LOG_THROW(this->hasIntegralReturnType(), storm::exceptions::InvalidTypeException, "Unable to evaluate expression as integer."); + + int_fast64_t operandEvaluated = this->getOperand()->evaluateAsInt(valuation); + switch (this->getOperatorType()) { + case OperatorType::Minus: return -operandEvaluated; break; + case OperatorType::Floor: return std::floor(operandEvaluated); break; + case OperatorType::Ceil: return std::ceil(operandEvaluated); break; + } + } + + double UnaryNumericalFunctionExpression::evaluateAsDouble(Valuation const* valuation) const { + LOG_THROW(this->hasNumericalReturnType(), storm::exceptions::InvalidTypeException, "Unable to evaluate expression as double."); + + double operandEvaluated = this->getOperand()->evaluateAsDouble(valuation); + switch (this->getOperatorType()) { + case OperatorType::Minus: return -operandEvaluated; break; + case OperatorType::Floor: return std::floor(operandEvaluated); break; + case OperatorType::Ceil: return std::ceil(operandEvaluated); break; + } + } + + std::shared_ptr<BaseExpression const> UnaryNumericalFunctionExpression::simplify() const { + std::shared_ptr<BaseExpression const> operandSimplified = this->getOperand()->simplify(); + + if (operandSimplified.get() == this->getOperand().get()) { + return this->shared_from_this(); + } else { + return std::shared_ptr<BaseExpression>(new UnaryNumericalFunctionExpression(this->getReturnType(), operandSimplified, this->getOperatorType())); + } + } + + void UnaryNumericalFunctionExpression::accept(ExpressionVisitor* visitor) const { + visitor->visit(this); + } + + void UnaryNumericalFunctionExpression::printToStream(std::ostream& stream) const { + switch (this->getOperatorType()) { + case OperatorType::Minus: stream << "-("; break; + case OperatorType::Floor: stream << "floor("; break; + case OperatorType::Ceil: stream << "ceil("; break; + } + stream << *this->getOperand() << ")"; + } + } +} \ No newline at end of file diff --git a/src/storage/expressions/UnaryNumericalFunctionExpression.h b/src/storage/expressions/UnaryNumericalFunctionExpression.h new file mode 100644 index 000000000..10b85e63e --- /dev/null +++ b/src/storage/expressions/UnaryNumericalFunctionExpression.h @@ -0,0 +1,59 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_UNARYNUMERICALFUNCTIONEXPRESSION_H_ +#define STORM_STORAGE_EXPRESSIONS_UNARYNUMERICALFUNCTIONEXPRESSION_H_ + +#include "src/storage/expressions/UnaryExpression.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace expressions { + class UnaryNumericalFunctionExpression : public UnaryExpression { + public: + /*! + * An enum type specifying the different functions applicable. + */ + enum class OperatorType {Minus, Floor, Ceil}; + + /*! + * Creates a unary numerical function expression with the given return type, operand and operator. + * + * @param returnType The return type of the expression. + * @param operand The operand of the expression. + * @param operatorType The operator of the expression. + */ + UnaryNumericalFunctionExpression(ExpressionReturnType returnType, std::shared_ptr<BaseExpression const> const& operand, OperatorType operatorType); + + // Instantiate constructors and assignments with their default implementations. + UnaryNumericalFunctionExpression(UnaryNumericalFunctionExpression const& other) = default; + UnaryNumericalFunctionExpression& operator=(UnaryNumericalFunctionExpression const& other) = default; +#ifndef WINDOWS + UnaryNumericalFunctionExpression(UnaryNumericalFunctionExpression&&) = default; + UnaryNumericalFunctionExpression& operator=(UnaryNumericalFunctionExpression&&) = default; +#endif + virtual ~UnaryNumericalFunctionExpression() = default; + + // Override base class methods. + virtual storm::expressions::OperatorType getOperator() const override; + virtual int_fast64_t evaluateAsInt(Valuation const* valuation = nullptr) const override; + virtual double evaluateAsDouble(Valuation const* valuation = nullptr) const override; + virtual std::shared_ptr<BaseExpression const> simplify() const override; + virtual void accept(ExpressionVisitor* visitor) const override; + + /*! + * Retrieves the operator associated with this expression. + * + * @return The operator associated with this expression. + */ + OperatorType getOperatorType() const; + + protected: + // Override base class method. + virtual void printToStream(std::ostream& stream) const override; + + private: + // The operator of this expression. + OperatorType operatorType; + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_UNARYNUMERICALFUNCTIONEXPRESSION_H_ */ diff --git a/src/storage/expressions/Valuation.h b/src/storage/expressions/Valuation.h new file mode 100644 index 000000000..a09c4f07c --- /dev/null +++ b/src/storage/expressions/Valuation.h @@ -0,0 +1,102 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_VALUATION_H_ +#define STORM_STORAGE_EXPRESSIONS_VALUATION_H_ + +#include <string> +#include <set> + +namespace storm { + namespace expressions { + /*! + * The base class of all valuations where a valuation assigns a concrete value to all identifiers. This is, for + * example, used for evaluating expressions. + */ + class Valuation { + public: + /*! + * Retrieves the boolean value of the identifier with the given name. + * + * @param name The name of the boolean identifier whose value to retrieve. + * @return The value of the boolean identifier. + */ + virtual bool getBooleanValue(std::string const& name) const = 0; + + /*! + * Retrieves the integer value of the identifier with the given name. + * + * @param name The name of the integer identifier whose value to retrieve. + * @return The value of the integer identifier. + */ + virtual int_fast64_t getIntegerValue(std::string const& name) const = 0; + + /*! + * Retrieves the double value of the identifier with the given name. + * + * @param name The name of the double identifier whose value to retrieve. + * @return The value of the double identifier. + */ + virtual double getDoubleValue(std::string const& name) const = 0; + + /*! + * Retrieves whether there exists a boolean identifier with the given name in the valuation. + * + * @param name The name of the boolean identifier to query. + * @return True iff the identifier exists and is of boolean type. + */ + virtual bool containsBooleanIdentifier(std::string const& name) const = 0; + + /*! + * Retrieves whether there exists a integer identifier with the given name in the valuation. + * + * @param name The name of the integer identifier to query. + * @return True iff the identifier exists and is of boolean type. + */ + virtual bool containsIntegerIdentifier(std::string const& name) const = 0; + + /*! + * Retrieves whether there exists a double identifier with the given name in the valuation. + * + * @param name The name of the double identifier to query. + * @return True iff the identifier exists and is of boolean type. + */ + virtual bool containsDoubleIdentifier(std::string const& name) const = 0; + + /*! + * Retrieves the number of identifiers in this valuation. + * + * @return The number of identifiers in this valuation. + */ + virtual std::size_t getNumberOfIdentifiers() const = 0; + + /*! + * Retrieves the set of all identifiers contained in this valuation. + * + * @return The set of all identifiers contained in this valuation. + */ + virtual std::set<std::string> getIdentifiers() const = 0; + + /*! + * Retrieves the set of boolean identifiers contained in this valuation. + * + * @return The set of boolean identifiers contained in this valuation. + */ + virtual std::set<std::string> getBooleanIdentifiers() const = 0; + + /*! + * Retrieves the set of integer identifiers contained in this valuation. + * + * @return The set of integer identifiers contained in this valuation. + */ + virtual std::set<std::string> getIntegerIdentifiers() const = 0; + + /*! + * Retrieves the set of double identifiers contained in this valuation. + * + * @return The set of double identifiers contained in this valuation. + */ + virtual std::set<std::string> getDoubleIdentifiers() const = 0; + + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_VALUATION_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/VariableExpression.cpp b/src/storage/expressions/VariableExpression.cpp new file mode 100644 index 000000000..2062c58ee --- /dev/null +++ b/src/storage/expressions/VariableExpression.cpp @@ -0,0 +1,68 @@ +#include "src/storage/expressions/VariableExpression.h" +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidTypeException.h" + +namespace storm { + namespace expressions { + VariableExpression::VariableExpression(ExpressionReturnType returnType, std::string const& variableName) : BaseExpression(returnType), variableName(variableName) { + // Intentionally left empty. + } + + std::string const& VariableExpression::getVariableName() const { + return this->variableName; + } + + bool VariableExpression::evaluateAsBool(Valuation const* valuation) const { + LOG_ASSERT(valuation != nullptr, "Evaluating expressions with unknowns without valuation."); + LOG_THROW(this->hasBooleanReturnType(), storm::exceptions::InvalidTypeException, "Cannot evaluate expression as boolean: return type is not a boolean."); + + return valuation->getBooleanValue(this->getVariableName()); + } + + int_fast64_t VariableExpression::evaluateAsInt(Valuation const* valuation) const { + LOG_ASSERT(valuation != nullptr, "Evaluating expressions with unknowns without valuation."); + LOG_THROW(this->hasIntegralReturnType(), storm::exceptions::InvalidTypeException, "Cannot evaluate expression as integer: return type is not an integer."); + + return valuation->getIntegerValue(this->getVariableName()); + } + + double VariableExpression::evaluateAsDouble(Valuation const* valuation) const { + LOG_ASSERT(valuation != nullptr, "Evaluating expressions with unknowns without valuation."); + LOG_THROW(this->hasNumericalReturnType(), storm::exceptions::InvalidTypeException, "Cannot evaluate expression as double: return type is not a double."); + + switch (this->getReturnType()) { + case ExpressionReturnType::Int: return static_cast<double>(valuation->getIntegerValue(this->getVariableName())); break; + case ExpressionReturnType::Double: valuation->getDoubleValue(this->getVariableName()); break; + default: break; + } + LOG_ASSERT(false, "Type of variable is required to be numeric."); + + // Silence warning. This point can never be reached. + return 0; + } + + std::string const& VariableExpression::getIdentifier() const { + return this->getVariableName(); + } + + bool VariableExpression::containsVariables() const { + return true; + } + + std::set<std::string> VariableExpression::getVariables() const { + return {this->getVariableName()}; + } + + std::shared_ptr<BaseExpression const> VariableExpression::simplify() const { + return this->shared_from_this(); + } + + void VariableExpression::accept(ExpressionVisitor* visitor) const { + visitor->visit(this); + } + + void VariableExpression::printToStream(std::ostream& stream) const { + stream << this->getVariableName(); + } + } +} \ No newline at end of file diff --git a/src/storage/expressions/VariableExpression.h b/src/storage/expressions/VariableExpression.h new file mode 100644 index 000000000..6eff8c794 --- /dev/null +++ b/src/storage/expressions/VariableExpression.h @@ -0,0 +1,56 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_VARIABLEEXPRESSION_H_ +#define STORM_STORAGE_EXPRESSIONS_VARIABLEEXPRESSION_H_ + +#include "src/storage/expressions/BaseExpression.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace expressions { + class VariableExpression : public BaseExpression { + public: + /*! + * Creates a variable expression with the given return type and variable name. + * + * @param returnType The return type of the variable expression. + * @param variableName The name of the variable associated with this expression. + */ + VariableExpression(ExpressionReturnType returnType, std::string const& variableName); + + // Instantiate constructors and assignments with their default implementations. + VariableExpression(VariableExpression const&) = default; + VariableExpression& operator=(VariableExpression const&) = default; +#ifndef WINDOWS + VariableExpression(VariableExpression&&) = default; + VariableExpression& operator=(VariableExpression&&) = default; +#endif + virtual ~VariableExpression() = default; + + // Override base class methods. + virtual bool evaluateAsBool(Valuation const* valuation = nullptr) const override; + virtual int_fast64_t evaluateAsInt(Valuation const* valuation = nullptr) const override; + virtual double evaluateAsDouble(Valuation const* valuation = nullptr) const override; + virtual std::string const& getIdentifier() const override; + virtual bool containsVariables() const override; + virtual std::set<std::string> getVariables() const override; + virtual std::shared_ptr<BaseExpression const> simplify() const override; + virtual void accept(ExpressionVisitor* visitor) const override; + + /*! + * Retrieves the name of the variable associated with this expression. + * + * @return The name of the variable. + */ + std::string const& getVariableName() const; + + protected: + // Override base class method. + virtual void printToStream(std::ostream& stream) const override; + + private: + // The variable name associated with this expression. + std::string variableName; + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_VARIABLEEXPRESSION_H_ */ \ No newline at end of file diff --git a/src/storage/prism/Assignment.cpp b/src/storage/prism/Assignment.cpp new file mode 100644 index 000000000..3b63703ed --- /dev/null +++ b/src/storage/prism/Assignment.cpp @@ -0,0 +1,27 @@ +#include "Assignment.h" + +namespace storm { + namespace prism { + Assignment::Assignment(std::string const& variableName, storm::expressions::Expression const& expression, std::string const& filename, uint_fast64_t lineNumber) : LocatedInformation(filename, lineNumber), variableName(variableName), expression(expression) { + // Intentionally left empty. + } + + std::string const& Assignment::getVariableName() const { + return variableName; + } + + storm::expressions::Expression const& Assignment::getExpression() const { + return this->expression; + } + + Assignment Assignment::substitute(std::map<std::string, storm::expressions::Expression> const& substitution) const { + return Assignment(this->getVariableName(), this->getExpression().substitute(substitution), this->getFilename(), this->getLineNumber()); + } + + std::ostream& operator<<(std::ostream& stream, Assignment const& assignment) { + stream << "(" << assignment.getVariableName() << "' = " << assignment.getExpression() << ")"; + return stream; + } + + } // namespace prism +} // namespace storm diff --git a/src/storage/prism/Assignment.h b/src/storage/prism/Assignment.h new file mode 100644 index 000000000..c6604a03e --- /dev/null +++ b/src/storage/prism/Assignment.h @@ -0,0 +1,67 @@ +#ifndef STORM_STORAGE_PRISM_ASSIGNMENT_H_ +#define STORM_STORAGE_PRISM_ASSIGNMENT_H_ + +#include <map> + +#include "src/storage/prism/LocatedInformation.h" +#include "src/storage/expressions/Expression.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace prism { + class Assignment : public LocatedInformation { + public: + /*! + * Constructs an assignment using the given variable name and expression. + * + * @param variableName The variable that this assignment targets. + * @param expression The expression to assign to the variable. + * @param filename The filename in which the assignment is defined. + * @param lineNumber The line number in which the assignment is defined. + */ + Assignment(std::string const& variableName, storm::expressions::Expression const& expression, std::string const& filename = "", uint_fast64_t lineNumber = 0); + + // Create default implementations of constructors/assignment. + Assignment() = default; + Assignment(Assignment const& other) = default; + Assignment& operator=(Assignment const& other)= default; +#ifndef WINDOWS + Assignment(Assignment&& other) = default; + Assignment& operator=(Assignment&& other) = default; +#endif + + /*! + * Retrieves the name of the variable that this assignment targets. + * + * @return The name of the variable that this assignment targets. + */ + std::string const& getVariableName() const; + + /*! + * Retrieves the expression that is assigned to the variable. + * + * @return The expression that is assigned to the variable. + */ + storm::expressions::Expression const& getExpression() const; + + /*! + * Substitutes all identifiers in the assignment according to the given map. + * + * @param substitution The substitution to perform. + * @return The resulting assignment. + */ + Assignment substitute(std::map<std::string, storm::expressions::Expression> const& substitution) const; + + friend std::ostream& operator<<(std::ostream& stream, Assignment const& assignment); + + private: + // The name of the variable that this assignment targets. + std::string variableName; + + // The expression that is assigned to the variable. + storm::expressions::Expression expression; + }; + } // namespace ir +} // namespace storm + +#endif /* STORM_STORAGE_PRISM_ASSIGNMENT_H_ */ diff --git a/src/storage/prism/BooleanVariable.cpp b/src/storage/prism/BooleanVariable.cpp new file mode 100644 index 000000000..ef88cefc4 --- /dev/null +++ b/src/storage/prism/BooleanVariable.cpp @@ -0,0 +1,23 @@ +#include "src/storage/prism/BooleanVariable.h" + +namespace storm { + namespace prism { + BooleanVariable::BooleanVariable(std::string const& variableName, std::string const& filename, uint_fast64_t lineNumber) : Variable(variableName, storm::expressions::Expression::createFalse(), true, filename, lineNumber) { + // Nothing to do here. + } + + BooleanVariable::BooleanVariable(std::string const& variableName, storm::expressions::Expression const& initialValueExpression, std::string const& filename, uint_fast64_t lineNumber) : Variable(variableName, initialValueExpression, false, filename, lineNumber) { + // Nothing to do here. + } + + BooleanVariable BooleanVariable::substitute(std::map<std::string, storm::expressions::Expression> const& substitution) const { + return BooleanVariable(this->getName(), this->getInitialValueExpression().substitute(substitution), this->getFilename(), this->getLineNumber()); + } + + std::ostream& operator<<(std::ostream& stream, BooleanVariable const& variable) { + stream << variable.getName() << ": bool " << variable.getInitialValueExpression() << ";"; + return stream; + } + + } // namespace prism +} // namespace storm diff --git a/src/storage/prism/BooleanVariable.h b/src/storage/prism/BooleanVariable.h new file mode 100644 index 000000000..f32035010 --- /dev/null +++ b/src/storage/prism/BooleanVariable.h @@ -0,0 +1,55 @@ +#ifndef STORM_STORAGE_PRISM_BOOLEANVARIABLE_H_ +#define STORM_STORAGE_PRISM_BOOLEANVARIABLE_H_ + +#include <map> + +#include "src/storage/prism/Variable.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace prism { + class BooleanVariable : public Variable { + public: + // Create default implementations of constructors/assignment. + BooleanVariable() = default; + BooleanVariable(BooleanVariable const& other) = default; + BooleanVariable& operator=(BooleanVariable const& other)= default; +#ifndef WINDOWS + BooleanVariable(BooleanVariable&& other) = default; + BooleanVariable& operator=(BooleanVariable&& other) = default; +#endif + + /*! + * Creates a boolean variable with the given name and the default initial value expression. + * + * @param variableName The name of the variable. + * @param filename The filename in which the variable is defined. + * @param lineNumber The line number in which the variable is defined. + */ + BooleanVariable(std::string const& variableName, std::string const& filename = "", uint_fast64_t lineNumber = 0); + + /*! + * Creates a boolean variable with the given name and the given constant initial value expression. + * + * @param variableName The name of the variable. + * @param initialValueExpression The constant expression that defines the initial value of the variable. + * @param filename The filename in which the variable is defined. + * @param lineNumber The line number in which the variable is defined. + */ + BooleanVariable(std::string const& variableName, storm::expressions::Expression const& initialValueExpression, std::string const& filename = "", uint_fast64_t lineNumber = 0); + + /*! + * Substitutes all identifiers in the boolean variable according to the given map. + * + * @param substitution The substitution to perform. + * @return The resulting boolean variable. + */ + BooleanVariable substitute(std::map<std::string, storm::expressions::Expression> const& substitution) const; + + friend std::ostream& operator<<(std::ostream& stream, BooleanVariable const& variable); + }; + + } // namespace prism +} // namespace storm + +#endif /* STORM_STORAGE_PRISM_BOOLEANVARIABLE_H_ */ diff --git a/src/storage/prism/Command.cpp b/src/storage/prism/Command.cpp new file mode 100644 index 000000000..4c1619732 --- /dev/null +++ b/src/storage/prism/Command.cpp @@ -0,0 +1,55 @@ +#include "Command.h" + +namespace storm { + namespace prism { + Command::Command(uint_fast64_t globalIndex, std::string const& actionName, storm::expressions::Expression const& guardExpression, std::vector<storm::prism::Update> const& updates, std::string const& filename, uint_fast64_t lineNumber) : LocatedInformation(filename, lineNumber), actionName(actionName), guardExpression(guardExpression), updates(updates), globalIndex(globalIndex) { + // Nothing to do here. + } + + std::string const& Command::getActionName() const { + return this->actionName; + } + + storm::expressions::Expression const& Command::getGuardExpression() const { + return guardExpression; + } + + std::size_t Command::getNumberOfUpdates() const { + return this->updates.size(); + } + + storm::prism::Update const& Command::getUpdate(uint_fast64_t index) const { + return this->updates[index]; + } + + std::vector<storm::prism::Update> const& Command::getUpdates() const { + return this->updates; + } + + uint_fast64_t Command::getGlobalIndex() const { + return this->globalIndex; + } + + Command Command::substitute(std::map<std::string, storm::expressions::Expression> const& substitution) const { + std::vector<Update> newUpdates; + newUpdates.reserve(this->getNumberOfUpdates()); + for (auto const& update : this->getUpdates()) { + newUpdates.emplace_back(update.substitute(substitution)); + } + + return Command(this->getGlobalIndex(), this->getActionName(), this->getGuardExpression().substitute(substitution), newUpdates, this->getFilename(), this->getLineNumber()); + } + + std::ostream& operator<<(std::ostream& stream, Command const& command) { + stream << "[" << command.getActionName() << "] " << command.getGuardExpression() << " -> "; + for (uint_fast64_t i = 0; i < command.getUpdates().size(); ++i) { + stream << command.getUpdate(i); + if (i < command.getUpdates().size() - 1) { + stream << " + "; + } + } + stream << ";"; + return stream; + } + } // namespace prism +} // namespace storm diff --git a/src/storage/prism/Command.h b/src/storage/prism/Command.h new file mode 100644 index 000000000..6e32eba5e --- /dev/null +++ b/src/storage/prism/Command.h @@ -0,0 +1,106 @@ +#ifndef STORM_STORAGE_PRISM_COMMAND_H_ +#define STORM_STORAGE_PRISM_COMMAND_H_ + +#include <vector> +#include <string> +#include <map> + +#include "src/storage/expressions/Expression.h" +#include "src/storage/prism/Update.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace prism { + class Command : public LocatedInformation { + public: + /*! + * Creates a command with the given action name, guard and updates. + * + * @param globalIndex The global index of the command. + * @param actionName The action name of the command. + * @param guardExpression the expression that defines the guard of the command. + * @param updates A list of updates that is associated with this command. + * @param filename The filename in which the command is defined. + * @param lineNumber The line number in which the command is defined. + */ + Command(uint_fast64_t globalIndex, std::string const& actionName, storm::expressions::Expression const& guardExpression, std::vector<storm::prism::Update> const& updates, std::string const& filename = "", uint_fast64_t lineNumber = 0); + + // Create default implementations of constructors/assignment. + Command() = default; + Command(Command const& other) = default; + Command& operator=(Command const& other)= default; +#ifndef WINDOWS + Command(Command&& other) = default; + Command& operator=(Command&& other) = default; +#endif + + /*! + * Retrieves the action name of this command. + * + * @return The action name of this command. + */ + std::string const& getActionName() const; + + /*! + * Retrieves a reference to the guard of the command. + * + * @return A reference to the guard of the command. + */ + storm::expressions::Expression const& getGuardExpression() const; + + /*! + * Retrieves the number of updates associated with this command. + * + * @return The number of updates associated with this command. + */ + std::size_t getNumberOfUpdates() const; + + /*! + * Retrieves a reference to the update with the given index. + * + * @return A reference to the update with the given index. + */ + storm::prism::Update const& getUpdate(uint_fast64_t index) const; + + /*! + * Retrieves a vector of all updates associated with this command. + * + * @return A vector of updates associated with this command. + */ + std::vector<storm::prism::Update> const& getUpdates() const; + + /*! + * Retrieves the global index of the command, that is, a unique index over all modules. + * + * @return The global index of the command. + */ + uint_fast64_t getGlobalIndex() const; + + /*! + * Substitutes all identifiers in the command according to the given map. + * + * @param substitution The substitution to perform. + * @return The resulting command. + */ + Command substitute(std::map<std::string, storm::expressions::Expression> const& substitution) const; + + friend std::ostream& operator<<(std::ostream& stream, Command const& command); + + private: + // The name of the command. + std::string actionName; + + // The expression that defines the guard of the command. + storm::expressions::Expression guardExpression; + + // The list of updates of the command. + std::vector<storm::prism::Update> updates; + + // The global index of the command. + uint_fast64_t globalIndex; + }; + + } // namespace prism +} // namespace storm + +#endif /* STORM_STORAGE_PRISM_COMMAND_H_ */ diff --git a/src/storage/prism/Constant.cpp b/src/storage/prism/Constant.cpp new file mode 100644 index 000000000..c02deea99 --- /dev/null +++ b/src/storage/prism/Constant.cpp @@ -0,0 +1,52 @@ +#include "src/storage/prism/Constant.h" +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/IllegalFunctionCallException.h" + +namespace storm { + namespace prism { + Constant::Constant(storm::expressions::ExpressionReturnType type, std::string const& name, storm::expressions::Expression const& expression, std::string const& filename, uint_fast64_t lineNumber) : LocatedInformation(filename, lineNumber), type(type), name(name), defined(true), expression(expression) { + // Intentionally left empty. + } + + Constant::Constant(storm::expressions::ExpressionReturnType type, std::string const& name, std::string const& filename, uint_fast64_t lineNumber) : LocatedInformation(filename, lineNumber), type(type), name(name), defined(false), expression() { + // Intentionally left empty. + } + + std::string const& Constant::getName() const { + return this->name; + } + + storm::expressions::ExpressionReturnType Constant::getType() const { + return this->type; + } + + bool Constant::isDefined() const { + return this->defined; + } + + storm::expressions::Expression const& Constant::getExpression() const { + LOG_THROW(this->isDefined(), storm::exceptions::IllegalFunctionCallException, "Unable to retrieve defining expression for undefined constant."); + return this->expression; + } + + Constant Constant::substitute(std::map<std::string, storm::expressions::Expression> const& substitution) const { + return Constant(this->getType(), this->getName(), this->getExpression().substitute(substitution), this->getFilename(), this->getLineNumber()); + } + + std::ostream& operator<<(std::ostream& stream, Constant const& constant) { + stream << "const "; + switch (constant.getType()) { + case storm::expressions::ExpressionReturnType::Undefined: stream << "undefined "; break; + case storm::expressions::ExpressionReturnType::Bool: stream << "bool "; break; + case storm::expressions::ExpressionReturnType::Int: stream << "int "; break; + case storm::expressions::ExpressionReturnType::Double: stream << "double "; break; + } + stream << constant.getName(); + if (constant.isDefined()) { + stream << " = " << constant.getExpression(); + } + stream << ";"; + return stream; + } + } // namespace prism +} // namespace storm \ No newline at end of file diff --git a/src/storage/prism/Constant.h b/src/storage/prism/Constant.h new file mode 100644 index 000000000..2e1c27590 --- /dev/null +++ b/src/storage/prism/Constant.h @@ -0,0 +1,99 @@ +#ifndef STORM_STORAGE_PRISM_CONSTANT_H_ +#define STORM_STORAGE_PRISM_CONSTANT_H_ + +#include <map> + +#include "src/storage/prism/LocatedInformation.h" +#include "src/storage/expressions/Expression.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace prism { + class Constant : public LocatedInformation { + public: + /*! + * Creates a constant with the given type, name and defining expression. + * + * @param type The type of the constant. + * @param name The name of the constant. + * @param expression The expression that defines the constant. + * @param filename The filename in which the transition reward is defined. + * @param lineNumber The line number in which the transition reward is defined. + */ + Constant(storm::expressions::ExpressionReturnType type, std::string const& name, storm::expressions::Expression const& expression, std::string const& filename = "", uint_fast64_t lineNumber = 0); + + /*! + * Creates an undefined constant with the given type and name. + * + * @param constantType The type of the constant. + * @param constantName The name of the constant. + * @param filename The filename in which the transition reward is defined. + * @param lineNumber The line number in which the transition reward is defined. + */ + Constant(storm::expressions::ExpressionReturnType constantType, std::string const& constantName, std::string const& filename = "", uint_fast64_t lineNumber = 0); + + // Create default implementations of constructors/assignment. + Constant() = default; + Constant(Constant const& other) = default; + Constant& operator=(Constant const& other)= default; +#ifndef WINDOWS + Constant(Constant&& other) = default; + Constant& operator=(Constant&& other) = default; +#endif + + /*! + * Retrieves the name of the constant. + * + * @return The name of the constant. + */ + std::string const& getName() const; + + /*! + * Retrieves the type of the constant. + * + * @return The type of the constant; + */ + storm::expressions::ExpressionReturnType getType() const; + + /*! + * Retrieves whether the constant is defined, i.e., whether there is an expression defining its value. + * + * @return True iff the constant is defined. + */ + bool isDefined() const; + + /*! + * Retrieves the expression that defines the constant. This may only be called if the object is a defined + * constant. + * + * @return The expression that defines the constant. + */ + storm::expressions::Expression const& getExpression() const; + + /*! + * Substitutes all identifiers in the constant according to the given map. + * + * @param substitution The substitution to perform. + * @return The resulting constant. + */ + Constant substitute(std::map<std::string, storm::expressions::Expression> const& substitution) const; + + friend std::ostream& operator<<(std::ostream& stream, Constant const& constant); + + private: + // The type of the constant. + storm::expressions::ExpressionReturnType type; + + // The name of the constant. + std::string name; + + // A flag that stores whether or not the constant is defined. + bool defined; + + // The expression that defines the constant (in case it is defined). + storm::expressions::Expression expression; + }; + } // namespace prism +} // namespace storm + +#endif /* STORM_STORAGE_PRISM_CONSTANT_H_ */ diff --git a/src/storage/prism/Formula.cpp b/src/storage/prism/Formula.cpp new file mode 100644 index 000000000..a120078b8 --- /dev/null +++ b/src/storage/prism/Formula.cpp @@ -0,0 +1,30 @@ +#include "src/storage/prism/Formula.h" + +namespace storm { + namespace prism { + Formula::Formula(std::string const& name, storm::expressions::Expression const& expression, std::string const& filename, uint_fast64_t lineNumber) : LocatedInformation(filename, lineNumber), name(name), expression(expression) { + // Intentionally left empty. + } + + std::string const& Formula::getName() const { + return this->name; + } + + storm::expressions::Expression const& Formula::getExpression() const { + return this->expression; + } + + storm::expressions::ExpressionReturnType Formula::getType() const { + return this->getExpression().getReturnType(); + } + + Formula Formula::substitute(std::map<std::string, storm::expressions::Expression> const& substitution) const { + return Formula(this->getName(), this->getExpression().substitute(substitution), this->getFilename(), this->getLineNumber()); + } + + std::ostream& operator<<(std::ostream& stream, Formula const& formula) { + stream << "formula " << formula.getName() << " = " << formula.getExpression() << ";"; + return stream; + } + } // namespace prism +} // namespace storm \ No newline at end of file diff --git a/src/storage/prism/Formula.h b/src/storage/prism/Formula.h new file mode 100644 index 000000000..2df8e6d32 --- /dev/null +++ b/src/storage/prism/Formula.h @@ -0,0 +1,74 @@ +#ifndef STORM_STORAGE_PRISM_FORMULA_H_ +#define STORM_STORAGE_PRISM_FORMULA_H_ + +#include <map> + +#include "src/storage/prism/LocatedInformation.h" +#include "src/storage/expressions/Expression.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace prism { + class Formula : public LocatedInformation { + public: + /*! + * Creates a formula with the given name and expression. + * + * @param name The name of the formula. + * @param expression The expression associated with this formula. + * @param filename The filename in which the transition reward is defined. + * @param lineNumber The line number in which the transition reward is defined. + */ + Formula(std::string const& name, storm::expressions::Expression const& expression, std::string const& filename = "", uint_fast64_t lineNumber = 0); + + // Create default implementations of constructors/assignment. + Formula() = default; + Formula(Formula const& other) = default; + Formula& operator=(Formula const& other)= default; +#ifndef WINDOWS + Formula(Formula&& other) = default; + Formula& operator=(Formula&& other) = default; +#endif + + /*! + * Retrieves the name that is associated with this formula. + * + * @return The name that is associated with this formula. + */ + std::string const& getName() const; + + /*! + * Retrieves the expression that is associated with this formula. + * + * @return The expression that is associated with this formula. + */ + storm::expressions::Expression const& getExpression() const; + + /*! + * Retrieves the return type of the formula, i.e., the return-type of the defining expression. + * + * @return The return type of the formula. + */ + storm::expressions::ExpressionReturnType getType() const; + + /*! + * Substitutes all identifiers in the expression of the formula according to the given map. + * + * @param substitution The substitution to perform. + * @return The resulting formula. + */ + Formula substitute(std::map<std::string, storm::expressions::Expression> const& substitution) const; + + friend std::ostream& operator<<(std::ostream& stream, Formula const& formula); + + private: + // The name of the formula. + std::string name; + + // A predicate that needs to be satisfied by states for the label to be attached. + storm::expressions::Expression expression; + }; + } // namespace prism +} // namespace storm + +#endif /* STORM_STORAGE_PRISM_FORMULA_H_ */ diff --git a/src/storage/prism/InitialConstruct.cpp b/src/storage/prism/InitialConstruct.cpp new file mode 100644 index 000000000..61c662843 --- /dev/null +++ b/src/storage/prism/InitialConstruct.cpp @@ -0,0 +1,24 @@ +#include "src/storage/prism/InitialConstruct.h" + +namespace storm { + namespace prism { + InitialConstruct::InitialConstruct(storm::expressions::Expression initialStatesExpression, std::string const& filename, uint_fast64_t lineNumber) : LocatedInformation(filename, lineNumber), initialStatesExpression(initialStatesExpression) { + // Intentionally left empty. + } + + storm::expressions::Expression InitialConstruct::getInitialStatesExpression() const { + return this->initialStatesExpression; + } + + InitialConstruct InitialConstruct::substitute(std::map<std::string, storm::expressions::Expression> const& substitution) const { + return InitialConstruct(this->getInitialStatesExpression().substitute(substitution)); + } + + std::ostream& operator<<(std::ostream& stream, InitialConstruct const& initialConstruct) { + stream << "initial " << std::endl; + stream << "\t" << initialConstruct.getInitialStatesExpression() << std::endl; + stream << "endinitial" << std::endl; + return stream; + } + } // namespace prism +} // namespace storm \ No newline at end of file diff --git a/src/storage/prism/InitialConstruct.h b/src/storage/prism/InitialConstruct.h new file mode 100644 index 000000000..6f7f6adb9 --- /dev/null +++ b/src/storage/prism/InitialConstruct.h @@ -0,0 +1,56 @@ +#ifndef STORM_STORAGE_PRISM_INITIALCONSTRUCT_H_ +#define STORM_STORAGE_PRISM_INITIALCONSTRUCT_H_ + +#include <string> + +#include "src/storage/prism/LocatedInformation.h" +#include "src/storage/expressions/Expression.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace prism { + class InitialConstruct : public LocatedInformation { + public: + /*! + * Creates an initial construct with the given expression. + * + * @param initialStatesExpression An expression characterizing the initial states. + * @param filename The filename in which the command is defined. + * @param lineNumber The line number in which the command is defined. + */ + InitialConstruct(storm::expressions::Expression initialStatesExpression, std::string const& filename = "", uint_fast64_t lineNumber = 0); + + // Create default implementations of constructors/assignment. + InitialConstruct() = default; + InitialConstruct(InitialConstruct const& other) = default; + InitialConstruct& operator=(InitialConstruct const& other)= default; +#ifndef WINDOWS + InitialConstruct(InitialConstruct&& other) = default; + InitialConstruct& operator=(InitialConstruct&& other) = default; +#endif + + /*! + * Retrieves the expression characterizing the initial states. + * + * @return The expression characterizing the initial states. + */ + storm::expressions::Expression getInitialStatesExpression() const; + + /*! + * Substitutes all identifiers in the constant according to the given map. + * + * @param substitution The substitution to perform. + * @return The resulting initial construct. + */ + InitialConstruct substitute(std::map<std::string, storm::expressions::Expression> const& substitution) const; + + friend std::ostream& operator<<(std::ostream& stream, InitialConstruct const& initialConstruct); + + private: + // An expression characterizing the initial states. + storm::expressions::Expression initialStatesExpression; + }; + } +} + +#endif /* STORM_STORAGE_PRISM_INITIALCONSTRUCT_H_ */ \ No newline at end of file diff --git a/src/storage/prism/IntegerVariable.cpp b/src/storage/prism/IntegerVariable.cpp new file mode 100644 index 000000000..1968a07ca --- /dev/null +++ b/src/storage/prism/IntegerVariable.cpp @@ -0,0 +1,30 @@ +#include "src/storage/prism/IntegerVariable.h" + +namespace storm { + namespace prism { + IntegerVariable::IntegerVariable(std::string const& name, storm::expressions::Expression const& lowerBoundExpression, storm::expressions::Expression const& upperBoundExpression, std::string const& filename, uint_fast64_t lineNumber) : Variable(name, lowerBoundExpression, true, filename, lineNumber), lowerBoundExpression(lowerBoundExpression), upperBoundExpression(upperBoundExpression) { + // Intentionally left empty. + } + + IntegerVariable::IntegerVariable(std::string const& name, storm::expressions::Expression const& lowerBoundExpression, storm::expressions::Expression const& upperBoundExpression, storm::expressions::Expression const& initialValueExpression, std::string const& filename, uint_fast64_t lineNumber) : Variable(name, initialValueExpression, false, filename, lineNumber), lowerBoundExpression(lowerBoundExpression), upperBoundExpression(upperBoundExpression) { + // Intentionally left empty. + } + + storm::expressions::Expression const& IntegerVariable::getLowerBoundExpression() const { + return this->lowerBoundExpression; + } + + storm::expressions::Expression const& IntegerVariable::getUpperBoundExpression() const { + return this->upperBoundExpression; + } + + IntegerVariable IntegerVariable::substitute(std::map<std::string, storm::expressions::Expression> const& substitution) const { + return IntegerVariable(this->getName(), this->getLowerBoundExpression().substitute(substitution), this->getUpperBoundExpression().substitute(substitution), this->getInitialValueExpression().substitute(substitution), this->getFilename(), this->getLineNumber()); + } + + std::ostream& operator<<(std::ostream& stream, IntegerVariable const& variable) { + stream << variable.getName() << ": [" << variable.getLowerBoundExpression() << ".." << variable.getUpperBoundExpression() << "]" << " init " << variable.getInitialValueExpression() << ";"; + return stream; + } + } // namespace prism +} // namespace storm diff --git a/src/storage/prism/IntegerVariable.h b/src/storage/prism/IntegerVariable.h new file mode 100644 index 000000000..a7c069c53 --- /dev/null +++ b/src/storage/prism/IntegerVariable.h @@ -0,0 +1,80 @@ +#ifndef STORM_STORAGE_PRISM_INTEGERVARIABLE_H_ +#define STORM_STORAGE_PRISM_INTEGERVARIABLE_H_ + +#include <map> + +#include "src/storage/prism/Variable.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace prism { + class IntegerVariable : public Variable { + public: + // Create default implementations of constructors/assignment. + IntegerVariable() = default; + IntegerVariable(IntegerVariable const& other) = default; + IntegerVariable& operator=(IntegerVariable const& other)= default; +#ifndef WINDOWS + IntegerVariable(IntegerVariable&& other) = default; + IntegerVariable& operator=(IntegerVariable&& other) = default; +#endif + + /*! + * Creates an integer variable with the given name and a default initial value. + * + * @param name The name of the variable. + * @param lowerBoundExpression A constant expression defining the lower bound of the domain of the variable. + * @param upperBoundExpression A constant expression defining the upper bound of the domain of the variable. + * @param filename The filename in which the variable is defined. + * @param lineNumber The line number in which the variable is defined. + */ + IntegerVariable(std::string const& name, storm::expressions::Expression const& lowerBoundExpression, storm::expressions::Expression const& upperBoundExpression, std::string const& filename = "", uint_fast64_t lineNumber = 0); + + /*! + * Creates an integer variable with the given name and the given initial value expression. + * + * @param name The name of the variable. + * @param lowerBoundExpression A constant expression defining the lower bound of the domain of the variable. + * @param upperBoundExpression A constant expression defining the upper bound of the domain of the variable. + * @param initialValueExpression A constant expression that defines the initial value of the variable. + * @param filename The filename in which the variable is defined. + * @param lineNumber The line number in which the variable is defined. + */ + IntegerVariable(std::string const& name, storm::expressions::Expression const& lowerBoundExpression, storm::expressions::Expression const& upperBoundExpression, storm::expressions::Expression const& initialValueExpression, std::string const& filename = "", uint_fast64_t lineNumber = 0); + + /*! + * Retrieves an expression defining the lower bound for this integer variable. + * + * @return An expression defining the lower bound for this integer variable. + */ + storm::expressions::Expression const& getLowerBoundExpression() const; + + /*! + * Retrieves an expression defining the upper bound for this integer variable. + * + * @return An expression defining the upper bound for this integer variable. + */ + storm::expressions::Expression const& getUpperBoundExpression() const; + + /*! + * Substitutes all identifiers in the boolean variable according to the given map. + * + * @param substitution The substitution to perform. + * @return The resulting boolean variable. + */ + IntegerVariable substitute(std::map<std::string, storm::expressions::Expression> const& substitution) const; + + friend std::ostream& operator<<(std::ostream& stream, IntegerVariable const& variable); + + private: + // A constant expression that specifies the lower bound of the domain of the variable. + storm::expressions::Expression lowerBoundExpression; + + // A constant expression that specifies the upper bound of the domain of the variable. + storm::expressions::Expression upperBoundExpression; + }; + + } // namespace prism +} // namespace storm + +#endif /* STORM_STORAGE_PRISM_INTEGERVARIABLE_H_ */ diff --git a/src/storage/prism/Label.cpp b/src/storage/prism/Label.cpp new file mode 100644 index 000000000..d11091cf0 --- /dev/null +++ b/src/storage/prism/Label.cpp @@ -0,0 +1,26 @@ +#include "src/storage/prism/Label.h" + +namespace storm { + namespace prism { + Label::Label(std::string const& name, storm::expressions::Expression const& statePredicateExpression, std::string const& filename, uint_fast64_t lineNumber) : LocatedInformation(filename, lineNumber), name(name), statePredicateExpression(statePredicateExpression) { + // Intentionally left empty. + } + + std::string const& Label::getName() const { + return this->name; + } + + storm::expressions::Expression const& Label::getStatePredicateExpression() const { + return this->statePredicateExpression; + } + + Label Label::substitute(std::map<std::string, storm::expressions::Expression> const& substitution) const { + return Label(this->getName(), this->getStatePredicateExpression().substitute(substitution), this->getFilename(), this->getLineNumber()); + } + + std::ostream& operator<<(std::ostream& stream, Label const& label) { + stream << "label \"" << label.getName() << "\" = " << label.getStatePredicateExpression() << ";"; + return stream; + } + } // namespace prism +} // namespace storm \ No newline at end of file diff --git a/src/storage/prism/Label.h b/src/storage/prism/Label.h new file mode 100644 index 000000000..96934adf2 --- /dev/null +++ b/src/storage/prism/Label.h @@ -0,0 +1,68 @@ +#ifndef STORM_STORAGE_PRISM_LABEL_H_ +#define STORM_STORAGE_PRISM_LABEL_H_ + +#include <map> + +#include "src/storage/prism/LocatedInformation.h" +#include "src/storage/expressions/Expression.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace prism { + class Label : public LocatedInformation { + public: + /*! + * Creates a label with the given name and state predicate expression. + * + * @param name The name of the label. + * @param statePredicateExpression The predicate that needs to hold before taking a transition with the previously + * specified name in order to obtain the reward. + * @param filename The filename in which the transition reward is defined. + * @param lineNumber The line number in which the transition reward is defined. + */ + Label(std::string const& name, storm::expressions::Expression const& statePredicateExpression, std::string const& filename = "", uint_fast64_t lineNumber = 0); + + // Create default implementations of constructors/assignment. + Label() = default; + Label(Label const& other) = default; + Label& operator=(Label const& other)= default; +#ifndef WINDOWS + Label(Label&& other) = default; + Label& operator=(Label&& other) = default; +#endif + + /*! + * Retrieves the name that is associated with this label. + * + * @return The name that is associated with this label. + */ + std::string const& getName() const; + + /*! + * Retrieves the state predicate expression that is associated with this label. + * + * @return The state predicate expression that is associated with this label. + */ + storm::expressions::Expression const& getStatePredicateExpression() const; + + /*! + * Substitutes all identifiers in the expression of the label according to the given map. + * + * @param substitution The substitution to perform. + * @return The resulting label. + */ + Label substitute(std::map<std::string, storm::expressions::Expression> const& substitution) const; + + friend std::ostream& operator<<(std::ostream& stream, Label const& label); + + private: + // The name of the label. + std::string name; + + // A predicate that needs to be satisfied by states for the label to be attached. + storm::expressions::Expression statePredicateExpression; + }; + } // namespace prism +} // namespace storm + +#endif /* STORM_STORAGE_PRISM_LABEL_H_ */ diff --git a/src/storage/prism/LocatedInformation.cpp b/src/storage/prism/LocatedInformation.cpp new file mode 100644 index 000000000..a3862d57a --- /dev/null +++ b/src/storage/prism/LocatedInformation.cpp @@ -0,0 +1,25 @@ +#include "src/storage/prism/LocatedInformation.h" + +namespace storm { + namespace prism { + LocatedInformation::LocatedInformation(std::string const& filename, uint_fast64_t lineNumber) : filename(filename), lineNumber(lineNumber) { + // Intentionally left empty. + } + + std::string const& LocatedInformation::getFilename() const { + return this->filename; + } + + void LocatedInformation::setFilename(std::string const& filename) { + this->filename = filename; + } + + uint_fast64_t LocatedInformation::getLineNumber() const { + return this->lineNumber; + } + + void LocatedInformation::setLineNumber(uint_fast64_t lineNumber) { + this->lineNumber = lineNumber; + } + } // namespace prism +} // namespace storm \ No newline at end of file diff --git a/src/storage/prism/LocatedInformation.h b/src/storage/prism/LocatedInformation.h new file mode 100644 index 000000000..81f5334b5 --- /dev/null +++ b/src/storage/prism/LocatedInformation.h @@ -0,0 +1,68 @@ +#ifndef STORM_STORAGE_PRISM_LOCATEDINFORMATION_H_ +#define STORM_STORAGE_PRISM_LOCATEDINFORMATION_H_ + +#include <string> +#include <cstdint> + +#include "src/utility/OsDetection.h" + +namespace storm { + namespace prism { + class LocatedInformation { + public: + /*! + * Constructs a located information with the given filename and line number. + * + * @param filename The file in which the information was found. + * @param lineNumber The line number in which the information was found. + */ + LocatedInformation(std::string const& filename, uint_fast64_t lineNumber); + + // Create default implementations of constructors/assignment. + LocatedInformation() = default; + LocatedInformation(LocatedInformation const& other) = default; + LocatedInformation& operator=(LocatedInformation const& other)= default; +#ifndef WINDOWS + LocatedInformation(LocatedInformation&& other) = default; + LocatedInformation& operator=(LocatedInformation&& other) = default; +#endif + + /*! + * Retrieves the name of the file in which the information was found. + * + * @return The name of the file in which the information was found. + */ + std::string const& getFilename() const; + + /*! + * Sets the filename of this information. + * + * @param filename The new filename of this information. + */ + void setFilename(std::string const& filename); + + /*! + * Retrieves the line number in which the information was found. + * + * @return The line number in which the information was found. + */ + uint_fast64_t getLineNumber() const; + + /*! + * Sets the line number of this information. + * + * @param lineNumber The new line number for this information. + */ + void setLineNumber(uint_fast64_t lineNumber); + + private: + // The file in which the piece of information was found. + std::string filename; + + // The line in the file in which the piece of information was found. + uint_fast64_t lineNumber; + }; + } +} + +#endif /* STORM_STORAGE_PRISM_LOCATEDINFORMATION_H_ */ \ No newline at end of file diff --git a/src/storage/prism/Module.cpp b/src/storage/prism/Module.cpp new file mode 100644 index 000000000..6f4becb0a --- /dev/null +++ b/src/storage/prism/Module.cpp @@ -0,0 +1,187 @@ +#include "src/storage/prism/Module.h" +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/OutOfRangeException.h" +#include "src/exceptions/InvalidArgumentException.h" +#include "src/exceptions/InvalidAccessException.h" + +namespace storm { + namespace prism { + Module::Module(std::string const& moduleName, std::vector<storm::prism::BooleanVariable> const& booleanVariables, std::vector<storm::prism::IntegerVariable> const& integerVariables, std::vector<storm::prism::Command> const& commands, std::string const& filename, uint_fast64_t lineNumber) : Module(moduleName, booleanVariables, integerVariables, commands, "", std::map<std::string, std::string>(), filename, lineNumber) { + // Intentionally left empty. + } + + Module::Module(std::string const& moduleName, std::vector<storm::prism::BooleanVariable> const& booleanVariables, std::vector<storm::prism::IntegerVariable> const& integerVariables, std::vector<storm::prism::Command> const& commands, std::string const& renamedFromModule, std::map<std::string, std::string> const& renaming, std::string const& filename, uint_fast64_t lineNumber) : LocatedInformation(filename, lineNumber), moduleName(moduleName), booleanVariables(booleanVariables), booleanVariableToIndexMap(), integerVariables(integerVariables), integerVariableToIndexMap(), commands(commands), actions(), actionsToCommandIndexMap(), renamedFromModule(renamedFromModule), renaming(renaming) { + // Initialize the internal mappings for fast information retrieval. + this->createMappings(); + } + + std::size_t Module::getNumberOfBooleanVariables() const { + return this->booleanVariables.size(); + } + + std::size_t Module::getNumberOfIntegerVariables() const { + return this->integerVariables.size(); + } + + storm::prism::BooleanVariable const& Module::getBooleanVariable(std::string const& variableName) const { + auto const& nameIndexPair = this->booleanVariableToIndexMap.find(variableName); + LOG_THROW(nameIndexPair != this->booleanVariableToIndexMap.end(), storm::exceptions::InvalidArgumentException, "Unknown boolean variable '" << variableName << "'."); + return this->getBooleanVariables()[nameIndexPair->second]; + } + + std::vector<storm::prism::BooleanVariable> const& Module::getBooleanVariables() const { + return this->booleanVariables; + } + + storm::prism::IntegerVariable const& Module::getIntegerVariable(std::string const& variableName) const { + auto const& nameIndexPair = this->integerVariableToIndexMap.find(variableName); + LOG_THROW(nameIndexPair != this->integerVariableToIndexMap.end(), storm::exceptions::InvalidArgumentException, "Unknown integer variable '" << variableName << "'."); + return this->getIntegerVariables()[nameIndexPair->second]; + } + + std::vector<storm::prism::IntegerVariable> const& Module::getIntegerVariables() const { + return this->integerVariables; + } + + std::size_t Module::getNumberOfCommands() const { + return this->commands.size(); + } + + std::size_t Module::getNumberOfUpdates() const { + std::size_t result = 0; + for (auto const& command : this->getCommands()) { + result += command.getNumberOfUpdates(); + } + return result; + } + + storm::prism::Command const& Module::getCommand(uint_fast64_t index) const { + return this->commands[index]; + } + + std::vector<storm::prism::Command> const& Module::getCommands() const { + return this->commands; + } + + std::string const& Module::getName() const { + return this->moduleName; + } + + std::set<std::string> const& Module::getActions() const { + return this->actions; + } + + bool Module::hasAction(std::string const& action) const { + auto const& actionEntry = this->actions.find(action); + return actionEntry != this->actions.end(); + } + + bool Module::isRenamedFromModule() const { + return this->renamedFromModule != ""; + } + + std::string const& Module::getBaseModule() const { + LOG_THROW(this->isRenamedFromModule(), storm::exceptions::InvalidAccessException, "Unable to retrieve base module of module that was not created by renaming."); + return this->renamedFromModule; + } + + std::map<std::string, std::string> const& Module::getRenaming() const { + LOG_THROW(this->isRenamedFromModule(), storm::exceptions::InvalidAccessException, "Unable to retrieve renaming of module that was not created by renaming."); + return this->renaming; + } + + std::set<uint_fast64_t> const& Module::getCommandIndicesByAction(std::string const& action) const { + auto actionsCommandSetPair = this->actionsToCommandIndexMap.find(action); + if (actionsCommandSetPair != this->actionsToCommandIndexMap.end()) { + return actionsCommandSetPair->second; + } + + LOG_THROW(false, storm::exceptions::OutOfRangeException, "Action name '" << action << "' does not exist in module."); + } + + void Module::createMappings() { + // Clear the current mappings. + this->actionsToCommandIndexMap.clear(); + this->booleanVariableToIndexMap.clear(); + this->integerVariableToIndexMap.clear(); + + // Create the mappings for the variables. + for (uint_fast64_t i = 0; i < this->booleanVariables.size(); ++i) { + this->booleanVariableToIndexMap[this->getBooleanVariables()[i].getName()] = i; + } + for (uint_fast64_t i = 0; i < this->integerVariables.size(); ++i) { + this->integerVariableToIndexMap[this->getIntegerVariables()[i].getName()] = i; + } + + // Add the mapping for all commands. + for (uint_fast64_t i = 0; i < this->commands.size(); i++) { + std::string const& action = this->commands[i].getActionName(); + if (action != "") { + if (this->actionsToCommandIndexMap.find(action) == this->actionsToCommandIndexMap.end()) { + this->actionsToCommandIndexMap.emplace(action, std::set<uint_fast64_t>()); + } + this->actionsToCommandIndexMap[action].insert(i); + this->actions.insert(action); + } + } + + // For all actions that are "in the module", but for which no command exists, we add the mapping to an empty + // set of commands. + for (auto const& action : this->actions) { + if (this->actionsToCommandIndexMap.find(action) == this->actionsToCommandIndexMap.end()) { + this->actionsToCommandIndexMap[action] = std::set<uint_fast64_t>(); + } + } + } + + Module Module::restrictCommands(boost::container::flat_set<uint_fast64_t> const& indexSet) const { + // First construct the new vector of commands. + std::vector<storm::prism::Command> newCommands; + for (auto const& command : commands) { + if (indexSet.find(command.getGlobalIndex()) != indexSet.end()) { + newCommands.push_back(command); + } + } + + return Module(this->getName(), this->getBooleanVariables(), this->getIntegerVariables(), newCommands); + } + + Module Module::substitute(std::map<std::string, storm::expressions::Expression> const& substitution) const { + std::vector<BooleanVariable> newBooleanVariables; + newBooleanVariables.reserve(this->getNumberOfBooleanVariables()); + for (auto const& booleanVariable : this->getBooleanVariables()) { + newBooleanVariables.emplace_back(booleanVariable.substitute(substitution)); + } + + std::vector<IntegerVariable> newIntegerVariables; + newBooleanVariables.reserve(this->getNumberOfIntegerVariables()); + for (auto const& integerVariable : this->getIntegerVariables()) { + newIntegerVariables.emplace_back(integerVariable.substitute(substitution)); + } + + std::vector<Command> newCommands; + newCommands.reserve(this->getNumberOfCommands()); + for (auto const& command : this->getCommands()) { + newCommands.emplace_back(command.substitute(substitution)); + } + + return Module(this->getName(), newBooleanVariables, newIntegerVariables, newCommands, this->getFilename(), this->getLineNumber()); + } + + std::ostream& operator<<(std::ostream& stream, Module const& module) { + stream << "module " << module.getName() << std::endl; + for (auto const& booleanVariable : module.getBooleanVariables()) { + stream << "\t" << booleanVariable << std::endl; + } + for (auto const& integerVariable : module.getIntegerVariables()) { + stream << "\t" << integerVariable << std::endl; + } + for (auto const& command : module.getCommands()) { + stream << "\t" << command << std::endl; + } + stream << "endmodule" << std::endl; + return stream; + } + + } // namespace prism +} // namespace storm diff --git a/src/storage/prism/Module.h b/src/storage/prism/Module.h new file mode 100644 index 000000000..1031cbf27 --- /dev/null +++ b/src/storage/prism/Module.h @@ -0,0 +1,241 @@ +#ifndef STORM_STORAGE_PRISM_MODULE_H_ +#define STORM_STORAGE_PRISM_MODULE_H_ + +#include <set> +#include <map> +#include <string> +#include <vector> +#include <memory> +#include <boost/container/flat_set.hpp> + +#include "src/storage/prism/BooleanVariable.h" +#include "src/storage/prism/IntegerVariable.h" +#include "src/storage/prism/Command.h" +#include "src/storage/expressions/VariableExpression.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace prism { + class Module : public LocatedInformation { + public: + /*! + * Creates a module with the given name, variables and commands. + * + * @param moduleName The name of the module. + * @param booleanVariables The boolean variables defined by the module. + * @param integerVariables The integer variables defined by the module. + * @param commands The commands of the module. + * @param filename The filename in which the module is defined. + * @param lineNumber The line number in which the module is defined. + */ + Module(std::string const& moduleName, std::vector<storm::prism::BooleanVariable> const& booleanVariables, std::vector<storm::prism::IntegerVariable> const& integerVariables, std::vector<storm::prism::Command> const& commands, std::string const& filename = "", uint_fast64_t lineNumber = 0); + + /*! + * Creates a module with the given name, variables and commands that is marked as being renamed from the + * given module with the given renaming. + * + * @param moduleName The name of the module. + * @param booleanVariables The boolean variables defined by the module. + * @param integerVariables The integer variables defined by the module. + * @param commands The commands of the module. + * @param renamedFromModule The name of the module from which this module was renamed. + * @param renaming The renaming of identifiers used to create this module. + * @param filename The filename in which the module is defined. + * @param lineNumber The line number in which the module is defined. + */ + Module(std::string const& moduleName, std::vector<storm::prism::BooleanVariable> const& booleanVariables, std::vector<storm::prism::IntegerVariable> const& integerVariables, std::vector<storm::prism::Command> const& commands, std::string const& renamedFromModule, std::map<std::string, std::string> const& renaming, std::string const& filename = "", uint_fast64_t lineNumber = 0); + + // Create default implementations of constructors/assignment. + Module() = default; + Module(Module const& other) = default; + Module& operator=(Module const& other)= default; +#ifndef WINDOWS + Module(Module&& other) = default; + Module& operator=(Module&& other) = default; +#endif + + /*! + * Retrieves the number of boolean variables in the module. + * + * @return the number of boolean variables in the module. + */ + std::size_t getNumberOfBooleanVariables() const; + + /*! + * Retrieves the number of integer variables in the module. + * + * @return The number of integer variables in the module. + */ + std::size_t getNumberOfIntegerVariables() const; + + /*! + * Retrieves a reference to the boolean variable with the given name. + * + * @param variableName The name of the boolean variable to retrieve. + * @return A reference to the boolean variable with the given name. + */ + storm::prism::BooleanVariable const& getBooleanVariable(std::string const& variableName) const; + + /*! + * Retrieves the boolean variables of the module. + * + * @return The boolean variables of the module. + */ + std::vector<storm::prism::BooleanVariable> const& getBooleanVariables() const; + + /*! + * Retrieves a reference to the integer variable with the given name. + * + * @param variableName The name of the integer variable to retrieve. + * @return A reference to the integer variable with the given name. + */ + storm::prism::IntegerVariable const& getIntegerVariable(std::string const& variableName) const; + + /*! + * Retrieves the integer variables of the module. + * + * @return The integer variables of the module. + */ + std::vector<storm::prism::IntegerVariable> const& getIntegerVariables() const; + + /*! + * Retrieves the number of commands of this module. + * + * @return The number of commands of this module. + */ + std::size_t getNumberOfCommands() const; + + /*! + * Retrieves the total number of updates of this module. + * + * @return The total number of updates of this module. + */ + std::size_t getNumberOfUpdates() const; + + /*! + * Retrieves a reference to the command with the given index. + * + * @param index The index of the command to retrieve. + * @return A reference to the command with the given index. + */ + storm::prism::Command const& getCommand(uint_fast64_t index) const; + + /*! + * Retrieves the commands of the module. + * + * @return The commands of the module. + */ + std::vector<storm::prism::Command> const& getCommands() const; + + /*! + * Retrieves the name of the module. + * + * @return The name of the module. + */ + std::string const& getName() const; + + /*! + * Retrieves the set of actions present in this module. + * + * @return the set of actions present in this module. + */ + std::set<std::string> const& getActions() const; + + /*! + * Retrieves whether or not this module contains a command labeled with the given action. + * + * @param action The action name to look for in this module. + * @return True iff the module has at least one command labeled with the given action. + */ + bool hasAction(std::string const& action) const; + + /*! + * Retrieves whether this module was created from another module via renaming. + * + * @return True iff the module was created via renaming. + */ + bool isRenamedFromModule() const; + + /*! + * If the module was created via renaming, this method retrieves the name of the module that was used as the + * in the base in the renaming process. + * + * @return The name of the module from which this module was created via renaming. + */ + std::string const& getBaseModule() const; + + /*! + * If the module was created via renaming, this method returns the applied renaming of identifiers used for + * the renaming process. + * + * @return A mapping of identifiers to new identifiers that was used in the renaming process. + */ + std::map<std::string, std::string> const& getRenaming() const; + + /*! + * Retrieves the indices of all commands within this module that are labelled by the given action. + * + * @param action The action with which the commands have to be labelled. + * @return A set of indices of commands that are labelled with the given action. + */ + std::set<uint_fast64_t> const& getCommandIndicesByAction(std::string const& action) const; + + /*! + * Creates a new module that drops all commands whose indices are not in the given set. + * + * @param indexSet The set of indices for which to keep the commands. + * @return The module resulting from erasing all commands whose indices are not in the given set. + */ + Module restrictCommands(boost::container::flat_set<uint_fast64_t> const& indexSet) const; + + /*! + * Substitutes all identifiers in the module according to the given map. + * + * @param substitution The substitution to perform. + * @return The resulting module. + */ + Module substitute(std::map<std::string, storm::expressions::Expression> const& substitution) const; + + friend std::ostream& operator<<(std::ostream& stream, Module const& module); + + private: + /*! + * Computes the locally maintained mappings for fast data retrieval. + */ + void createMappings(); + + // The name of the module. + std::string moduleName; + + // A list of boolean variables. + std::vector<storm::prism::BooleanVariable> booleanVariables; + + // A mapping from boolean variables to the corresponding indices in the vector. + std::map<std::string, uint_fast64_t> booleanVariableToIndexMap; + + // A list of integer variables. + std::vector<storm::prism::IntegerVariable> integerVariables; + + // A mapping from integer variables to the corresponding indices in the vector. + std::map<std::string, uint_fast64_t> integerVariableToIndexMap; + + // The commands associated with the module. + std::vector<storm::prism::Command> commands; + + // The set of actions present in this module. + std::set<std::string> actions; + + // A map of actions to the set of commands labeled with this action. + std::map<std::string, std::set<uint_fast64_t>> actionsToCommandIndexMap; + + // This string indicates whether and from what module this module was created via renaming. + std::string renamedFromModule; + + // If the module was created by renaming, this mapping contains the provided renaming of identifiers. + std::map<std::string, std::string> renaming; + }; + + } // namespace prism +} // namespace storm + +#endif /* STORM_STORAGE_PRISM_MODULE_H_ */ diff --git a/src/storage/prism/Program.cpp b/src/storage/prism/Program.cpp new file mode 100644 index 000000000..617a1c52b --- /dev/null +++ b/src/storage/prism/Program.cpp @@ -0,0 +1,703 @@ +#include "src/storage/prism/Program.h" + +#include <algorithm> + +#include "src/exceptions/ExceptionMacros.h" +#include "exceptions/InvalidArgumentException.h" +#include "src/exceptions/OutOfRangeException.h" +#include "src/exceptions/WrongFormatException.h" +#include "src/exceptions/InvalidTypeException.h" + +namespace storm { + namespace prism { + Program::Program(ModelType modelType, std::vector<Constant> const& constants, std::vector<BooleanVariable> const& globalBooleanVariables, std::vector<IntegerVariable> const& globalIntegerVariables, std::vector<Formula> const& formulas, std::vector<Module> const& modules, std::vector<RewardModel> const& rewardModels, bool fixInitialConstruct, storm::prism::InitialConstruct const& initialConstruct, std::vector<Label> const& labels, std::string const& filename, uint_fast64_t lineNumber, bool checkValidity) : LocatedInformation(filename, lineNumber), modelType(modelType), constants(constants), constantToIndexMap(), globalBooleanVariables(globalBooleanVariables), globalBooleanVariableToIndexMap(), globalIntegerVariables(globalIntegerVariables), globalIntegerVariableToIndexMap(), formulas(formulas), formulaToIndexMap(), modules(modules), moduleToIndexMap(), rewardModels(rewardModels), rewardModelToIndexMap(), initialConstruct(initialConstruct), labels(labels), labelToIndexMap(), actions(), actionsToModuleIndexMap(), variableToModuleIndexMap() { + this->createMappings(); + + // Create a new initial construct if the corresponding flag was set. + if (fixInitialConstruct) { + if (this->getInitialConstruct().getInitialStatesExpression().isFalse()) { + storm::expressions::Expression newInitialExpression = storm::expressions::Expression::createTrue(); + + for (auto const& booleanVariable : this->getGlobalBooleanVariables()) { + newInitialExpression = newInitialExpression && (storm::expressions::Expression::createBooleanVariable(booleanVariable.getName()).iff(booleanVariable.getInitialValueExpression())); + } + for (auto const& integerVariable : this->getGlobalIntegerVariables()) { + newInitialExpression = newInitialExpression && (storm::expressions::Expression::createIntegerVariable(integerVariable.getName()) == integerVariable.getInitialValueExpression()); + } + for (auto const& module : this->getModules()) { + for (auto const& booleanVariable : module.getBooleanVariables()) { + newInitialExpression = newInitialExpression && (storm::expressions::Expression::createBooleanVariable(booleanVariable.getName()).iff(booleanVariable.getInitialValueExpression())); + } + for (auto const& integerVariable : module.getIntegerVariables()) { + newInitialExpression = newInitialExpression && (storm::expressions::Expression::createIntegerVariable(integerVariable.getName()) == integerVariable.getInitialValueExpression()); + } + } + this->initialConstruct = storm::prism::InitialConstruct(newInitialExpression, this->getInitialConstruct().getFilename(), this->getInitialConstruct().getLineNumber()); + } + } + + if (checkValidity) { + this->checkValidity(); + } + } + + Program::ModelType Program::getModelType() const { + return modelType; + } + + bool Program::hasUndefinedConstants() const { + for (auto const& constant : this->getConstants()) { + if (!constant.isDefined()) { + return true; + } + } + return false; + } + + bool Program::hasConstant(std::string const& constantName) const { + return this->constantToIndexMap.find(constantName) != this->constantToIndexMap.end(); + } + + Constant const& Program::getConstant(std::string const& constantName) const { + auto const& constantIndexPair = this->constantToIndexMap.find(constantName); + return this->getConstants()[constantIndexPair->second]; + } + + std::vector<Constant> const& Program::getConstants() const { + return this->constants; + } + + std::size_t Program::getNumberOfConstants() const { + return this->getConstants().size(); + } + + std::vector<BooleanVariable> const& Program::getGlobalBooleanVariables() const { + return this->globalBooleanVariables; + } + + std::vector<IntegerVariable> const& Program::getGlobalIntegerVariables() const { + return this->globalIntegerVariables; + } + + BooleanVariable const& Program::getGlobalBooleanVariable(std::string const& variableName) const { + auto const& nameIndexPair = this->globalBooleanVariableToIndexMap.find(variableName); + LOG_THROW(nameIndexPair != this->globalBooleanVariableToIndexMap.end(), storm::exceptions::OutOfRangeException, "Unknown boolean variable '" << variableName << "'."); + return this->getGlobalBooleanVariables()[nameIndexPair->second]; + } + + IntegerVariable const& Program::getGlobalIntegerVariable(std::string const& variableName) const { + auto const& nameIndexPair = this->globalIntegerVariableToIndexMap.find(variableName); + LOG_THROW(nameIndexPair != this->globalIntegerVariableToIndexMap.end(), storm::exceptions::OutOfRangeException, "Unknown integer variable '" << variableName << "'."); + return this->getGlobalIntegerVariables()[nameIndexPair->second]; + } + + std::size_t Program::getNumberOfGlobalBooleanVariables() const { + return this->getGlobalBooleanVariables().size(); + } + + std::size_t Program::getNumberOfGlobalIntegerVariables() const { + return this->getGlobalIntegerVariables().size(); + } + + std::vector<Formula> const& Program::getFormulas() const { + return this->formulas; + } + + std::size_t Program::getNumberOfFormulas() const { + return this->getFormulas().size(); + } + + std::size_t Program::getNumberOfModules() const { + return this->getModules().size(); + } + + storm::prism::Module const& Program::getModule(uint_fast64_t index) const { + return this->modules[index]; + } + + Module const& Program::getModule(std::string const& moduleName) const { + auto const& nameIndexPair = this->moduleToIndexMap.find(moduleName); + LOG_THROW(nameIndexPair != this->moduleToIndexMap.end(), storm::exceptions::OutOfRangeException, "Unknown module '" << moduleName << "'."); + return this->getModules()[nameIndexPair->second]; + } + + std::vector<storm::prism::Module> const& Program::getModules() const { + return this->modules; + } + + storm::prism::InitialConstruct const& Program::getInitialConstruct() const { + return this->initialConstruct; + } + + std::set<std::string> const& Program::getActions() const { + return this->actions; + } + + std::set<uint_fast64_t> const& Program::getModuleIndicesByAction(std::string const& action) const { + auto const& actionModuleSetPair = this->actionsToModuleIndexMap.find(action); + LOG_THROW(actionModuleSetPair != this->actionsToModuleIndexMap.end(), storm::exceptions::OutOfRangeException, "Action name '" << action << "' does not exist."); + return actionModuleSetPair->second; + } + + uint_fast64_t Program::getModuleIndexByVariable(std::string const& variableName) const { + auto const& variableNameToModuleIndexPair = this->variableToModuleIndexMap.find(variableName); + LOG_THROW(variableNameToModuleIndexPair != this->variableToModuleIndexMap.end(), storm::exceptions::OutOfRangeException, "Variable '" << variableName << "' does not exist."); + return variableNameToModuleIndexPair->second; + } + + std::vector<storm::prism::RewardModel> const& Program::getRewardModels() const { + return this->rewardModels; + } + + std::size_t Program::getNumberOfRewardModels() const { + return this->getRewardModels().size(); + } + + storm::prism::RewardModel const& Program::getRewardModel(std::string const& name) const { + auto const& nameIndexPair = this->rewardModelToIndexMap.find(name); + LOG_THROW(nameIndexPair != this->rewardModelToIndexMap.end(), storm::exceptions::OutOfRangeException, "Reward model '" << name << "' does not exist."); + return this->getRewardModels()[nameIndexPair->second]; + } + + std::vector<Label> const& Program::getLabels() const { + return this->labels; + } + + std::size_t Program::getNumberOfLabels() const { + return this->getLabels().size(); + } + + Program Program::restrictCommands(boost::container::flat_set<uint_fast64_t> const& indexSet) const { + std::vector<storm::prism::Module> newModules; + newModules.reserve(this->getNumberOfModules()); + + for (auto const& module : this->getModules()) { + newModules.push_back(module.restrictCommands(indexSet)); + } + + return Program(this->getModelType(), this->getConstants(), this->getGlobalBooleanVariables(), this->getGlobalIntegerVariables(), this->getFormulas(), newModules, this->getRewardModels(), false, this->getInitialConstruct(), this->getLabels()); + } + + void Program::createMappings() { + // Build the mappings for constants, global variables, formulas, modules, reward models and labels. + for (uint_fast64_t constantIndex = 0; constantIndex < this->getNumberOfConstants(); ++constantIndex) { + this->constantToIndexMap[this->getConstants()[constantIndex].getName()] = constantIndex; + } + for (uint_fast64_t globalVariableIndex = 0; globalVariableIndex < this->getNumberOfGlobalBooleanVariables(); ++globalVariableIndex) { + this->globalBooleanVariableToIndexMap[this->getGlobalBooleanVariables()[globalVariableIndex].getName()] = globalVariableIndex; + } + for (uint_fast64_t globalVariableIndex = 0; globalVariableIndex < this->getNumberOfGlobalIntegerVariables(); ++globalVariableIndex) { + this->globalIntegerVariableToIndexMap[this->getGlobalIntegerVariables()[globalVariableIndex].getName()] = globalVariableIndex; + } + for (uint_fast64_t formulaIndex = 0; formulaIndex < this->getNumberOfFormulas(); ++formulaIndex) { + this->formulaToIndexMap[this->getFormulas()[formulaIndex].getName()] = formulaIndex; + } + for (uint_fast64_t moduleIndex = 0; moduleIndex < this->getNumberOfModules(); ++moduleIndex) { + this->moduleToIndexMap[this->getModules()[moduleIndex].getName()] = moduleIndex; + } + for (uint_fast64_t rewardModelIndex = 0; rewardModelIndex < this->getNumberOfRewardModels(); ++rewardModelIndex) { + this->rewardModelToIndexMap[this->getRewardModels()[rewardModelIndex].getName()] = rewardModelIndex; + } + for (uint_fast64_t labelIndex = 0; labelIndex < this->getNumberOfLabels(); ++labelIndex) { + this->labelToIndexMap[this->getLabels()[labelIndex].getName()] = labelIndex; + } + + // Build the mapping from action names to module indices so that the lookup can later be performed quickly. + for (unsigned int moduleIndex = 0; moduleIndex < this->getNumberOfModules(); moduleIndex++) { + Module const& module = this->getModule(moduleIndex); + + for (auto const& action : module.getActions()) { + auto const& actionModuleIndicesPair = this->actionsToModuleIndexMap.find(action); + if (actionModuleIndicesPair == this->actionsToModuleIndexMap.end()) { + this->actionsToModuleIndexMap[action] = std::set<uint_fast64_t>(); + } + this->actionsToModuleIndexMap[action].insert(moduleIndex); + this->actions.insert(action); + } + + // Put in the appropriate entries for the mapping from variable names to module index. + for (auto const& booleanVariable : module.getBooleanVariables()) { + this->variableToModuleIndexMap[booleanVariable.getName()] = moduleIndex; + } + for (auto const& integerVariable : module.getBooleanVariables()) { + this->variableToModuleIndexMap[integerVariable.getName()] = moduleIndex; + } + } + + } + + Program Program::defineUndefinedConstants(std::map<std::string, storm::expressions::Expression> const& constantDefinitions) const { + // For sanity checking, we keep track of all undefined constants that we define in the course of this + // procedure. + std::set<std::string> definedUndefinedConstants; + + std::vector<Constant> newConstants; + newConstants.reserve(this->getNumberOfConstants()); + for (auto const& constant : this->getConstants()) { + // If the constant is already defined, we need to replace the appearances of undefined constants in its + // defining expression + if (constant.isDefined()) { + // Make sure we are not trying to define an already defined constant. + LOG_THROW(constantDefinitions.find(constant.getName()) == constantDefinitions.end(), storm::exceptions::InvalidArgumentException, "Illegally defining already defined constant '" << constant.getName() << "'."); + + // Now replace the occurrences of undefined constants in its defining expression. + newConstants.emplace_back(constant.getType(), constant.getName(), constant.getExpression().substitute(constantDefinitions), constant.getFilename(), constant.getLineNumber()); + } else { + auto const& variableExpressionPair = constantDefinitions.find(constant.getName()); + + // If the constant is not defined by the mapping, we leave it like it is. + if (variableExpressionPair == constantDefinitions.end()) { + newConstants.emplace_back(constant); + } else { + // Otherwise, we add it to the defined constants and assign it the appropriate expression. + definedUndefinedConstants.insert(constant.getName()); + + // Make sure the type of the constant is correct. + LOG_THROW(variableExpressionPair->second.getReturnType() == constant.getType(), storm::exceptions::InvalidArgumentException, "Illegal type of expression defining constant '" << constant.getName() << "'."); + + // Now create the defined constant. + newConstants.emplace_back(constant.getType(), constant.getName(), variableExpressionPair->second, constant.getFilename(), constant.getLineNumber()); + } + } + } + + // As a sanity check, we make sure that the given mapping does not contain any definitions for identifiers + // that are not undefined constants. + for (auto const& constantExpressionPair : constantDefinitions) { + LOG_THROW(definedUndefinedConstants.find(constantExpressionPair.first) != definedUndefinedConstants.end(), storm::exceptions::InvalidArgumentException, "Unable to define non-existant constant."); + } + + return Program(this->getModelType(), newConstants, this->getGlobalBooleanVariables(), this->getGlobalIntegerVariables(), this->getFormulas(), this->getModules(), this->getRewardModels(), false, this->getInitialConstruct(), this->getLabels()); + } + + Program Program::substituteConstants() const { + // We start by creating the appropriate substitution + std::map<std::string, storm::expressions::Expression> constantSubstitution; + std::vector<Constant> newConstants(this->getConstants()); + for (uint_fast64_t constantIndex = 0; constantIndex < newConstants.size(); ++constantIndex) { + auto const& constant = newConstants[constantIndex]; + LOG_THROW(constant.isDefined(), storm::exceptions::InvalidArgumentException, "Cannot substitute constants in program that contains undefined constants."); + + // Put the corresponding expression in the substitution. + constantSubstitution.emplace(constant.getName(), constant.getExpression()); + + // If there is at least one more constant to come, we substitute the costants we have so far. + if (constantIndex + 1 < newConstants.size()) { + newConstants[constantIndex + 1] = newConstants[constantIndex + 1].substitute(constantSubstitution); + } + } + + // Now we can substitute the constants in all expressions appearing in the program. + std::vector<BooleanVariable> newBooleanVariables; + newBooleanVariables.reserve(this->getNumberOfGlobalBooleanVariables()); + for (auto const& booleanVariable : this->getGlobalBooleanVariables()) { + newBooleanVariables.emplace_back(booleanVariable.substitute(constantSubstitution)); + } + + std::vector<IntegerVariable> newIntegerVariables; + newBooleanVariables.reserve(this->getNumberOfGlobalIntegerVariables()); + for (auto const& integerVariable : this->getGlobalIntegerVariables()) { + newIntegerVariables.emplace_back(integerVariable.substitute(constantSubstitution)); + } + + std::vector<Formula> newFormulas; + newFormulas.reserve(this->getNumberOfFormulas()); + for (auto const& formula : this->getFormulas()) { + newFormulas.emplace_back(formula.substitute(constantSubstitution)); + } + + std::vector<Module> newModules; + newModules.reserve(this->getNumberOfModules()); + for (auto const& module : this->getModules()) { + newModules.emplace_back(module.substitute(constantSubstitution)); + } + + std::vector<RewardModel> newRewardModels; + newRewardModels.reserve(this->getNumberOfRewardModels()); + for (auto const& rewardModel : this->getRewardModels()) { + newRewardModels.emplace_back(rewardModel.substitute(constantSubstitution)); + } + + storm::prism::InitialConstruct newInitialConstruct = this->getInitialConstruct().substitute(constantSubstitution); + + std::vector<Label> newLabels; + newLabels.reserve(this->getNumberOfLabels()); + for (auto const& label : this->getLabels()) { + newLabels.emplace_back(label.substitute(constantSubstitution)); + } + + return Program(this->getModelType(), newConstants, newBooleanVariables, newIntegerVariables, newFormulas, newModules, newRewardModels, false, newInitialConstruct, newLabels); + } + + void Program::checkValidity() const { + // We need to construct a mapping from identifiers to their types, so we can type-check the expressions later. + std::map<std::string, storm::expressions::ExpressionReturnType> identifierToTypeMap; + + // Start by checking the constant declarations. + std::set<std::string> allIdentifiers; + std::set<std::string> globalIdentifiers; + std::set<std::string> constantNames; + for (auto const& constant : this->getConstants()) { + // Check for duplicate identifiers. + LOG_THROW(allIdentifiers.find(constant.getName()) == allIdentifiers.end(), storm::exceptions::WrongFormatException, "Error in " << constant.getFilename() << ", line " << constant.getLineNumber() << ": duplicate identifier '" << constant.getName() << "'."); + + // Check defining expressions of defined constants. + if (constant.isDefined()) { + std::set<std::string> containedIdentifiers = constant.getExpression().getVariables(); + bool isValid = std::includes(constantNames.begin(), constantNames.end(), containedIdentifiers.begin(), containedIdentifiers.end()); + LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << constant.getFilename() << ", line " << constant.getLineNumber() << ": defining expression refers to unknown identifiers."); + + // Now check that the constants appear with the right types. + try { + constant.getExpression().check(identifierToTypeMap); + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Error in " << constant.getFilename() << ", line " << constant.getLineNumber() << ": " << e.what()); + } + } + + // Finally, register the type of the constant for later type checks. + identifierToTypeMap.emplace(constant.getName(), constant.getType()); + + // Record the new identifier for future checks. + constantNames.insert(constant.getName()); + allIdentifiers.insert(constant.getName()); + globalIdentifiers.insert(constant.getName()); + } + + // Now we check the variable declarations. We start with the global variables. + std::set<std::string> variableNames; + for (auto const& variable : this->getGlobalBooleanVariables()) { + // Check for duplicate identifiers. + LOG_THROW(allIdentifiers.find(variable.getName()) == allIdentifiers.end(), storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": duplicate identifier '" << variable.getName() << "'."); + + // Check the initial value of the variable. + std::set<std::string> containedIdentifiers = variable.getInitialValueExpression().getVariables(); + bool isValid = std::includes(constantNames.begin(), constantNames.end(), containedIdentifiers.begin(), containedIdentifiers.end()); + LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": initial value expression refers to unknown constants."); + try { + variable.getInitialValueExpression().check(identifierToTypeMap); + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": " << e.what()); + } + + // Register the type of the constant for later type checks. + identifierToTypeMap.emplace(variable.getName(), storm::expressions::ExpressionReturnType::Bool); + + // Record the new identifier for future checks. + variableNames.insert(variable.getName()); + allIdentifiers.insert(variable.getName()); + globalIdentifiers.insert(variable.getName()); + } + for (auto const& variable : this->getGlobalIntegerVariables()) { + // Check for duplicate identifiers. + LOG_THROW(allIdentifiers.find(variable.getName()) == allIdentifiers.end(), storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": duplicate identifier '" << variable.getName() << "'."); + + // Check that bound expressions of the range. + std::set<std::string> containedIdentifiers = variable.getLowerBoundExpression().getVariables(); + bool isValid = std::includes(constantNames.begin(), constantNames.end(), containedIdentifiers.begin(), containedIdentifiers.end()); + LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": lower bound expression refers to unknown constants."); + try { + variable.getLowerBoundExpression().check(identifierToTypeMap); + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": " << e.what()); + } + + containedIdentifiers = variable.getLowerBoundExpression().getVariables(); + isValid = std::includes(constantNames.begin(), constantNames.end(), containedIdentifiers.begin(), containedIdentifiers.end()); + LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": upper bound expression refers to unknown constants."); + try { + variable.getUpperBoundExpression().check(identifierToTypeMap); + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": " << e.what()); + } + + // Check the initial value of the variable. + containedIdentifiers = variable.getInitialValueExpression().getVariables(); + isValid = std::includes(constantNames.begin(), constantNames.end(), containedIdentifiers.begin(), containedIdentifiers.end()); + LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": initial value expression refers to unknown constants."); + try { + variable.getInitialValueExpression().check(identifierToTypeMap); + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": " << e.what()); + } + + // Register the type of the constant for later type checks. + identifierToTypeMap.emplace(variable.getName(), storm::expressions::ExpressionReturnType::Int); + + // Record the new identifier for future checks. + variableNames.insert(variable.getName()); + allIdentifiers.insert(variable.getName()); + globalIdentifiers.insert(variable.getName()); + } + + // Now go through the variables of the modules. + for (auto const& module : this->getModules()) { + for (auto const& variable : module.getBooleanVariables()) { + // Check for duplicate identifiers. + LOG_THROW(allIdentifiers.find(variable.getName()) == allIdentifiers.end(), storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": duplicate identifier '" << variable.getName() << "'."); + + // Check the initial value of the variable. + std::set<std::string> containedIdentifiers = variable.getInitialValueExpression().getVariables(); + bool isValid = std::includes(constantNames.begin(), constantNames.end(), containedIdentifiers.begin(), containedIdentifiers.end()); + LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": initial value expression refers to unknown constants."); + try { + variable.getInitialValueExpression().check(identifierToTypeMap); + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": " << e.what()); + } + + // Register the type of the constant for later type checks. + identifierToTypeMap.emplace(variable.getName(), storm::expressions::ExpressionReturnType::Bool); + + // Record the new identifier for future checks. + variableNames.insert(variable.getName()); + allIdentifiers.insert(variable.getName()); + } + for (auto const& variable : module.getIntegerVariables()) { + // Check for duplicate identifiers. + LOG_THROW(allIdentifiers.find(variable.getName()) == allIdentifiers.end(), storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": duplicate identifier '" << variable.getName() << "'."); + + // Register the type of the constant for later type checks. + identifierToTypeMap.emplace(variable.getName(), storm::expressions::ExpressionReturnType::Int); + + // Check that bound expressions of the range. + std::set<std::string> containedIdentifiers = variable.getLowerBoundExpression().getVariables(); + bool isValid = std::includes(constantNames.begin(), constantNames.end(), containedIdentifiers.begin(), containedIdentifiers.end()); + LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": lower bound expression refers to unknown constants."); + try { + variable.getLowerBoundExpression().check(identifierToTypeMap); + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": " << e.what()); + } + + containedIdentifiers = variable.getLowerBoundExpression().getVariables(); + isValid = std::includes(constantNames.begin(), constantNames.end(), containedIdentifiers.begin(), containedIdentifiers.end()); + LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": upper bound expression refers to unknown constants."); + try { + variable.getUpperBoundExpression().check(identifierToTypeMap); + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": " << e.what()); + } + + // Check the initial value of the variable. + containedIdentifiers = variable.getInitialValueExpression().getVariables(); + isValid = std::includes(constantNames.begin(), constantNames.end(), containedIdentifiers.begin(), containedIdentifiers.end()); + LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": initial value expression refers to unknown constants."); + try { + variable.getInitialValueExpression().check(identifierToTypeMap); + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": " << e.what()); + } + + // Record the new identifier for future checks. + variableNames.insert(variable.getName()); + allIdentifiers.insert(variable.getName()); + } + } + + // Create the set of valid identifiers for future checks. + std::set<std::string> variablesAndConstants; + std::set_union(variableNames.begin(), variableNames.end(), constantNames.begin(), constantNames.end(), std::inserter(variablesAndConstants, variablesAndConstants.begin())); + + // Check the commands of the modules. + for (auto const& module : this->getModules()) { + std::set<std::string> legalIdentifiers = globalIdentifiers; + for (auto const& variable : module.getBooleanVariables()) { + legalIdentifiers.insert(variable.getName()); + } + for (auto const& variable : module.getIntegerVariables()) { + legalIdentifiers.insert(variable.getName()); + } + + for (auto const& command : module.getCommands()) { + // Check the guard. + std::set<std::string> containedIdentifiers = command.getGuardExpression().getVariables(); + bool isValid = std::includes(variablesAndConstants.begin(), variablesAndConstants.end(), containedIdentifiers.begin(), containedIdentifiers.end()); + LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << command.getFilename() << ", line " << command.getLineNumber() << ": guard refers to unknown identifiers."); + try { + command.getGuardExpression().check(identifierToTypeMap); + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Error in " << command.getFilename() << ", line " << command.getLineNumber() << ": " << e.what()); + } + LOG_THROW(command.getGuardExpression().hasBooleanReturnType(), storm::exceptions::WrongFormatException, "Error in " << command.getFilename() << ", line " << command.getLineNumber() << ": expression for guard must evaluate to type 'bool'."); + + // Check all updates. + for (auto const& update : command.getUpdates()) { + containedIdentifiers = update.getLikelihoodExpression().getVariables(); + isValid = std::includes(variablesAndConstants.begin(), variablesAndConstants.end(), containedIdentifiers.begin(), containedIdentifiers.end()); + LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << command.getFilename() << ", line " << command.getLineNumber() << ": likelihood expression refers to unknown identifiers."); + try { + update.getLikelihoodExpression().check(identifierToTypeMap); + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Error in " << command.getFilename() << ", line " << command.getLineNumber() << ": " << e.what()); + } + + // Check all assignments. + std::set<std::string> alreadyAssignedIdentifiers; + for (auto const& assignment : update.getAssignments()) { + if (legalIdentifiers.find(assignment.getVariableName()) == legalIdentifiers.end()) { + if (allIdentifiers.find(assignment.getVariableName()) != allIdentifiers.end()) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Error in " << command.getFilename() << ", line " << command.getLineNumber() << ": assignment illegally refers to variable '" << assignment.getVariableName() << "'."); + } else { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Error in " << command.getFilename() << ", line " << command.getLineNumber() << ": assignment refers to unknown variable '" << assignment.getVariableName() << "'."); + } + } + LOG_THROW(alreadyAssignedIdentifiers.find(assignment.getVariableName()) == alreadyAssignedIdentifiers.end(), storm::exceptions::WrongFormatException, "Error in " << command.getFilename() << ", line " << command.getLineNumber() << ": duplicate assignment to variable '" << assignment.getVariableName() << "'."); + auto variableTypePair = identifierToTypeMap.find(assignment.getVariableName()); + LOG_THROW(variableTypePair->second == assignment.getExpression().getReturnType(), storm::exceptions::WrongFormatException, "Error in " << command.getFilename() << ", line " << command.getLineNumber() << ": illegally assigning a value of type '" << assignment.getExpression().getReturnType() << "' to variable '" << variableTypePair->first << "' of type '" << variableTypePair->second << "'."); + + containedIdentifiers = assignment.getExpression().getVariables(); + isValid = std::includes(variablesAndConstants.begin(), variablesAndConstants.end(), containedIdentifiers.begin(), containedIdentifiers.end()); + LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << command.getFilename() << ", line " << command.getLineNumber() << ": likelihood expression refers to unknown identifiers."); + try { + assignment.getExpression().check(identifierToTypeMap); + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Error in " << command.getFilename() << ", line " << command.getLineNumber() << ": " << e.what()); + } + + // Add the current variable to the set of assigned variables (of this update). + alreadyAssignedIdentifiers.insert(assignment.getVariableName()); + } + } + } + } + + // Now check the reward models. + for (auto const& rewardModel : this->getRewardModels()) { + for (auto const& stateReward : rewardModel.getStateRewards()) { + std::set<std::string> containedIdentifiers = stateReward.getStatePredicateExpression().getVariables(); + bool isValid = std::includes(variablesAndConstants.begin(), variablesAndConstants.end(), containedIdentifiers.begin(), containedIdentifiers.end()); + LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << stateReward.getFilename() << ", line " << stateReward.getLineNumber() << ": state reward expression refers to unknown identifiers."); + try { + stateReward.getStatePredicateExpression().check(identifierToTypeMap); + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Error in " << stateReward.getFilename() << ", line " << stateReward.getLineNumber() << ": " << e.what()); + } + LOG_THROW(stateReward.getStatePredicateExpression().hasBooleanReturnType(), storm::exceptions::WrongFormatException, "Error in " << stateReward.getFilename() << ", line " << stateReward.getLineNumber() << ": state predicate must evaluate to type 'bool'."); + + containedIdentifiers = stateReward.getRewardValueExpression().getVariables(); + isValid = std::includes(variablesAndConstants.begin(), variablesAndConstants.end(), containedIdentifiers.begin(), containedIdentifiers.end()); + LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << stateReward.getFilename() << ", line " << stateReward.getLineNumber() << ": state reward value expression refers to unknown identifiers."); + try { + stateReward.getRewardValueExpression().check(identifierToTypeMap); + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Error in " << stateReward.getFilename() << ", line " << stateReward.getLineNumber() << ": " << e.what()); + } + LOG_THROW(stateReward.getRewardValueExpression().hasNumericalReturnType(), storm::exceptions::WrongFormatException, "Error in " << stateReward.getFilename() << ", line " << stateReward.getLineNumber() << ": reward value expression must evaluate to numerical type."); + } + + for (auto const& transitionReward : rewardModel.getTransitionRewards()) { + std::set<std::string> containedIdentifiers = transitionReward.getStatePredicateExpression().getVariables(); + bool isValid = std::includes(variablesAndConstants.begin(), variablesAndConstants.end(), containedIdentifiers.begin(), containedIdentifiers.end()); + LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << transitionReward.getFilename() << ", line " << transitionReward.getLineNumber() << ": state reward expression refers to unknown identifiers."); + try { + transitionReward.getStatePredicateExpression().check(identifierToTypeMap); + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Error in " << transitionReward.getFilename() << ", line " << transitionReward.getLineNumber() << ": " << e.what()); + } + LOG_THROW(transitionReward.getStatePredicateExpression().hasBooleanReturnType(), storm::exceptions::WrongFormatException, "Error in " << transitionReward.getFilename() << ", line " << transitionReward.getLineNumber() << ": state predicate must evaluate to type 'bool'."); + + containedIdentifiers = transitionReward.getRewardValueExpression().getVariables(); + isValid = std::includes(variablesAndConstants.begin(), variablesAndConstants.end(), containedIdentifiers.begin(), containedIdentifiers.end()); + LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << transitionReward.getFilename() << ", line " << transitionReward.getLineNumber() << ": state reward value expression refers to unknown identifiers."); + try { + transitionReward.getRewardValueExpression().check(identifierToTypeMap); + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Error in " << transitionReward.getFilename() << ", line " << transitionReward.getLineNumber() << ": " << e.what()); + } + LOG_THROW(transitionReward.getRewardValueExpression().hasNumericalReturnType(), storm::exceptions::WrongFormatException, "Error in " << transitionReward.getFilename() << ", line " << transitionReward.getLineNumber() << ": reward value expression must evaluate to numerical type."); + } + } + + // Check the initial states expression. + std::set<std::string> containedIdentifiers = this->getInitialConstruct().getInitialStatesExpression().getVariables(); + bool isValid = std::includes(variablesAndConstants.begin(), variablesAndConstants.end(), containedIdentifiers.begin(), containedIdentifiers.end()); + LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << this->getInitialConstruct().getFilename() << ", line " << this->getInitialConstruct().getLineNumber() << ": initial expression refers to unknown identifiers."); + try { + this->getInitialConstruct().getInitialStatesExpression().check(identifierToTypeMap); + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Error in " << this->getInitialConstruct().getFilename() << ", line " << this->getInitialConstruct().getLineNumber() << ": " << e.what()); + } + + // Check the labels. + for (auto const& label : this->getLabels()) { + // Check for duplicate identifiers. + LOG_THROW(allIdentifiers.find(label.getName()) == allIdentifiers.end(), storm::exceptions::WrongFormatException, "Error in " << label.getFilename() << ", line " << label.getLineNumber() << ": duplicate identifier '" << label.getName() << "'."); + + std::set<std::string> containedIdentifiers = label.getStatePredicateExpression().getVariables(); + bool isValid = std::includes(variablesAndConstants.begin(), variablesAndConstants.end(), containedIdentifiers.begin(), containedIdentifiers.end()); + LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << label.getFilename() << ", line " << label.getLineNumber() << ": label expression refers to unknown identifiers."); + try { + label.getStatePredicateExpression().check(identifierToTypeMap); + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Error in " << label.getFilename() << ", line " << label.getLineNumber() << ": " << e.what()); + } + + LOG_THROW(label.getStatePredicateExpression().hasBooleanReturnType(), storm::exceptions::WrongFormatException, "Error in " << label.getFilename() << ", line " << label.getLineNumber() << ": label predicate must evaluate to type 'bool'."); + } + + // Check the formulas. + for (auto const& formula : this->getFormulas()) { + // Check for duplicate identifiers. + LOG_THROW(allIdentifiers.find(formula.getName()) == allIdentifiers.end(), storm::exceptions::WrongFormatException, "Error in " << formula.getFilename() << ", line " << formula.getLineNumber() << ": duplicate identifier '" << formula.getName() << "'."); + + std::set<std::string> containedIdentifiers = formula.getExpression().getVariables(); + bool isValid = std::includes(variablesAndConstants.begin(), variablesAndConstants.end(), containedIdentifiers.begin(), containedIdentifiers.end()); + LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << formula.getFilename() << ", line " << formula.getLineNumber() << ": formula expression refers to unknown identifiers."); + try { + formula.getExpression().check(identifierToTypeMap); + } catch (storm::exceptions::InvalidTypeException const& e) { + LOG_THROW(false, storm::exceptions::WrongFormatException, "Error in " << formula.getFilename() << ", line " << formula.getLineNumber() << ": " << e.what()); + } + + // Record the new identifier for future checks. + allIdentifiers.insert(formula.getName()); + } + } + + std::ostream& operator<<(std::ostream& stream, Program const& program) { + switch (program.getModelType()) { + case Program::ModelType::UNDEFINED: stream << "undefined"; break; + case Program::ModelType::DTMC: stream << "dtmc"; break; + case Program::ModelType::CTMC: stream << "ctmc"; break; + case Program::ModelType::MDP: stream << "mdp"; break; + case Program::ModelType::CTMDP: stream << "ctmdp"; break; + case Program::ModelType::MA: stream << "ma"; break; + } + stream << std::endl; + + for (auto const& constant : program.getConstants()) { + stream << constant << std::endl; + } + stream << std::endl; + + for (auto const& variable : program.getGlobalBooleanVariables()) { + stream << "global " << variable << std::endl; + } + for (auto const& variable : program.getGlobalIntegerVariables()) { + stream << "global " << variable << std::endl; + } + stream << std::endl; + + for (auto const& formula : program.getFormulas()) { + stream << formula << std::endl; + } + stream << std::endl; + + for (auto const& module : program.getModules()) { + stream << module << std::endl; + } + + for (auto const& rewardModel : program.getRewardModels()) { + stream << rewardModel << std::endl; + } + + for (auto const& label : program.getLabels()) { + stream << label << std::endl; + } + + return stream; + } + + } // namespace prism +} // namepsace storm diff --git a/src/storage/prism/Program.h b/src/storage/prism/Program.h new file mode 100644 index 000000000..2a618ff7a --- /dev/null +++ b/src/storage/prism/Program.h @@ -0,0 +1,356 @@ +#ifndef STORM_STORAGE_PRISM_PROGRAM_H_ +#define STORM_STORAGE_PRISM_PROGRAM_H_ + +#include <map> +#include <vector> +#include <set> +#include <boost/container/flat_set.hpp> + +#include "src/storage/expressions/Expression.h" +#include "src/storage/prism/Constant.h" +#include "src/storage/prism/Formula.h" +#include "src/storage/prism/Label.h" +#include "src/storage/prism/Module.h" +#include "src/storage/prism/RewardModel.h" +#include "src/storage/prism/InitialConstruct.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace prism { + class Program : public LocatedInformation { + public: + /*! + * An enum for the different model types. + */ + enum class ModelType {UNDEFINED, DTMC, CTMC, MDP, CTMDP, MA}; + + /*! + * Creates a program with the given model type, undefined constants, global variables, modules, reward + * models, labels and initial states. + * + * @param modelType The type of the program. + * @param constants The constants of the program. + * @param globalBooleanVariables The global boolean variables of the program. + * @param globalIntegerVariables The global integer variables of the program. + * @param formulas The formulas defined in the program. + * @param modules The modules of the program. + * @param fixInitialConstruct A flag that indicates whether the given initial construct is to be ignored and + * replaced by a new one created from the initial values of the variables. + * @param initialConstruct The initial construct of the program. If the initial construct specifies "false" + * as the initial condition, the default values of the variables are used to construct a legal initial + * condition. + * @param rewardModels The reward models of the program. + * @param labels The labels defined for this program. + * @param filename The filename in which the program is defined. + * @param lineNumber The line number in which the program is defined. + * @param checkValidity If set to true, the program is checked for validity. + */ + Program(ModelType modelType, std::vector<Constant> const& constants, std::vector<BooleanVariable> const& globalBooleanVariables, std::vector<IntegerVariable> const& globalIntegerVariables, std::vector<Formula> const& formulas, std::vector<Module> const& modules, std::vector<RewardModel> const& rewardModels, bool fixInitialConstruct, storm::prism::InitialConstruct const& initialConstruct, std::vector<Label> const& labels, std::string const& filename = "", uint_fast64_t lineNumber = 0, bool checkValidity = true); + + // Provide default implementations for constructors and assignments. + Program() = default; + Program(Program const& other) = default; + Program& operator=(Program const& other) = default; +#ifndef WINDOWS + Program(Program&& other) = default; + Program& operator=(Program&& other) = default; +#endif + + /*! + * Retrieves the model type of the model. + * + * @return The type of the model. + */ + ModelType getModelType() const; + + /*! + * Retrieves whether there are undefined constants of any type in the program. + * + * @return True iff there are undefined constants of any type in the program. + */ + bool hasUndefinedConstants() const; + + /*! + * Retrieves whether the given constant exists in the program. + * + * @param constantName The name of the constant to search. + * @return True iff the constant exists in the program. + */ + bool hasConstant(std::string const& constantName) const; + + /*! + * Retrieves the constant with the given name if it exists. + * + * @param constantName The name of the constant to retrieve. + * @return The constant with the given name if it exists. + */ + Constant const& getConstant(std::string const& constantName) const; + + /*! + * Retrieves all constants defined in the program. + * + * @return The constants defined in the program. + */ + std::vector<Constant> const& getConstants() const; + + /*! + * Retrieves the number of all constants defined in the program. + * + * @return The number of constants defined in the program. + */ + std::size_t getNumberOfConstants() const; + + /*! + * Retrieves the global boolean variables of the program. + * + * @return The global boolean variables of the program. + */ + std::vector<BooleanVariable> const& getGlobalBooleanVariables() const; + + /*! + * Retrieves a the global boolean variable with the given name. + * + * @param variableName The name of the global boolean variable to retrieve. + * @return The global boolean variable with the given name. + */ + BooleanVariable const& getGlobalBooleanVariable(std::string const& variableName) const; + + /*! + * Retrieves the global integer variables of the program. + * + * @return The global integer variables of the program. + */ + std::vector<IntegerVariable> const& getGlobalIntegerVariables() const; + + /*! + * Retrieves a the global integer variable with the given name. + * + * @param variableName The name of the global integer variable to retrieve. + * @return The global integer variable with the given name. + */ + IntegerVariable const& getGlobalIntegerVariable(std::string const& variableName) const; + + /*! + * Retrieves the number of global boolean variables of the program. + * + * @return The number of global boolean variables of the program. + */ + std::size_t getNumberOfGlobalBooleanVariables() const; + + /*! + * Retrieves the number of global integer variables of the program. + * + * @return The number of global integer variables of the program. + */ + std::size_t getNumberOfGlobalIntegerVariables() const; + + /*! + * Retrieves the formulas defined in the program. + * + * @return The formulas defined in the program. + */ + std::vector<Formula> const& getFormulas() const; + + /*! + * Retrieves the number of formulas in the program. + * + * @return The number of formulas in the program. + */ + std::size_t getNumberOfFormulas() const; + + /*! + * Retrieves the number of modules in the program. + * + * @return The number of modules in the program. + */ + std::size_t getNumberOfModules() const; + + /*! + * Retrieves the module with the given index. + * + * @param index The index of the module to retrieve. + * @return The module with the given index. + */ + Module const& getModule(uint_fast64_t index) const; + + /*! + * Retrieves the module with the given name. + * + * @param moduleName The name of the module to retrieve. + * @return The module with the given name. + */ + Module const& getModule(std::string const& moduleName) const; + + /*! + * Retrieves all modules of the program. + * + * @return All modules of the program. + */ + std::vector<Module> const& getModules() const; + + /*! + * Retrieves the initial construct of the program. + * + * @return The initial construct of the program. + */ + storm::prism::InitialConstruct const& getInitialConstruct() const; + + /*! + * Retrieves the set of actions present in the program. + * + * @return The set of actions present in the program. + */ + std::set<std::string> const& getActions() const; + + /*! + * Retrieves the indices of all modules within this program that contain commands that are labelled with the + * given action. + * + * @param action The name of the action the modules are supposed to possess. + * @return A set of indices of all matching modules. + */ + std::set<uint_fast64_t> const& getModuleIndicesByAction(std::string const& action) const; + + /*! + * Retrieves the index of the module in which the given variable name was declared. + * + * @param variableName The name of the variable to search. + * @return The index of the module in which the given variable name was declared. + */ + uint_fast64_t getModuleIndexByVariable(std::string const& variableName) const; + + /*! + * Retrieves the reward models of the program. + * + * @return The reward models of the program. + */ + std::vector<RewardModel> const& getRewardModels() const; + + /*! + * Retrieves the number of reward models in the program. + * + * @return The number of reward models in the program. + */ + std::size_t getNumberOfRewardModels() const; + + /*! + * Retrieves the reward model with the given name. + * + * @param rewardModelName The name of the reward model to return. + * @return The reward model with the given name. + */ + RewardModel const& getRewardModel(std::string const& rewardModelName) const; + + /*! + * Retrieves all labels that are defined by the probabilitic program. + * + * @return A set of labels that are defined in the program. + */ + std::vector<Label> const& getLabels() const; + + /*! + * Retrieves the number of labels in the program. + * + * @return The number of labels in the program. + */ + std::size_t getNumberOfLabels() const; + + /*! + * Creates a new program that drops all commands whose indices are not in the given set. + * + * @param indexSet The set of indices for which to keep the commands. + */ + Program restrictCommands(boost::container::flat_set<uint_fast64_t> const& indexSet) const; + + /*! + * Defines the undefined constants according to the given map and returns the resulting program. + * + * @param constantDefinitions A mapping from undefined constant names to the expressions they are supposed + * to be replaced with. + * @return The program after all undefined constants in the given map have been replaced with their + * definitions. + */ + Program defineUndefinedConstants(std::map<std::string, storm::expressions::Expression> const& constantDefinitions) const; + + /*! + * Substitutes all constants appearing in the expressions of the program by their defining expressions. For + * this to work, all constants need to be defined prior to calling this. + * + * @return The resulting program that only contains expressions over variables of the program. + */ + Program substituteConstants() const; + + /*! + * Checks the validity of the program. If the program is not valid, an exception is thrown with a message + * that indicates the source of the problem. + */ + void checkValidity() const; + + friend std::ostream& operator<<(std::ostream& stream, Program const& program); + + private: + // Creates the internal mappings. + void createMappings(); + + // The type of the model. + ModelType modelType; + + // The undefined constants of the program. + std::vector<Constant> constants; + + // A mapping from constant names to their corresponding indices. + std::map<std::string, uint_fast64_t> constantToIndexMap; + + // The global boolean variables. + std::vector<BooleanVariable> globalBooleanVariables; + + // A mapping from global boolean variable names to their corresponding indices. + std::map<std::string, uint_fast64_t> globalBooleanVariableToIndexMap; + + // The global integer variables. + std::vector<IntegerVariable> globalIntegerVariables; + + // A mapping from global integer variable names to their corresponding indices. + std::map<std::string, uint_fast64_t> globalIntegerVariableToIndexMap; + + // The formulas defined in the program. + std::vector<Formula> formulas; + + // A mapping of formula names to their corresponding indices. + std::map<std::string, uint_fast64_t> formulaToIndexMap; + + // The modules associated with the program. + std::vector<Module> modules; + + // A mapping of module names to their indices. + std::map<std::string, uint_fast64_t> moduleToIndexMap; + + // The reward models associated with the program. + std::vector<RewardModel> rewardModels; + + // A mapping of reward models to their indices. + std::map<std::string, uint_fast64_t> rewardModelToIndexMap; + + // The initial construct of the program. + storm::prism::InitialConstruct initialConstruct; + + // The labels that are defined for this model. + std::vector<Label> labels; + + // A mapping from label names to their corresponding indices. + std::map<std::string, uint_fast64_t> labelToIndexMap; + + // The set of actions present in this program. + std::set<std::string> actions; + + // A map of actions to the set of modules containing commands labelled with this action. + std::map<std::string, std::set<uint_fast64_t>> actionsToModuleIndexMap; + + // A mapping from variable names to the modules in which they were declared. + std::map<std::string, uint_fast64_t> variableToModuleIndexMap; + }; + + } // namespace prism +} // namespace storm + +#endif /* STORM_STORAGE_PRISM_PROGRAM_H_ */ diff --git a/src/storage/prism/RewardModel.cpp b/src/storage/prism/RewardModel.cpp new file mode 100644 index 000000000..a6c68b34f --- /dev/null +++ b/src/storage/prism/RewardModel.cpp @@ -0,0 +1,57 @@ +#include "src/storage/prism/RewardModel.h" + +namespace storm { + namespace prism { + RewardModel::RewardModel(std::string const& rewardModelName, std::vector<storm::prism::StateReward> const& stateRewards, std::vector<storm::prism::TransitionReward> const& transitionRewards, std::string const& filename, uint_fast64_t lineNumber) : LocatedInformation(filename, lineNumber), rewardModelName(rewardModelName), stateRewards(stateRewards), transitionRewards(transitionRewards) { + // Nothing to do here. + } + + std::string const& RewardModel::getName() const { + return this->rewardModelName; + } + + bool RewardModel::hasStateRewards() const { + return this->stateRewards.size() > 0; + } + + std::vector<storm::prism::StateReward> const& RewardModel::getStateRewards() const { + return this->stateRewards; + } + + bool RewardModel::hasTransitionRewards() const { + return this->transitionRewards.size() > 0; + } + + std::vector<storm::prism::TransitionReward> const& RewardModel::getTransitionRewards() const { + return this->transitionRewards; + } + + RewardModel RewardModel::substitute(std::map<std::string, storm::expressions::Expression> const& substitution) const { + std::vector<StateReward> newStateRewards; + newStateRewards.reserve(this->getStateRewards().size()); + for (auto const& stateReward : this->getStateRewards()) { + newStateRewards.emplace_back(stateReward.substitute(substitution)); + } + + std::vector<TransitionReward> newTransitionRewards; + newTransitionRewards.reserve(this->getTransitionRewards().size()); + for (auto const& transitionReward : this->getTransitionRewards()) { + newTransitionRewards.emplace_back(transitionReward.substitute(substitution)); + } + return RewardModel(this->getName(), newStateRewards, newTransitionRewards, this->getFilename(), this->getLineNumber()); + } + + std::ostream& operator<<(std::ostream& stream, RewardModel const& rewardModel) { + stream << "rewards \"" << rewardModel.getName() << "\"" << std::endl; + for (auto const& reward : rewardModel.getStateRewards()) { + stream << reward << std::endl; + } + for (auto const& reward : rewardModel.getTransitionRewards()) { + stream << reward << std::endl; + } + stream << "endrewards" << std::endl; + return stream; + } + + } // namespace prism +} // namespace storm diff --git a/src/storage/prism/RewardModel.h b/src/storage/prism/RewardModel.h new file mode 100644 index 000000000..3bb68d1b5 --- /dev/null +++ b/src/storage/prism/RewardModel.h @@ -0,0 +1,95 @@ +#ifndef STORM_STORAGE_PRISM_REWARDMODEL_H_ +#define STORM_STORAGE_PRISM_REWARDMODEL_H_ + +#include <string> +#include <vector> +#include <map> + +#include "src/storage/prism/StateReward.h" +#include "src/storage/prism/TransitionReward.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace prism { + class RewardModel : public LocatedInformation { + public: + /*! + * Creates a reward model with the given name, state and transition rewards. + * + * @param rewardModelName The name of the reward model. + * @param stateRewards A vector of state-based rewards. + * @param transitionRewards A vector of transition-based rewards. + * @param filename The filename in which the reward model is defined. + * @param lineNumber The line number in which the reward model is defined. + */ + RewardModel(std::string const& rewardModelName, std::vector<storm::prism::StateReward> const& stateRewards, std::vector<storm::prism::TransitionReward> const& transitionRewards, std::string const& filename = "", uint_fast64_t lineNumber = 0); + + // Create default implementations of constructors/assignment. + RewardModel() = default; + RewardModel(RewardModel const& other) = default; + RewardModel& operator=(RewardModel const& other)= default; +#ifndef WINDOWS + RewardModel(RewardModel&& other) = default; + RewardModel& operator=(RewardModel&& other) = default; +#endif + + /*! + * Retrieves the name of the reward model. + * + * @return The name of the reward model. + */ + std::string const& getName() const; + + /*! + * Retrieves whether there are any state rewards. + * + * @return True iff there are any state rewards. + */ + bool hasStateRewards() const; + + /*! + * Retrieves all state rewards associated with this reward model. + * + * @return The state rewards associated with this reward model. + */ + std::vector<storm::prism::StateReward> const& getStateRewards() const; + + /*! + * Retrieves whether there are any transition rewards. + * + * @return True iff there are any transition rewards. + */ + bool hasTransitionRewards() const; + + /*! + * Retrieves all transition rewards associated with this reward model. + * + * @return The transition rewards associated with this reward model. + */ + std::vector<storm::prism::TransitionReward> const& getTransitionRewards() const; + + /*! + * Substitutes all identifiers in the reward model according to the given map. + * + * @param substitution The substitution to perform. + * @return The resulting reward model. + */ + RewardModel substitute(std::map<std::string, storm::expressions::Expression> const& substitution) const; + + friend std::ostream& operator<<(std::ostream& stream, RewardModel const& rewardModel); + + private: + // The name of the reward model. + std::string rewardModelName; + + // The state-based rewards associated with this reward model. + std::vector<storm::prism::StateReward> stateRewards; + + // The transition-based rewards associated with this reward model. + std::vector<storm::prism::TransitionReward> transitionRewards; + }; + + } // namespace prism +} // namespace storm + +#endif /* STORM_STORAGE_PRISM_REWARDMODEL_H_ */ diff --git a/src/storage/prism/StateReward.cpp b/src/storage/prism/StateReward.cpp new file mode 100644 index 000000000..a93729b52 --- /dev/null +++ b/src/storage/prism/StateReward.cpp @@ -0,0 +1,26 @@ +#include "src/storage/prism/StateReward.h" + +namespace storm { + namespace prism { + StateReward::StateReward(storm::expressions::Expression const& statePredicateExpression, storm::expressions::Expression const& rewardValueExpression, std::string const& filename, uint_fast64_t lineNumber) : LocatedInformation(filename, lineNumber), statePredicateExpression(statePredicateExpression), rewardValueExpression(rewardValueExpression) { + // Nothing to do here. + } + + storm::expressions::Expression const& StateReward::getStatePredicateExpression() const { + return this->statePredicateExpression; + } + + storm::expressions::Expression const& StateReward::getRewardValueExpression() const { + return this->rewardValueExpression; + } + + StateReward StateReward::substitute(std::map<std::string, storm::expressions::Expression> const& substitution) const { + return StateReward(this->getStatePredicateExpression().substitute(substitution), this->getRewardValueExpression().substitute(substitution), this->getFilename(), this->getLineNumber()); + } + + std::ostream& operator<<(std::ostream& stream, StateReward const& stateReward) { + stream << "\t" << stateReward.getStatePredicateExpression() << ": " << stateReward.getRewardValueExpression() << ";"; + return stream; + } + } // namespace prism +} // namespace storm diff --git a/src/storage/prism/StateReward.h b/src/storage/prism/StateReward.h new file mode 100644 index 000000000..f64dc9e4d --- /dev/null +++ b/src/storage/prism/StateReward.h @@ -0,0 +1,69 @@ +#ifndef STORM_STORAGE_PRISM_STATEREWARD_H_ +#define STORM_STORAGE_PRISM_STATEREWARD_H_ + +#include <map> + +#include "src/storage/prism/LocatedInformation.h" +#include "src/storage/expressions/Expression.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace prism { + class StateReward : public LocatedInformation { + public: + /*! + * Creates a state reward for the states satisfying the given expression with the value given by a second + * expression. + * + * @param statePredicateExpression The predicate that states earning this state-based reward need to satisfy. + * @param rewardValueExpression An expression specifying the values of the rewards to attach to the states. + * @param filename The filename in which the state reward is defined. + * @param lineNumber The line number in which the state reward is defined. + */ + StateReward(storm::expressions::Expression const& statePredicateExpression, storm::expressions::Expression const& rewardValueExpression, std::string const& filename = "", uint_fast64_t lineNumber = 0); + + // Create default implementations of constructors/assignment. + StateReward() = default; + StateReward(StateReward const& other) = default; + StateReward& operator=(StateReward const& other)= default; +#ifndef WINDOWS + StateReward(StateReward&& other) = default; + StateReward& operator=(StateReward&& other) = default; +#endif + + /*! + * Retrieves the state predicate that is associated with this state reward. + * + * @return The state predicate that is associated with this state reward. + */ + storm::expressions::Expression const& getStatePredicateExpression() const; + + /*! + * Retrieves the reward value associated with this state reward. + * + * @return The reward value associated with this state reward. + */ + storm::expressions::Expression const& getRewardValueExpression() const; + + /*! + * Substitutes all identifiers in the state reward according to the given map. + * + * @param substitution The substitution to perform. + * @return The resulting state reward. + */ + StateReward substitute(std::map<std::string, storm::expressions::Expression> const& substitution) const; + + friend std::ostream& operator<<(std::ostream& stream, StateReward const& stateReward); + + private: + // The predicate that characterizes the states that obtain this reward. + storm::expressions::Expression statePredicateExpression; + + // The expression that specifies the value of the reward obtained. + storm::expressions::Expression rewardValueExpression; + }; + + } // namespace prism +} // namespace storm + +#endif /* STORM_STORAGE_PRISM_STATEREWARD_H_ */ diff --git a/src/storage/prism/TransitionReward.cpp b/src/storage/prism/TransitionReward.cpp new file mode 100644 index 000000000..cef7bf654 --- /dev/null +++ b/src/storage/prism/TransitionReward.cpp @@ -0,0 +1,31 @@ +#include "src/storage/prism/TransitionReward.h" + +namespace storm { + namespace prism { + TransitionReward::TransitionReward(std::string const& commandName, storm::expressions::Expression const& statePredicateExpression, storm::expressions::Expression const& rewardValueExpression, std::string const& filename, uint_fast64_t lineNumber) : LocatedInformation(filename, lineNumber), commandName(commandName), statePredicateExpression(statePredicateExpression), rewardValueExpression(rewardValueExpression) { + // Nothing to do here. + } + + std::string const& TransitionReward::getActionName() const { + return this->commandName; + } + + storm::expressions::Expression const& TransitionReward::getStatePredicateExpression() const { + return this->statePredicateExpression; + } + + storm::expressions::Expression const& TransitionReward::getRewardValueExpression() const { + return this->rewardValueExpression; + } + + TransitionReward TransitionReward::substitute(std::map<std::string, storm::expressions::Expression> const& substitution) const { + return TransitionReward(this->getActionName(), this->getStatePredicateExpression().substitute(substitution), this->getRewardValueExpression().substitute(substitution), this->getFilename(), this->getLineNumber()); + } + + std::ostream& operator<<(std::ostream& stream, TransitionReward const& transitionReward) { + stream << "\t[" << transitionReward.getActionName() << "] " << transitionReward.getStatePredicateExpression() << ": " << transitionReward.getRewardValueExpression() << ";"; + return stream; + } + + } // namespace prism +} // namespace storm diff --git a/src/storage/prism/TransitionReward.h b/src/storage/prism/TransitionReward.h new file mode 100644 index 000000000..2c118cec7 --- /dev/null +++ b/src/storage/prism/TransitionReward.h @@ -0,0 +1,82 @@ +#ifndef STORM_STORAGE_PRISM_TRANSITIONREWARD_H_ +#define STORM_STORAGE_PRISM_TRANSITIONREWARD_H_ + +#include <map> + +#include "src/storage/prism/LocatedInformation.h" +#include "src/storage/expressions/Expression.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace prism { + class TransitionReward : public LocatedInformation { + public: + /*! + * Creates a transition reward for the transitions with the given name emanating from states satisfying the + * given expression with the value given by another expression. + * + * @param actionName The name of the command that obtains this reward. + * @param statePredicateExpression The predicate that needs to hold before taking a transition with the previously + * specified name in order to obtain the reward. + * @param rewardValueExpression An expression specifying the values of the rewards to attach to the transitions. + * @param filename The filename in which the transition reward is defined. + * @param lineNumber The line number in which the transition reward is defined. + */ + TransitionReward(std::string const& actionName, storm::expressions::Expression const& statePredicateExpression, storm::expressions::Expression const& rewardValueExpression, std::string const& filename = "", uint_fast64_t lineNumber = 0); + + // Create default implementations of constructors/assignment. + TransitionReward() = default; + TransitionReward(TransitionReward const& other) = default; + TransitionReward& operator=(TransitionReward const& other)= default; +#ifndef WINDOWS + TransitionReward(TransitionReward&& other) = default; + TransitionReward& operator=(TransitionReward&& other) = default; +#endif + + /*! + * Retrieves the action name that is associated with this transition reward. + * + * @return The action name that is associated with this transition reward. + */ + std::string const& getActionName() const; + + /*! + * Retrieves the state predicate expression that is associated with this state reward. + * + * @return The state predicate expression that is associated with this state reward. + */ + storm::expressions::Expression const& getStatePredicateExpression() const; + + /*! + * Retrieves the reward value expression associated with this state reward. + * + * @return The reward value expression associated with this state reward. + */ + storm::expressions::Expression const& getRewardValueExpression() const; + + /*! + * Substitutes all identifiers in the transition reward according to the given map. + * + * @param substitution The substitution to perform. + * @return The resulting transition reward. + */ + TransitionReward substitute(std::map<std::string, storm::expressions::Expression> const& substitution) const; + + friend std::ostream& operator<<(std::ostream& stream, TransitionReward const& transitionReward); + + private: + // The name of the command this transition-based reward is attached to. + std::string commandName; + + // A predicate that needs to be satisfied by states for the reward to be obtained (by taking + // a corresponding command transition). + storm::expressions::Expression statePredicateExpression; + + // The expression specifying the value of the reward obtained along the transitions. + storm::expressions::Expression rewardValueExpression; + }; + + } // namespace prism +} // namespace storm + +#endif /* STORM_STORAGE_PRISM_TRANSITIONREWARD_H_ */ diff --git a/src/storage/prism/Update.cpp b/src/storage/prism/Update.cpp new file mode 100644 index 000000000..9082bc6f1 --- /dev/null +++ b/src/storage/prism/Update.cpp @@ -0,0 +1,64 @@ +#include "Update.h" +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/OutOfRangeException.h" + +namespace storm { + namespace prism { + Update::Update(uint_fast64_t globalIndex, storm::expressions::Expression const& likelihoodExpression, std::vector<storm::prism::Assignment> const& assignments, std::string const& filename, uint_fast64_t lineNumber) : LocatedInformation(filename, lineNumber), likelihoodExpression(likelihoodExpression), assignments(assignments), variableToAssignmentIndexMap(), globalIndex(globalIndex) { + this->createAssignmentMapping(); + } + + storm::expressions::Expression const& Update::getLikelihoodExpression() const { + return likelihoodExpression; + } + + std::size_t Update::getNumberOfAssignments() const { + return this->assignments.size(); + } + + std::vector<storm::prism::Assignment> const& Update::getAssignments() const { + return this->assignments; + } + + storm::prism::Assignment const& Update::getAssignment(std::string const& variableName) const { + auto const& variableIndexPair = this->variableToAssignmentIndexMap.find(variableName); + LOG_THROW(variableIndexPair != this->variableToAssignmentIndexMap.end(), storm::exceptions::OutOfRangeException, "Variable '" << variableName << "' is not assigned in update."); + return this->getAssignments()[variableIndexPair->second]; + } + + uint_fast64_t Update::getGlobalIndex() const { + return this->globalIndex; + } + + void Update::createAssignmentMapping() { + this->variableToAssignmentIndexMap.clear(); + for (uint_fast64_t assignmentIndex = 0; assignmentIndex < this->getAssignments().size(); ++assignmentIndex) { + this->variableToAssignmentIndexMap[this->getAssignments()[assignmentIndex].getVariableName()] = assignmentIndex; + } + } + + Update Update::substitute(std::map<std::string, storm::expressions::Expression> const& substitution) const { + std::vector<Assignment> newAssignments; + newAssignments.reserve(this->getNumberOfAssignments()); + for (auto const& assignment : this->getAssignments()) { + newAssignments.emplace_back(assignment.substitute(substitution)); + } + + return Update(this->getGlobalIndex(), this->getLikelihoodExpression().substitute(substitution), newAssignments, this->getFilename(), this->getLineNumber()); + } + + std::ostream& operator<<(std::ostream& stream, Update const& update) { + stream << update.getLikelihoodExpression() << " : "; + uint_fast64_t i = 0; + for (auto const& assignment : update.getAssignments()) { + stream << assignment; + if (i < update.getAssignments().size() - 1) { + stream << " & "; + } + ++i; + } + return stream; + } + + } // namespace prism +} // namespace storm diff --git a/src/storage/prism/Update.h b/src/storage/prism/Update.h new file mode 100644 index 000000000..b48aa99e4 --- /dev/null +++ b/src/storage/prism/Update.h @@ -0,0 +1,100 @@ +#ifndef STORM_STORAGE_PRISM_UPDATE_H_ +#define STORM_STORAGE_PRISM_UPDATE_H_ + +#include <vector> + +#include "src/storage/prism/LocatedInformation.h" +#include "src/storage/prism/Assignment.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace prism { + class Update : public LocatedInformation { + public: + /*! + * Creates an update with the given expression specifying the likelihood and assignments. + * + * @param globalIndex The global index of the update. + * @param likelihoodExpression An expression specifying the likelihood of this update. + * @param assignments A assignments to variables. + * @param filename The filename in which the update is defined. + * @param lineNumber The line number in which the update is defined. + */ + Update(uint_fast64_t globalIndex, storm::expressions::Expression const& likelihoodExpression, std::vector<storm::prism::Assignment> const& assignments, std::string const& filename = "", uint_fast64_t lineNumber = 0); + + // Create default implementations of constructors/assignment. + Update() = default; + Update(Update const& other) = default; + Update& operator=(Update const& other)= default; +#ifndef WINDOWS + Update(Update&& other) = default; + Update& operator=(Update&& other) = default; +#endif + + /*! + * Retrieves the expression for the likelihood of this update. + * + * @return The expression for the likelihood of this update. + */ + storm::expressions::Expression const& getLikelihoodExpression() const; + + /*! + * Retrieves the number of assignments associated with this update. + * + * @return The number of assignments associated with this update. + */ + std::size_t getNumberOfAssignments() const; + + /*! + * Retrieves a reference to the map of variable names to their respective assignments. + * + * @return A reference to the map of variable names to their respective assignments. + */ + std::vector<storm::prism::Assignment> const& getAssignments() const; + + /*! + * Retrieves a reference to the assignment for the variable with the given name. + * + * @return A reference to the assignment for the variable with the given name. + */ + storm::prism::Assignment const& getAssignment(std::string const& variableName) const; + + /*! + * Retrieves the global index of the update, that is, a unique index over all modules. + * + * @return The global index of the update. + */ + uint_fast64_t getGlobalIndex() const; + + /*! + * Substitutes all identifiers in the update according to the given map. + * + * @param substitution The substitution to perform. + * @return The resulting update. + */ + Update substitute(std::map<std::string, storm::expressions::Expression> const& substitution) const; + + friend std::ostream& operator<<(std::ostream& stream, Update const& assignment); + + private: + /*! + * Creates the internal mapping of variables to their assignments. + */ + void createAssignmentMapping(); + + // An expression specifying the likelihood of taking this update. + storm::expressions::Expression likelihoodExpression; + + // The assignments of this update. + std::vector<storm::prism::Assignment> assignments; + + // A mapping from variable names to their assignments. + std::map<std::string, uint_fast64_t> variableToAssignmentIndexMap; + + // The global index of the update. + uint_fast64_t globalIndex; + }; + } // namespace prism +} // namespace storm + +#endif /* STORM_STORAGE_PRISM_UPDATE_H_ */ diff --git a/src/storage/prism/Variable.cpp b/src/storage/prism/Variable.cpp new file mode 100644 index 000000000..4aabd548c --- /dev/null +++ b/src/storage/prism/Variable.cpp @@ -0,0 +1,27 @@ +#include <map> + +#include "src/storage/prism/Variable.h" + +namespace storm { + namespace prism { + Variable::Variable(std::string const& name, storm::expressions::Expression const& initialValueExpression, bool defaultInitialValue, std::string const& filename, uint_fast64_t lineNumber) : LocatedInformation(filename, lineNumber), name(name), initialValueExpression(initialValueExpression), defaultInitialValue(defaultInitialValue) { + // Nothing to do here. + } + + Variable::Variable(Variable const& oldVariable, std::string const& newName, std::map<std::string, std::string> const& renaming, std::string const& filename, uint_fast64_t lineNumber) : LocatedInformation(filename, lineNumber), name(newName), initialValueExpression(oldVariable.getInitialValueExpression().substitute(renaming)), defaultInitialValue(oldVariable.hasDefaultInitialValue()) { + // Intentionally left empty. + } + + std::string const& Variable::getName() const { + return this->name; + } + + bool Variable::hasDefaultInitialValue() const { + return this->defaultInitialValue; + } + + storm::expressions::Expression const& Variable::getInitialValueExpression() const { + return this->initialValueExpression; + } + } // namespace prism +} // namespace storm diff --git a/src/storage/prism/Variable.h b/src/storage/prism/Variable.h new file mode 100644 index 000000000..a4cb76cb9 --- /dev/null +++ b/src/storage/prism/Variable.h @@ -0,0 +1,84 @@ +#ifndef STORM_STORAGE_PRISM_VARIABLE_H_ +#define STORM_STORAGE_PRISM_VARIABLE_H_ + +#include <map> + +#include "src/storage/prism/LocatedInformation.h" +#include "src/storage/expressions/Expression.h" +#include "src/utility/OsDetection.h" + +namespace storm { + namespace prism { + class Variable : public LocatedInformation { + public: + // Create default implementations of constructors/assignment. + Variable(Variable const& otherVariable) = default; + Variable& operator=(Variable const& otherVariable)= default; +#ifndef WINDOWS + Variable(Variable&& otherVariable) = default; + Variable& operator=(Variable&& otherVariable) = default; +#endif + + /*! + * Retrieves the name of the variable. + * + * @return The name of the variable. + */ + std::string const& getName() const; + + /*! + * Retrieves the expression defining the initial value of the variable. + * + * @return The expression defining the initial value of the variable. + */ + storm::expressions::Expression const& getInitialValueExpression() const; + + /*! + * Retrieves whether the variable has the default initial value with respect to its type. + * + * @return True iff the variable has the default initial value. + */ + bool hasDefaultInitialValue() const; + + // Make the constructors protected to forbid instantiation of this class. + protected: + Variable() = default; + + /*! + * Creates a variable with the given name and initial value. + * + * @param name The name of the variable. + * @param initialValueExpression The constant expression that defines the initial value of the variable. + * @param hasDefaultInitialValue A flag indicating whether the initial value of the variable is its default + * value. + * @param filename The filename in which the variable is defined. + * @param lineNumber The line number in which the variable is defined. + */ + Variable(std::string const& name, storm::expressions::Expression const& initialValueExpression, bool defaultInitialValue, std::string const& filename = "", uint_fast64_t lineNumber = 0); + + /*! + * Creates a copy of the given variable and performs the provided renaming. + * + * @param oldVariable The variable to copy. + * @param newName New name of this variable. + * @param renaming A mapping from names that are to be renamed to the names they are to be replaced with. + * @param filename The filename in which the variable is defined. + * @param lineNumber The line number in which the variable is defined. + */ + Variable(Variable const& oldVariable, std::string const& newName, std::map<std::string, std::string> const& renaming, std::string const& filename = "", uint_fast64_t lineNumber = 0); + + private: + // The name of the variable. + std::string name; + + // The constant expression defining the initial value of the variable. + storm::expressions::Expression initialValueExpression; + + // A flag that stores whether the variable has its default initial expression. + bool defaultInitialValue; + }; + + } // namespace prism +} // namespace storm + +#endif /* STORM_STORAGE_PRISM_VARIABLE_H_ */ diff --git a/src/storm.cpp b/src/storm.cpp index c95fd1338..a41552d46 100644 --- a/src/storm.cpp +++ b/src/storm.cpp @@ -23,6 +23,7 @@ #include <chrono> #include "storm-config.h" +#include "storm-version.h" #include "src/models/Dtmc.h" #include "src/models/MarkovAutomaton.h" #include "src/storage/SparseMatrix.h" @@ -33,10 +34,11 @@ #include "src/modelchecker/prctl/SparseMdpPrctlModelChecker.h" #include "src/modelchecker/prctl/TopologicalValueIterationMdpPrctlModelChecker.h" #include "src/solver/GmmxxLinearEquationSolver.h" +#include "src/solver/NativeLinearEquationSolver.h" #include "src/solver/GmmxxNondeterministicLinearEquationSolver.h" #include "src/solver/GurobiLpSolver.h" #include "src/counterexamples/MILPMinimalLabelSetGenerator.h" -#include "src/counterexamples/SMTMinimalCommandSetGenerator.h" +// #include "src/counterexamples/SMTMinimalCommandSetGenerator.h" #include "src/counterexamples/PathBasedSubsystemGenerator.h" #include "src/parser/AutoParser.h" #include "src/parser/MarkovAutomatonParser.h" @@ -56,10 +58,11 @@ #include "log4cplus/loggingmacros.h" #include "log4cplus/consoleappender.h" #include "log4cplus/fileappender.h" +log4cplus::Logger logger; #include "src/parser/PrismParser.h" #include "src/adapters/ExplicitModelAdapter.h" -#include "src/adapters/SymbolicModelAdapter.h" +// #include "src/adapters/SymbolicModelAdapter.h" #include "src/exceptions/InvalidSettingsException.h" @@ -121,13 +124,11 @@ void printUsage() { double userTime = static_cast<double>(uLargeInteger.QuadPart) / 10000.0; std::cout << "CPU Time: " << std::endl; - std::cout << "\tKernel Time: " << std::setprecision(5) << kernelTime << std::endl; - std::cout << "\tUser Time: " << std::setprecision(5) << userTime << std::endl; + std::cout << "\tKernel Time: " << std::setprecision(5) << kernelTime << "ms" << std::endl; + std::cout << "\tUser Time: " << std::setprecision(5) << userTime << "ms" << std::endl; #endif } -log4cplus::Logger logger; - /*! * Initializes the logging framework and sets up logging to console. */ @@ -269,7 +270,7 @@ void setUp() { * Performs some necessary clean-up. */ void cleanUp() { - delete storm::utility::cuddUtilityInstance(); + // Intentionally left empty. } /*! @@ -278,13 +279,15 @@ void cleanUp() { * @param dtmc A reference to the DTMC for which the model checker is to be created. * @return A pointer to the resulting model checker. */ -storm::modelchecker::prctl::AbstractModelChecker<double>* createPrctlModelChecker(storm::models::Dtmc<double>& dtmc) { +storm::modelchecker::prctl::AbstractModelChecker<double>* createPrctlModelChecker(storm::models::Dtmc<double> const & dtmc) { // Create the appropriate model checker. storm::settings::Settings* s = storm::settings::Settings::getInstance(); - std::string const chosenMatrixLibrary = s->getOptionByLongName("matrixLibrary").getArgument(0).getValueAsString(); - if (chosenMatrixLibrary == "gmm++") { + std::string const& linsolver = s->getOptionByLongName("linsolver").getArgument(0).getValueAsString(); + if (linsolver == "gmm++") { return new storm::modelchecker::prctl::SparseDtmcPrctlModelChecker<double>(dtmc, new storm::solver::GmmxxLinearEquationSolver<double>()); - } + } else if (linsolver == "native") { + return new storm::modelchecker::prctl::SparseDtmcPrctlModelChecker<double>(dtmc, new storm::solver::NativeLinearEquationSolver<double>()); + } // The control flow should never reach this point, as there is a default setting for matrixlib. std::string message = "No matrix library suitable for DTMC model checking has been set."; @@ -298,7 +301,7 @@ storm::modelchecker::prctl::AbstractModelChecker<double>* createPrctlModelChecke * @param mdp The Dtmc that the model checker will check * @return */ -storm::modelchecker::prctl::AbstractModelChecker<double>* createPrctlModelChecker(storm::models::Mdp<double>& mdp) { +storm::modelchecker::prctl::AbstractModelChecker<double>* createPrctlModelChecker(storm::models::Mdp<double> const & mdp) { //return new storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double>(mdp); return new storm::modelchecker::prctl::TopologicalValueIterationMdpPrctlModelChecker<double>(mdp); } @@ -330,13 +333,13 @@ void checkPrctlFormulae(storm::modelchecker::prctl::AbstractModelChecker<double> * * @param parser An AutoParser to get the model from. */ - void generateCounterExample(storm::parser::AutoParser<double> parser) { + void generateCounterExample(std::shared_ptr<storm::models::AbstractModel<double>> model) { LOG4CPLUS_INFO(logger, "Starting counterexample generation."); LOG4CPLUS_INFO(logger, "Testing inputs..."); storm::settings::Settings* s = storm::settings::Settings::getInstance(); - //First test output directory. + // First test output directory. std::string outPath = s->getOptionByLongName("counterExample").getArgument(0).getValueAsString(); if(outPath.back() != '/' && outPath.back() != '\\') { LOG4CPLUS_ERROR(logger, "The output path is not valid."); @@ -350,13 +353,16 @@ void checkPrctlFormulae(storm::modelchecker::prctl::AbstractModelChecker<double> testFile.close(); std::remove((outPath + "test.dot").c_str()); - //Differentiate between model types. - if(parser.getType() != storm::models::DTMC) { + // Differentiate between model types. + if(model->getType() != storm::models::DTMC) { LOG4CPLUS_ERROR(logger, "Counterexample generation for the selected model type is not supported."); return; } - storm::models::Dtmc<double> model = *parser.getModel<storm::models::Dtmc<double>>(); + // Get the Dtmc back from the AbstractModel + // Note that the ownership of the object referenced by dtmc lies at the main function. + // Thus, it must not be deleted. + storm::models::Dtmc<double> dtmc = *(model->as<storm::models::Dtmc<double>>()); LOG4CPLUS_INFO(logger, "Model is a DTMC."); // Get specified PRCTL formulas. @@ -417,10 +423,10 @@ void checkPrctlFormulae(storm::modelchecker::prctl::AbstractModelChecker<double> // Also raise the logger threshold for the log file, so that the model check infos aren't logged (useless and there are lots of them) // Lower it again after the model check. logger.getAppender("mainFileAppender")->setThreshold(log4cplus::WARN_LOG_LEVEL); - storm::storage::BitVector result = stateForm.check(*createPrctlModelChecker(model)); + storm::storage::BitVector result = stateForm.check(*createPrctlModelChecker(dtmc)); logger.getAppender("mainFileAppender")->setThreshold(log4cplus::INFO_LOG_LEVEL); - if((result & model.getInitialStates()).getNumberOfSetBits() == model.getInitialStates().getNumberOfSetBits()) { + if((result & dtmc.getInitialStates()).getNumberOfSetBits() == dtmc.getInitialStates().getNumberOfSetBits()) { std::cout << "Formula is satisfied. Can not generate counterexample.\n\n" << std::endl; LOG4CPLUS_INFO(logger, "Formula is satisfied. Can not generate counterexample."); delete formula; @@ -428,7 +434,7 @@ void checkPrctlFormulae(storm::modelchecker::prctl::AbstractModelChecker<double> } // Generate counterexample - storm::models::Dtmc<double> counterExample = storm::counterexamples::PathBasedSubsystemGenerator<double>::computeCriticalSubsystem(*parser.getModel<storm::models::Dtmc<double>>(), stateForm); + storm::models::Dtmc<double> counterExample = storm::counterexamples::PathBasedSubsystemGenerator<double>::computeCriticalSubsystem(dtmc, stateForm); LOG4CPLUS_INFO(logger, "Found counterexample."); @@ -480,6 +486,9 @@ int main(const int argc, const char* argv[]) { stormSetAlarm(timeout); } + // Execution Time measurement, start + std::chrono::high_resolution_clock::time_point executionStart = std::chrono::high_resolution_clock::now(); + // Now, the settings are received and the specified model is parsed. The actual actions taken depend on whether // the model was provided in explicit or symbolic format. if (s->isSet("explicit")) { @@ -495,37 +504,46 @@ int main(const int argc, const char* argv[]) { chosenTransitionRewardsFile = s->getOptionByLongName("transitionRewards").getArgument(0).getValueAsString(); } - storm::parser::AutoParser<double> parser(chosenTransitionSystemFile, chosenLabelingFile, chosenStateRewardsFile, chosenTransitionRewardsFile); + std::shared_ptr<storm::models::AbstractModel<double>> model = storm::parser::AutoParser::parseModel(chosenTransitionSystemFile, chosenLabelingFile, chosenStateRewardsFile, chosenTransitionRewardsFile); + + // Model Parsing Time Measurement, End + std::chrono::high_resolution_clock::time_point parsingEnd = std::chrono::high_resolution_clock::now(); + std::cout << "Parsing the given model took " << std::chrono::duration_cast<std::chrono::milliseconds>(parsingEnd - executionStart).count() << " milliseconds." << std::endl; if (s->isSet("exportdot")) { std::ofstream outputFileStream; outputFileStream.open(s->getOptionByLongName("exportdot").getArgument(0).getValueAsString(), std::ofstream::out); - parser.getModel<storm::models::AbstractModel<double>>()->writeDotToStream(outputFileStream); + model->writeDotToStream(outputFileStream); outputFileStream.close(); } - //Should there be a counterexample generated in case the formula is not satisfied? + // Should there be a counterexample generated in case the formula is not satisfied? if(s->isSet("counterexample")) { + // Counterexample Time Measurement, Start + std::chrono::high_resolution_clock::time_point counterexampleStart = std::chrono::high_resolution_clock::now(); - generateCounterExample(parser); + generateCounterExample(model); + // Counterexample Time Measurement, End + std::chrono::high_resolution_clock::time_point counterexampleEnd = std::chrono::high_resolution_clock::now(); + std::cout << "Generating the counterexample took " << std::chrono::duration_cast<std::chrono::milliseconds>(counterexampleEnd - counterexampleStart).count() << " milliseconds." << std::endl; } else { - // Determine which engine is to be used to choose the right model checker. - LOG4CPLUS_DEBUG(logger, s->getOptionByLongName("matrixLibrary").getArgument(0).getValueAsString()); - // Depending on the model type, the appropriate model checking procedure is chosen. storm::modelchecker::prctl::AbstractModelChecker<double>* modelchecker = nullptr; - parser.getModel<storm::models::AbstractModel<double>>()->printModelInformationToStream(std::cout); + model->printModelInformationToStream(std::cout); - switch (parser.getType()) { + // Modelchecking Time Measurement, Start + std::chrono::high_resolution_clock::time_point modelcheckingStart = std::chrono::high_resolution_clock::now(); + + switch (model->getType()) { case storm::models::DTMC: LOG4CPLUS_INFO(logger, "Model is a DTMC."); - modelchecker = createPrctlModelChecker(*parser.getModel<storm::models::Dtmc<double>>()); + modelchecker = createPrctlModelChecker(*model->as<storm::models::Dtmc<double>>()); checkPrctlFormulae(*modelchecker); break; case storm::models::MDP: LOG4CPLUS_INFO(logger, "Model is an MDP."); - modelchecker = createPrctlModelChecker(*parser.getModel<storm::models::Mdp<double>>()); + modelchecker = createPrctlModelChecker(*model->as<storm::models::Mdp<double>>()); checkPrctlFormulae(*modelchecker); break; case storm::models::CTMC: @@ -538,13 +556,13 @@ int main(const int argc, const char* argv[]) { break; case storm::models::MA: { LOG4CPLUS_INFO(logger, "Model is a Markov automaton."); - std::shared_ptr<storm::models::MarkovAutomaton<double>> markovAutomaton = parser.getModel<storm::models::MarkovAutomaton<double>>(); - markovAutomaton->close(); - storm::modelchecker::csl::SparseMarkovAutomatonCslModelChecker<double> mc(*markovAutomaton); + storm::models::MarkovAutomaton<double> markovAutomaton = *model->as<storm::models::MarkovAutomaton<double>>(); + markovAutomaton.close(); + storm::modelchecker::csl::SparseMarkovAutomatonCslModelChecker<double> mc(markovAutomaton); // std::cout << mc.checkExpectedTime(true, markovAutomaton->getLabeledStates("goal")) << std::endl; // std::cout << mc.checkExpectedTime(false, markovAutomaton->getLabeledStates("goal")) << std::endl; - std::cout << mc.checkLongRunAverage(true, markovAutomaton->getLabeledStates("goal")) << std::endl; - std::cout << mc.checkLongRunAverage(false, markovAutomaton->getLabeledStates("goal")) << std::endl; + std::cout << mc.checkLongRunAverage(true, markovAutomaton.getLabeledStates("goal")) << std::endl; + std::cout << mc.checkLongRunAverage(false, markovAutomaton.getLabeledStates("goal")) << std::endl; // std::cout << mc.checkTimeBoundedEventually(true, markovAutomaton->getLabeledStates("goal"), 0, 1) << std::endl; // std::cout << mc.checkTimeBoundedEventually(true, markovAutomaton->getLabeledStates("goal"), 1, 2) << std::endl; break; @@ -558,15 +576,26 @@ int main(const int argc, const char* argv[]) { if (modelchecker != nullptr) { delete modelchecker; } + + // Modelchecking Time Measurement, End + std::chrono::high_resolution_clock::time_point modelcheckingEnd = std::chrono::high_resolution_clock::now(); + std::cout << "Running the ModelChecker took " << std::chrono::duration_cast<std::chrono::milliseconds>(modelcheckingEnd - modelcheckingStart).count() << " milliseconds." << std::endl; } } else if (s->isSet("symbolic")) { + // Program Translation Time Measurement, Start + std::chrono::high_resolution_clock::time_point programTranslationStart = std::chrono::high_resolution_clock::now(); + // First, we build the model using the given symbolic model description and constant definitions. std::string const& programFile = s->getOptionByLongName("symbolic").getArgument(0).getValueAsString(); std::string const& constants = s->getOptionByLongName("constants").getArgument(0).getValueAsString(); - storm::ir::Program program = storm::parser::PrismParserFromFile(programFile); + storm::prism::Program program = storm::parser::PrismParser::parse(programFile); std::shared_ptr<storm::models::AbstractModel<double>> model = storm::adapters::ExplicitModelAdapter<double>::translateProgram(program, constants); model->printModelInformationToStream(std::cout); + // Program Translation Time Measurement, End + std::chrono::high_resolution_clock::time_point programTranslationEnd = std::chrono::high_resolution_clock::now(); + std::cout << "Parsing and translating the Symbolic Input took " << std::chrono::duration_cast<std::chrono::milliseconds>(programTranslationEnd - programTranslationStart).count() << " milliseconds." << std::endl; + if (s->isSet("mincmd")) { if (model->getType() != storm::models::MDP) { LOG4CPLUS_ERROR(logger, "Minimal command counterexample generation is only supported for models of type MDP."); @@ -578,6 +607,9 @@ int main(const int argc, const char* argv[]) { // Determine whether we are required to use the MILP-version or the SAT-version. bool useMILP = s->getOptionByLongName("mincmd").getArgumentByName("method").getValueAsString() == "milp"; + // MinCMD Time Measurement, Start + std::chrono::high_resolution_clock::time_point minCmdStart = std::chrono::high_resolution_clock::now(); + // Now parse the property file and receive the list of parsed formulas. std::string const& propertyFile = s->getOptionByLongName("mincmd").getArgumentByName("propertyFile").getValueAsString(); std::list<storm::property::prctl::AbstractPrctlFormula<double>*> formulaList = storm::parser::PrctlFileParser(propertyFile); @@ -587,19 +619,23 @@ int main(const int argc, const char* argv[]) { if (useMILP) { storm::counterexamples::MILPMinimalLabelSetGenerator<double>::computeCounterexample(program, *mdp, formulaPtr); } else { - storm::counterexamples::SMTMinimalCommandSetGenerator<double>::computeCounterexample(program, constants, *mdp, formulaPtr); + // storm::counterexamples::SMTMinimalCommandSetGenerator<double>::computeCounterexample(program, constants, *mdp, formulaPtr); } // Once we are done with the formula, delete it. delete formulaPtr; } + + // MinCMD Time Measurement, End + std::chrono::high_resolution_clock::time_point minCmdEnd = std::chrono::high_resolution_clock::now(); + std::cout << "Minimal command Counterexample generation took " << std::chrono::duration_cast<std::chrono::milliseconds>(minCmdStart - minCmdEnd).count() << " milliseconds." << std::endl; } else if (s->isSet("prctl")) { - // Determine which engine is to be used to choose the right model checker. - LOG4CPLUS_DEBUG(logger, s->getOptionByLongName("matrixLibrary").getArgument(0).getValueAsString()); - // Depending on the model type, the appropriate model checking procedure is chosen. storm::modelchecker::prctl::AbstractModelChecker<double>* modelchecker = nullptr; + // Modelchecking Time Measurement, Start + std::chrono::high_resolution_clock::time_point modelcheckingStart = std::chrono::high_resolution_clock::now(); + switch (model->getType()) { case storm::models::DTMC: LOG4CPLUS_INFO(logger, "Model is a DTMC."); @@ -631,8 +667,16 @@ int main(const int argc, const char* argv[]) { if (modelchecker != nullptr) { delete modelchecker; } + + // Modelchecking Time Measurement, End + std::chrono::high_resolution_clock::time_point modelcheckingEnd = std::chrono::high_resolution_clock::now(); + std::cout << "Running the PRCTL ModelChecker took " << std::chrono::duration_cast<std::chrono::milliseconds>(modelcheckingEnd - modelcheckingStart).count() << " milliseconds." << std::endl; } } + + // Execution Time Measurement, End + std::chrono::high_resolution_clock::time_point executionEnd = std::chrono::high_resolution_clock::now(); + std::cout << "Complete execution took " << std::chrono::duration_cast<std::chrono::milliseconds>(executionEnd - executionStart).count() << " milliseconds." << std::endl; // Perform clean-up and terminate. cleanUp(); diff --git a/src/utility/IRUtility.h b/src/utility/IRUtility.h deleted file mode 100644 index a75f1972d..000000000 --- a/src/utility/IRUtility.h +++ /dev/null @@ -1,517 +0,0 @@ -/* - * IRUtility.h - * - * Created on: 05.10.2013 - * Author: Christian Dehnert - */ - -#ifndef STORM_UTILITY_IRUTILITY_H_ -#define STORM_UTILITY_IRUTILITY_H_ - -#include <boost/algorithm/string.hpp> - -#include "src/storage/LabeledValues.h" -#include "src/ir/IR.h" -#include "src/exceptions/InvalidArgumentException.h" - -#include "log4cplus/logger.h" -#include "log4cplus/loggingmacros.h" - -extern log4cplus::Logger logger; - -namespace storm { - namespace utility { - namespace ir { - - /*! - * A state of the model, i.e. a valuation of all variables. - */ - typedef std::pair<std::vector<bool>, std::vector<int_fast64_t>> StateType; - - /*! - * A helper class that provides the functionality to compute a hash value for states of the model. - */ - class StateHash { - public: - std::size_t operator()(StateType* state) const { - size_t seed = 0; - for (auto it : state->first) { - boost::hash_combine<bool>(seed, it); - } - for (auto it : state->second) { - boost::hash_combine<int_fast64_t>(seed, it); - } - return seed; - } - }; - - /*! - * A helper class that provides the functionality to compare states of the model wrt. equality. - */ - class StateCompare { - public: - bool operator()(StateType* state1, StateType* state2) const { - return *state1 == *state2; - } - }; - - /*! - * A helper class that provides the functionality to compare states of the model wrt. the relation "<". - */ - class StateLess { - public: - bool operator()(StateType* state1, StateType* state2) const { - // Compare boolean variables. - for (uint_fast64_t i = 0; i < state1->first.size(); ++i) { - if (!state1->first.at(i) && state2->first.at(i)) { - return true; - } - } - // Then compare integer variables. - for (uint_fast64_t i = 0; i < state1->second.size(); ++i) { - if (!state1->second.at(i) && state2->second.at(i)) { - return true; - } - } - return false; - } - }; - - // A structure holding information about a particular choice. - template<typename ValueType, typename KeyType=uint_fast64_t, typename Compare=std::less<uint_fast64_t>> - struct Choice { - public: - Choice(std::string const& actionLabel) : distribution(), actionLabel(actionLabel), choiceLabels() { - // Intentionally left empty. - } - - /*! - * Returns an iterator to the first element of this choice. - * - * @return An iterator to the first element of this choice. - */ - typename std::map<KeyType, ValueType>::iterator begin() { - return distribution.begin(); - } - - /*! - * Returns an iterator to the first element of this choice. - * - * @return An iterator to the first element of this choice. - */ - typename std::map<KeyType, ValueType>::const_iterator begin() const { - return distribution.cbegin(); - } - - /*! - * Returns an iterator that points past the elements of this choice. - * - * @return An iterator that points past the elements of this choice. - */ - typename std::map<KeyType, ValueType>::iterator end() { - return distribution.end(); - } - - /*! - * Returns an iterator that points past the elements of this choice. - * - * @return An iterator that points past the elements of this choice. - */ - typename std::map<KeyType, ValueType>::const_iterator end() const { - return distribution.cend(); - } - - /*! - * Returns an iterator to the element with the given key, if there is one. Otherwise, the iterator points to - * distribution.end(). - * - * @param value The value to find. - * @return An iterator to the element with the given key, if there is one. - */ - typename std::map<KeyType, ValueType>::iterator find(uint_fast64_t value) { - return distribution.find(value); - } - - /*! - * Inserts the contents of this object to the given output stream. - * - * @param out The stream in which to insert the contents. - */ - friend std::ostream& operator<<(std::ostream& out, Choice<ValueType> const& choice) { - out << "<"; - for (auto const& stateProbabilityPair : choice.distribution) { - out << stateProbabilityPair.first << " : " << stateProbabilityPair.second << ", "; - } - out << ">"; - return out; - } - - /*! - * Adds the given label to the labels associated with this choice. - * - * @param label The label to associate with this choice. - */ - void addChoiceLabel(uint_fast64_t label) { - choiceLabels.insert(label); - } - - /*! - * Adds the given label set to the labels associated with this choice. - * - * @param labelSet The label set to associate with this choice. - */ - void addChoiceLabels(boost::container::flat_set<uint_fast64_t> const& labelSet) { - for (uint_fast64_t label : labelSet) { - addChoiceLabel(label); - } - } - - /*! - * Retrieves the set of labels associated with this choice. - * - * @return The set of labels associated with this choice. - */ - boost::container::flat_set<uint_fast64_t> const& getChoiceLabels() const { - return choiceLabels; - } - - /*! - * Retrieves the action label of this choice. - * - * @return The action label of this choice. - */ - std::string const& getActionLabel() const { - return actionLabel; - } - - /*! - * Retrieves the entry in the choice that is associated with the given state and creates one if none exists, - * yet. - * - * @param state The state for which to add the entry. - * @return A reference to the entry that is associated with the given state. - */ - ValueType& getOrAddEntry(uint_fast64_t state) { - auto stateProbabilityPair = distribution.find(state); - - if (stateProbabilityPair == distribution.end()) { - distribution[state] = ValueType(); - } - return distribution.at(state); - } - - /*! - * Retrieves the entry in the choice that is associated with the given state and creates one if none exists, - * yet. - * - * @param state The state for which to add the entry. - * @return A reference to the entry that is associated with the given state. - */ - ValueType const& getOrAddEntry(uint_fast64_t state) const { - auto stateProbabilityPair = distribution.find(state); - - if (stateProbabilityPair == distribution.end()) { - distribution[state] = ValueType(); - } - return distribution.at(state); - } - - private: - // The distribution that is associated with the choice. - std::map<KeyType, ValueType, Compare> distribution; - - // The label of the choice. - std::string actionLabel; - - // The labels that are associated with this choice. - boost::container::flat_set<uint_fast64_t> choiceLabels; - }; - - /*! - * Adds the target state and probability to the given choice and ignores the labels. This function overloads with - * other functions to ensure the proper treatment of labels. - * - * @param choice The choice to which to add the target state and probability. - * @param state The target state of the probability. - * @param probability The probability to reach the target state in one step. - * @param labels A set of labels that is supposed to be associated with this state and probability. NOTE: this - * is ignored by this particular function but not by the overloaded functions. - */ - template<typename ValueType> - void addProbabilityToChoice(Choice<ValueType>& choice, uint_fast64_t state, ValueType probability, boost::container::flat_set<uint_fast64_t> const& labels) { - choice.getOrAddEntry(state) += probability; - } - - /*! - * Adds the target state and probability to the given choice and labels it accordingly. This function overloads - * with other functions to ensure the proper treatment of labels. - * - * @param choice The choice to which to add the target state and probability. - * @param state The target state of the probability. - * @param probability The probability to reach the target state in one step. - * @param labels A set of labels that is supposed to be associated with this state and probability. - */ - template<typename ValueType> - void addProbabilityToChoice(Choice<storm::storage::LabeledValues<ValueType>>& choice, uint_fast64_t state, ValueType probability, boost::container::flat_set<uint_fast64_t> const& labels) { - auto& labeledEntry = choice.getOrAddEntry(state); - labeledEntry.addValue(probability, labels); - } - - // A structure storing information about the used variables of the program. - struct VariableInformation { - VariableInformation() : booleanVariables(), booleanVariableToIndexMap(), integerVariables(), integerVariableToIndexMap() { - // Intentinally left empty. - } - - // List of all boolean variables. - std::vector<storm::ir::BooleanVariable> booleanVariables; - - // A mapping of boolean variable names to their indices. - std::map<std::string, uint_fast64_t> booleanVariableToIndexMap; - - // List of all integer variables. - std::vector<storm::ir::IntegerVariable> integerVariables; - - // List of all lower bounds for integer variables. - std::vector<int_fast64_t> lowerBounds; - - // List of all upper bounds for integer variables. - std::vector<int_fast64_t> upperBounds; - - // A mapping of integer variable names to their indices. - std::map<std::string, uint_fast64_t> integerVariableToIndexMap; - }; - - /*! - * Aggregates information about the variables in the program. - * - * @param program The program whose variables to aggregate. - * @return A structure containing information about the variables in the program. - */ - static VariableInformation createVariableInformation(storm::ir::Program const& program) { - VariableInformation result; - - uint_fast64_t numberOfIntegerVariables = 0; - uint_fast64_t numberOfBooleanVariables = 0; - - // Count number of variables. - numberOfBooleanVariables += program.getNumberOfGlobalBooleanVariables(); - numberOfIntegerVariables += program.getNumberOfGlobalIntegerVariables(); - for (uint_fast64_t i = 0; i < program.getNumberOfModules(); ++i) { - numberOfBooleanVariables += program.getModule(i).getNumberOfBooleanVariables(); - numberOfIntegerVariables += program.getModule(i).getNumberOfIntegerVariables(); - } - - // Resize the variable vectors appropriately. - result.booleanVariables.resize(numberOfBooleanVariables); - result.integerVariables.resize(numberOfIntegerVariables); - result.lowerBounds.resize(numberOfIntegerVariables); - result.upperBounds.resize(numberOfIntegerVariables); - - // Create variables. - for (uint_fast64_t i = 0; i < program.getNumberOfGlobalBooleanVariables(); ++i) { - storm::ir::BooleanVariable const& var = program.getGlobalBooleanVariable(i); - result.booleanVariables[var.getGlobalIndex()] = var; - result.booleanVariableToIndexMap[var.getName()] = var.getGlobalIndex(); - } - for (uint_fast64_t i = 0; i < program.getNumberOfGlobalIntegerVariables(); ++i) { - storm::ir::IntegerVariable const& var = program.getGlobalIntegerVariable(i); - result.integerVariables[var.getGlobalIndex()] = var; - result.integerVariableToIndexMap[var.getName()] = var.getGlobalIndex(); - result.lowerBounds[var.getGlobalIndex()] = var.getLowerBound()->getValueAsInt(nullptr); - result.upperBounds[var.getGlobalIndex()] = var.getUpperBound()->getValueAsInt(nullptr); - } - for (uint_fast64_t i = 0; i < program.getNumberOfModules(); ++i) { - storm::ir::Module const& module = program.getModule(i); - - for (uint_fast64_t j = 0; j < module.getNumberOfBooleanVariables(); ++j) { - storm::ir::BooleanVariable const& var = module.getBooleanVariable(j); - result.booleanVariables[var.getGlobalIndex()] = var; - result.booleanVariableToIndexMap[var.getName()] = var.getGlobalIndex(); - } - for (uint_fast64_t j = 0; j < module.getNumberOfIntegerVariables(); ++j) { - storm::ir::IntegerVariable const& var = module.getIntegerVariable(j); - result.integerVariables[var.getGlobalIndex()] = var; - result.integerVariableToIndexMap[var.getName()] = var.getGlobalIndex(); - result.lowerBounds[var.getGlobalIndex()] = var.getLowerBound()->getValueAsInt(nullptr); - result.upperBounds[var.getGlobalIndex()] = var.getUpperBound()->getValueAsInt(nullptr); - } - } - - return result; - } - - /*! - * Generates the initial state of the given program. - * - * @param program The program for which to construct the initial state. - * @param variableInformation A structure with information about the variables in the program. - * @return The initial state. - */ - static StateType* getInitialState(storm::ir::Program const& program, VariableInformation const& variableInformation) { - StateType* initialState = new StateType(); - initialState->first.resize(variableInformation.booleanVariables.size()); - initialState->second.resize(variableInformation.integerVariables.size()); - - // Start with boolean variables. - for (uint_fast64_t i = 0; i < variableInformation.booleanVariables.size(); ++i) { - // Check if an initial value is given - if (variableInformation.booleanVariables[i].getInitialValue().get() == nullptr) { - // If no initial value was given, we assume that the variable is initially false. - std::get<0>(*initialState)[i] = false; - } else { - // Initial value was given. - bool initialValue = variableInformation.booleanVariables[i].getInitialValue()->getValueAsBool(nullptr); - std::get<0>(*initialState)[i] = initialValue; - } - } - - // Now process integer variables. - for (uint_fast64_t i = 0; i < variableInformation.integerVariables.size(); ++i) { - // Check if an initial value was given. - if (variableInformation.integerVariables[i].getInitialValue().get() == nullptr) { - // No initial value was given, so we assume that the variable initially has the least value it can take. - std::get<1>(*initialState)[i] = variableInformation.integerVariables[i].getLowerBound()->getValueAsInt(nullptr); - } else { - // Initial value was given. - int_fast64_t initialValue = variableInformation.integerVariables[i].getInitialValue()->getValueAsInt(nullptr); - std::get<1>(*initialState)[i] = initialValue; - } - } - - LOG4CPLUS_DEBUG(logger, "Generated initial state."); - return initialState; - } - - /*! - * Sets some boolean variable in the given state object. - * - * @param state The state to modify. - * @param index The index of the boolean variable to modify. - * @param value The new value of the variable. - */ - static void setValue(StateType* state, uint_fast64_t index, bool value) { - std::get<0>(*state)[index] = value; - } - - /*! - * Set some integer variable in the given state object. - * - * @param state The state to modify. - * @param index index of the integer variable to modify. - * @param value The new value of the variable. - */ - static void setValue(StateType* state, uint_fast64_t index, int_fast64_t value) { - std::get<1>(*state)[index] = value; - } - - /*! - * Defines the undefined constants of the given program using the given string. - * - * @param program The program in which to define the constants. - * @param constantDefinitionString A comma-separated list of constant definitions. - */ - static void defineUndefinedConstants(storm::ir::Program& program, std::string const& constantDefinitionString) { - if (!constantDefinitionString.empty()) { - // Parse the string that defines the undefined constants of the model and make sure that it contains exactly - // one value for each undefined constant of the model. - std::vector<std::string> definitions; - boost::split(definitions, constantDefinitionString, boost::is_any_of(",")); - for (auto& definition : definitions) { - boost::trim(definition); - - // Check whether the token could be a legal constant definition. - uint_fast64_t positionOfAssignmentOperator = definition.find('='); - if (positionOfAssignmentOperator == std::string::npos) { - throw storm::exceptions::InvalidArgumentException() << "Illegal constant definition string: syntax error."; - } - - // Now extract the variable name and the value from the string. - std::string constantName = definition.substr(0, positionOfAssignmentOperator); - boost::trim(constantName); - std::string value = definition.substr(positionOfAssignmentOperator + 1); - boost::trim(value); - - // Check whether the constant is a legal undefined constant of the program and if so, of what type it is. - if (program.hasUndefinedBooleanConstant(constantName)) { - if (value == "true") { - program.getUndefinedBooleanConstantExpression(constantName)->define(true); - } else if (value == "false") { - program.getUndefinedBooleanConstantExpression(constantName)->define(false); - } else { - throw storm::exceptions::InvalidArgumentException() << "Illegal value for boolean constant: " << value << "."; - } - } else if (program.hasUndefinedIntegerConstant(constantName)) { - try { - int_fast64_t integerValue = std::stoi(value); - program.getUndefinedIntegerConstantExpression(constantName)->define(integerValue); - } catch (std::invalid_argument const&) { - throw storm::exceptions::InvalidArgumentException() << "Illegal value of integer constant: " << value << "."; - } catch (std::out_of_range const&) { - throw storm::exceptions::InvalidArgumentException() << "Illegal value of integer constant: " << value << " (value too big)."; - } - } else if (program.hasUndefinedDoubleConstant(constantName)) { - try { - double doubleValue = std::stod(value); - program.getUndefinedDoubleConstantExpression(constantName)->define(doubleValue); - } catch (std::invalid_argument const&) { - throw storm::exceptions::InvalidArgumentException() << "Illegal value of double constant: " << value << "."; - } catch (std::out_of_range const&) { - throw storm::exceptions::InvalidArgumentException() << "Illegal value of double constant: " << value << " (value too big)."; - } - - } else { - throw storm::exceptions::InvalidArgumentException() << "Illegal constant definition string: unknown undefined constant " << constantName << "."; - } - } - } - } - - /*! - * Undefines all previously defined constants in the given program. - * - * @param program The program in which to undefine the constants. - */ - static void undefineUndefinedConstants(storm::ir::Program& program) { - for (auto const& nameExpressionPair : program.getBooleanUndefinedConstantExpressionsMap()) { - nameExpressionPair.second->undefine(); - } - for (auto const& nameExpressionPair : program.getIntegerUndefinedConstantExpressionsMap()) { - nameExpressionPair.second->undefine(); - } - for (auto const& nameExpressionPair : program.getDoubleUndefinedConstantExpressionsMap()) { - nameExpressionPair.second->undefine(); - } - } - - /*! - * Computes the weakest precondition of the given boolean expression wrt. the given updates. The weakest - * precondition is the most liberal expression that must hold in order to satisfy the given boolean - * expression after performing the updates. The updates must be disjoint in the sense that they must not - * assign an expression to a variable twice or more. - * - * @param expression The expression for which to build the weakest precondition. - * @param update The update with respect to which to compute the weakest precondition. - */ - std::unique_ptr<storm::ir::expressions::BaseExpression> getWeakestPrecondition(std::unique_ptr<storm::ir::expressions::BaseExpression> const& booleanExpression, std::vector<std::reference_wrapper<storm::ir::Update const>> const& updates) { - std::map<std::string, std::reference_wrapper<storm::ir::expressions::BaseExpression>> variableToExpressionMap; - - // Construct the full substitution we need to perform later. - for (auto const& update : updates) { - for (auto const& variableAssignmentPair : update.get().getBooleanAssignments()) { - variableToExpressionMap.emplace(variableAssignmentPair.first, *variableAssignmentPair.second.getExpression()); - } - for (auto const& variableAssignmentPair : update.get().getIntegerAssignments()) { - variableToExpressionMap.emplace(variableAssignmentPair.first, *variableAssignmentPair.second.getExpression()); - } - } - - // Copy the given expression and apply the substitution. - return storm::ir::expressions::BaseExpression::substitute(booleanExpression->clone(), variableToExpressionMap); - } - - } // namespace ir - } // namespace utility -} // namespace storm - -#endif /* STORM_UTILITY_IRUTILITY_H_ */ diff --git a/src/utility/PrismUtility.h b/src/utility/PrismUtility.h new file mode 100644 index 000000000..9ed15411a --- /dev/null +++ b/src/utility/PrismUtility.h @@ -0,0 +1,195 @@ +#ifndef STORM_UTILITY_PRISMUTILITY +#define STORM_UTILITY_PRISMUTILITY + +#include "src/storage/LabeledValues.h" +#include "src/storage/prism/Program.h" +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidArgumentException.h" + +namespace storm { + namespace utility { + namespace prism { + // A structure holding information about a particular choice. + template<typename ValueType, typename KeyType=uint_fast64_t, typename Compare=std::less<uint_fast64_t>> + struct Choice { + public: + Choice(std::string const& actionLabel) : distribution(), actionLabel(actionLabel), choiceLabels() { + // Intentionally left empty. + } + + /*! + * Returns an iterator to the first element of this choice. + * + * @return An iterator to the first element of this choice. + */ + typename std::map<KeyType, ValueType>::iterator begin() { + return distribution.begin(); + } + + /*! + * Returns an iterator to the first element of this choice. + * + * @return An iterator to the first element of this choice. + */ + typename std::map<KeyType, ValueType>::const_iterator begin() const { + return distribution.cbegin(); + } + + /*! + * Returns an iterator that points past the elements of this choice. + * + * @return An iterator that points past the elements of this choice. + */ + typename std::map<KeyType, ValueType>::iterator end() { + return distribution.end(); + } + + /*! + * Returns an iterator that points past the elements of this choice. + * + * @return An iterator that points past the elements of this choice. + */ + typename std::map<KeyType, ValueType>::const_iterator end() const { + return distribution.cend(); + } + + /*! + * Returns an iterator to the element with the given key, if there is one. Otherwise, the iterator points to + * distribution.end(). + * + * @param value The value to find. + * @return An iterator to the element with the given key, if there is one. + */ + typename std::map<KeyType, ValueType>::iterator find(uint_fast64_t value) { + return distribution.find(value); + } + + /*! + * Inserts the contents of this object to the given output stream. + * + * @param out The stream in which to insert the contents. + */ + friend std::ostream& operator<<(std::ostream& out, Choice<ValueType> const& choice) { + out << "<"; + for (auto const& stateProbabilityPair : choice.distribution) { + out << stateProbabilityPair.first << " : " << stateProbabilityPair.second << ", "; + } + out << ">"; + return out; + } + + /*! + * Adds the given label to the labels associated with this choice. + * + * @param label The label to associate with this choice. + */ + void addChoiceLabel(uint_fast64_t label) { + choiceLabels.insert(label); + } + + /*! + * Adds the given label set to the labels associated with this choice. + * + * @param labelSet The label set to associate with this choice. + */ + void addChoiceLabels(boost::container::flat_set<uint_fast64_t> const& labelSet) { + for (uint_fast64_t label : labelSet) { + addChoiceLabel(label); + } + } + + /*! + * Retrieves the set of labels associated with this choice. + * + * @return The set of labels associated with this choice. + */ + boost::container::flat_set<uint_fast64_t> const& getChoiceLabels() const { + return choiceLabels; + } + + /*! + * Retrieves the action label of this choice. + * + * @return The action label of this choice. + */ + std::string const& getActionLabel() const { + return actionLabel; + } + + /*! + * Retrieves the entry in the choice that is associated with the given state and creates one if none exists, + * yet. + * + * @param state The state for which to add the entry. + * @return A reference to the entry that is associated with the given state. + */ + ValueType& getOrAddEntry(uint_fast64_t state) { + auto stateProbabilityPair = distribution.find(state); + + if (stateProbabilityPair == distribution.end()) { + distribution[state] = ValueType(); + } + return distribution.at(state); + } + + /*! + * Retrieves the entry in the choice that is associated with the given state and creates one if none exists, + * yet. + * + * @param state The state for which to add the entry. + * @return A reference to the entry that is associated with the given state. + */ + ValueType const& getOrAddEntry(uint_fast64_t state) const { + auto stateProbabilityPair = distribution.find(state); + + if (stateProbabilityPair == distribution.end()) { + distribution[state] = ValueType(); + } + return distribution.at(state); + } + + private: + // The distribution that is associated with the choice. + std::map<KeyType, ValueType, Compare> distribution; + + // The label of the choice. + std::string actionLabel; + + // The labels that are associated with this choice. + boost::container::flat_set<uint_fast64_t> choiceLabels; + }; + + /*! + * Adds the target state and probability to the given choice and ignores the labels. This function overloads with + * other functions to ensure the proper treatment of labels. + * + * @param choice The choice to which to add the target state and probability. + * @param state The target state of the probability. + * @param probability The probability to reach the target state in one step. + * @param labels A set of labels that is supposed to be associated with this state and probability. NOTE: this + * is ignored by this particular function but not by the overloaded functions. + */ + template<typename ValueType> + void addProbabilityToChoice(Choice<ValueType>& choice, uint_fast64_t state, ValueType probability, boost::container::flat_set<uint_fast64_t> const& labels) { + choice.getOrAddEntry(state) += probability; + } + + /*! + * Adds the target state and probability to the given choice and labels it accordingly. This function overloads + * with other functions to ensure the proper treatment of labels. + * + * @param choice The choice to which to add the target state and probability. + * @param state The target state of the probability. + * @param probability The probability to reach the target state in one step. + * @param labels A set of labels that is supposed to be associated with this state and probability. + */ + template<typename ValueType> + void addProbabilityToChoice(Choice<storm::storage::LabeledValues<ValueType>>& choice, uint_fast64_t state, ValueType probability, boost::container::flat_set<uint_fast64_t> const& labels) { + auto& labeledEntry = choice.getOrAddEntry(state); + labeledEntry.addValue(probability, labels); + } + } // namespace prism + } // namespace utility +} // namespace storm + +#endif /* STORM_UTILITY_PRISMUTILITY_H_ */ diff --git a/src/utility/StormOptions.cpp b/src/utility/StormOptions.cpp index 29f6dccfd..48b7aa3e0 100644 --- a/src/utility/StormOptions.cpp +++ b/src/utility/StormOptions.cpp @@ -28,7 +28,7 @@ bool storm::utility::StormOptions::optionsRegistered = storm::settings::Settings settings->addOption(storm::settings::OptionBuilder("StoRM Main", "counterExample", "", "Generates a counterexample for the given PRCTL formulas if not satisfied by the model").addArgument(storm::settings::ArgumentBuilder::createStringArgument("outputPath", "The path to the directory to write the generated counterexample files to.").build()).build()); - settings->addOption(storm::settings::OptionBuilder("StoRM Main", "transitionRewards", "", "If specified, the transition rewards are read from this file and added to the explicit model. Note that this requires an explicit model.").addArgument(storm::settings::ArgumentBuilder::createStringArgument("transitionRewardsFileName", "The file from which to read the rransition rewards.").addValidationFunctionString(storm::settings::ArgumentValidators::existingReadableFileValidator()).build()).build()); + settings->addOption(storm::settings::OptionBuilder("StoRM Main", "transitionRewards", "", "If specified, the transition rewards are read from this file and added to the explicit model. Note that this requires an explicit model.").addArgument(storm::settings::ArgumentBuilder::createStringArgument("transitionRewardsFileName", "The file from which to read the transition rewards.").addValidationFunctionString(storm::settings::ArgumentValidators::existingReadableFileValidator()).build()).build()); settings->addOption(storm::settings::OptionBuilder("StoRM Main", "stateRewards", "", "If specified, the state rewards are read from this file and added to the explicit model. Note that this requires an explicit model.").addArgument(storm::settings::ArgumentBuilder::createStringArgument("stateRewardsFileName", "The file from which to read the state rewards.").addValidationFunctionString(storm::settings::ArgumentValidators::existingReadableFileValidator()).build()).build()); diff --git a/src/utility/counterexamples.h b/src/utility/counterexamples.h index 23b5b372e..8b233e402 100644 --- a/src/utility/counterexamples.h +++ b/src/utility/counterexamples.h @@ -36,8 +36,8 @@ namespace storm { // for (auto state : psiStates) { // analysisInformation[state] = boost::container::flat_set<uint_fast64_t>(); // for (auto const& predecessorEntry : backwardTransitions.getRow(state)) { -// if (predecessorEntry.first != state && !psiStates.get(predecessorEntry.first)) { -// worklist.push(std::make_pair(predecessorEntry.first, state)); +// if (predecessorEntry.getColumn() != state && !psiStates.get(predecessorEntry.getColumn())) { +// worklist.push(std::make_pair(predecessorEntry.getColumn(), state)); // } // } // } @@ -57,7 +57,7 @@ namespace storm { // bool choiceTargetsTargetState = false; // // for (auto& entry : transitionMatrix.getRow(currentChoice)) { -// if (entry.first == targetState) { +// if (entry.getColumn() == targetState) { // choiceTargetsTargetState = true; // break; // } @@ -79,8 +79,8 @@ namespace storm { // if (analysisInformation[currentState].size() != analysisInformationSizeBefore) { // for (auto& predecessorEntry : backwardTransitions.getRow(currentState)) { // // Only put the predecessor in the worklist if it's not already a target state. -// if (!psiStates.get(predecessorEntry.first)) { -// worklist.push(std::make_pair(predecessorEntry.first, currentState)); +// if (!psiStates.get(predecessorEntry.getColumn())) { +// worklist.push(std::make_pair(predecessorEntry.getColumn(), currentState)); // } // } // } @@ -96,9 +96,9 @@ namespace storm { for (auto state : psiStates) { analysisInformation[state] = boost::container::flat_set<uint_fast64_t>(); for (auto const& predecessorEntry : backwardTransitions.getRow(state)) { - if (predecessorEntry.first != state && !statesInWorkList.get(predecessorEntry.first) && !psiStates.get(predecessorEntry.first)) { - worklist.push(predecessorEntry.first); - statesInWorkList.set(predecessorEntry.first); + if (predecessorEntry.getColumn() != state && !statesInWorkList.get(predecessorEntry.getColumn()) && !psiStates.get(predecessorEntry.getColumn())) { + worklist.push(predecessorEntry.getColumn()); + statesInWorkList.set(predecessorEntry.getColumn()); markedStates.set(state); } } @@ -116,7 +116,7 @@ namespace storm { bool modifiedChoice = false; for (auto const& entry : transitionMatrix.getRow(currentChoice)) { - if (markedStates.get(entry.first)) { + if (markedStates.get(entry.getColumn())) { modifiedChoice = true; break; } @@ -127,9 +127,9 @@ namespace storm { // and the choice labels. if (modifiedChoice) { for (auto const& entry : transitionMatrix.getRow(currentChoice)) { - if (markedStates.get(entry.first)) { + if (markedStates.get(entry.getColumn())) { boost::container::flat_set<uint_fast64_t> tmpIntersection; - std::set_intersection(analysisInformation[currentState].begin(), analysisInformation[currentState].end(), analysisInformation[entry.first].begin(), analysisInformation[entry.first].end(), std::inserter(tmpIntersection, tmpIntersection.begin())); + std::set_intersection(analysisInformation[currentState].begin(), analysisInformation[currentState].end(), analysisInformation[entry.getColumn()].begin(), analysisInformation[entry.getColumn()].end(), std::inserter(tmpIntersection, tmpIntersection.begin())); std::set_intersection(analysisInformation[currentState].begin(), analysisInformation[currentState].end(), choiceLabeling[currentChoice].begin(), choiceLabeling[currentChoice].end(), std::inserter(tmpIntersection, tmpIntersection.begin())); analysisInformation[currentState] = std::move(tmpIntersection); } @@ -142,9 +142,9 @@ namespace storm { if (analysisInformation[currentState].size() != analysisInformationSizeBefore) { for (auto const& predecessorEntry : backwardTransitions.getRow(currentState)) { // Only put the predecessor in the worklist if it's not already a target state. - if (!psiStates.get(predecessorEntry.first) && !statesInWorkList.get(predecessorEntry.first)) { - worklist.push(predecessorEntry.first); - statesInWorkList.set(predecessorEntry.first); + if (!psiStates.get(predecessorEntry.getColumn()) && !statesInWorkList.get(predecessorEntry.getColumn())) { + worklist.push(predecessorEntry.getColumn()); + statesInWorkList.set(predecessorEntry.getColumn()); } } markedStates.set(currentState, true); diff --git a/src/utility/cstring.cpp b/src/utility/cstring.cpp new file mode 100644 index 000000000..2267fdf62 --- /dev/null +++ b/src/utility/cstring.cpp @@ -0,0 +1,95 @@ +#include "src/utility/cstring.h" + +#include <cstring> + +#include "src/exceptions/WrongFormatException.h" + +#include "log4cplus/logger.h" +#include "log4cplus/loggingmacros.h" +extern log4cplus::Logger logger; + +namespace storm { + +namespace utility { + +namespace cstring { + +/*! + * Calls strtol() internally and checks if the new pointer is different + * from the original one, i.e. if str != *end. If they are the same, a + * storm::exceptions::WrongFormatException will be thrown. + * @param str String to parse + * @param end New pointer will be written there + * @return Result of strtol() + */ +uint_fast64_t checked_strtol(const char* str, char** end) { + uint_fast64_t res = strtol(str, end, 10); + if (str == *end) { + LOG4CPLUS_ERROR(logger, "Error while parsing integer. Next input token is not a number."); + LOG4CPLUS_ERROR(logger, "\tUpcoming input is: \"" << std::string(str, 0, 16) << "\""); + throw storm::exceptions::WrongFormatException("Error while parsing integer. Next input token is not a number."); + } + return res; +} + +/*! + * Calls strtod() internally and checks if the new pointer is different + * from the original one, i.e. if str != *end. If they are the same, a + * storm::exceptions::WrongFormatException will be thrown. + * @param str String to parse + * @param end New pointer will be written there + * @return Result of strtod() + */ +double checked_strtod(const char* str, char** end) { + double res = strtod(str, end); + if (str == *end) { + LOG4CPLUS_ERROR(logger, "Error while parsing floating point. Next input token is not a number."); + LOG4CPLUS_ERROR(logger, "\tUpcoming input is: \"" << std::string(str, 0, 16) << "\""); + throw storm::exceptions::WrongFormatException("Error while parsing floating point. Next input token is not a number."); + } + return res; +} + +/*! + * Skips all numbers, letters and special characters. + * Returns a pointer to the first char that is a whitespace. + * @param buf The string buffer to operate on. + * @return A pointer to the first whitespace character. + */ +char* skipWord(char* buf){ + while(!isspace(*buf) && *buf != '\0') buf++; + return buf; +} + +/*! + * Skips spaces, tabs, newlines and carriage returns. Returns a pointer + * to first char that is not a whitespace. + * @param buf The string buffer to operate on. + * @return A pointer to the first non-whitespace character. + */ +char* trimWhitespaces(char* buf) { + while (isspace(*buf)) buf++; + return buf; +} + +/*! + * @brief Encapsulates the usage of function @strcspn to forward to the end of the line (next char is the newline character). + */ +char* forwardToLineEnd(char* buffer) { + return buffer + strcspn(buffer, "\n\r\0"); +} + +/*! + * @brief Encapsulates the usage of function @strchr to forward to the next line + */ +char* forwardToNextLine(char* buffer) { + char* lineEnd = forwardToLineEnd(buffer); + while((*lineEnd == '\n') || (*lineEnd == '\r')) lineEnd++; + return lineEnd; +} + +} // namespace cstring + +} // namespace utility + +} // namespace storm diff --git a/src/utility/cstring.h b/src/utility/cstring.h new file mode 100644 index 000000000..ebe153f3b --- /dev/null +++ b/src/utility/cstring.h @@ -0,0 +1,53 @@ +/* + * cstring.h + * + * Created on: 30.01.2014 + * Author: Manuel Sascha Weiand + */ + +#ifndef STORM_UTILITY_CSTRING_H_ +#define STORM_UTILITY_CSTRING_H_ + +#include <cstdint> + +namespace storm { + namespace utility { + namespace cstring { + + /*! + * @brief Parses integer and checks, if something has been parsed. + */ + uint_fast64_t checked_strtol(const char* str, char** end); + + /*! + * @brief Parses floating point and checks, if something has been parsed. + */ + double checked_strtod(const char* str, char** end); + + /*! + * @brief Skips all non whitespace characters until the next whitespace. + */ + char* skipWord(char* buf); + + /*! + * @brief Skips common whitespaces in a string. + */ + char* trimWhitespaces(char* buf); + + /*! + * @brief Encapsulates the usage of function @strcspn to forward to the end of the line (next char is the newline character). + */ + char* forwardToLineEnd(char* buffer); + + /*! + * @brief Encapsulates the usage of function @strchr to forward to the next line + * + * Note: All lines after the current, which do not contain any characters are skipped. + */ + char* forwardToNextLine(char* buffer); + + } // namespace cstring + } // namespace utility +} // namespace storm + +#endif /* STORM_UTILITY_CSTRING_H_ */ diff --git a/src/utility/graph.h b/src/utility/graph.h index e9757d50e..32f311304 100644 --- a/src/utility/graph.h +++ b/src/utility/graph.h @@ -79,16 +79,16 @@ namespace storm { } for (typename storm::storage::SparseMatrix<T>::const_iterator entryIt = backwardTransitions.begin(currentState), entryIte = backwardTransitions.end(currentState); entryIt != entryIte; ++entryIt) { - if (phiStates[entryIt->first] && (!statesWithProbabilityGreater0.get(entryIt->first) || (useStepBound && remainingSteps[entryIt->first] < currentStepBound - 1))) { + if (phiStates[entryIt->getColumn()] && (!statesWithProbabilityGreater0.get(entryIt->getColumn()) || (useStepBound && remainingSteps[entryIt->getColumn()] < currentStepBound - 1))) { // If we don't have a bound on the number of steps to take, just add the state to the stack. if (!useStepBound) { - statesWithProbabilityGreater0.set(entryIt->first, true); - stack.push_back(entryIt->first); + statesWithProbabilityGreater0.set(entryIt->getColumn(), true); + stack.push_back(entryIt->getColumn()); } else if (currentStepBound > 0) { // If there is at least one more step to go, we need to push the state and the new number of steps. - remainingSteps[entryIt->first] = currentStepBound - 1; - statesWithProbabilityGreater0.set(entryIt->first, true); - stack.push_back(entryIt->first); + remainingSteps[entryIt->getColumn()] = currentStepBound - 1; + statesWithProbabilityGreater0.set(entryIt->getColumn(), true); + stack.push_back(entryIt->getColumn()); stepStack.push_back(currentStepBound - 1); } } @@ -211,16 +211,16 @@ namespace storm { } for (typename storm::storage::SparseMatrix<T>::const_iterator entryIt = backwardTransitions.begin(currentState), entryIte = backwardTransitions.end(currentState); entryIt != entryIte; ++entryIt) { - if (phiStates.get(entryIt->first) && (!statesWithProbabilityGreater0.get(entryIt->first) || (useStepBound && remainingSteps[entryIt->first] < currentStepBound - 1))) { + if (phiStates.get(entryIt->getColumn()) && (!statesWithProbabilityGreater0.get(entryIt->getColumn()) || (useStepBound && remainingSteps[entryIt->getColumn()] < currentStepBound - 1))) { // If we don't have a bound on the number of steps to take, just add the state to the stack. if (!useStepBound) { - statesWithProbabilityGreater0.set(entryIt->first, true); - stack.push_back(entryIt->first); + statesWithProbabilityGreater0.set(entryIt->getColumn(), true); + stack.push_back(entryIt->getColumn()); } else if (currentStepBound > 0) { // If there is at least one more step to go, we need to push the state and the new number of steps. - remainingSteps[entryIt->first] = currentStepBound - 1; - statesWithProbabilityGreater0.set(entryIt->first, true); - stack.push_back(entryIt->first); + remainingSteps[entryIt->getColumn()] = currentStepBound - 1; + statesWithProbabilityGreater0.set(entryIt->getColumn(), true); + stack.push_back(entryIt->getColumn()); stepStack.push_back(currentStepBound - 1); } } @@ -290,13 +290,13 @@ namespace storm { stack.pop_back(); for (typename storm::storage::SparseMatrix<T>::const_iterator predecessorEntryIt = backwardTransitions.begin(currentState), predecessorEntryIte = backwardTransitions.end(currentState); predecessorEntryIt != predecessorEntryIte; ++predecessorEntryIt) { - if (phiStates.get(predecessorEntryIt->first) && !nextStates.get(predecessorEntryIt->first)) { + if (phiStates.get(predecessorEntryIt->getColumn()) && !nextStates.get(predecessorEntryIt->getColumn())) { // Check whether the predecessor has only successors in the current state set for one of the // nondeterminstic choices. - for (uint_fast64_t row = nondeterministicChoiceIndices[predecessorEntryIt->first]; row < nondeterministicChoiceIndices[predecessorEntryIt->first + 1]; ++row) { + for (uint_fast64_t row = nondeterministicChoiceIndices[predecessorEntryIt->getColumn()]; row < nondeterministicChoiceIndices[predecessorEntryIt->getColumn() + 1]; ++row) { bool allSuccessorsInCurrentStates = true; for (typename storm::storage::SparseMatrix<T>::const_iterator successorEntryIt = transitionMatrix.begin(row), successorEntryIte = transitionMatrix.end(row); successorEntryIt != successorEntryIte; ++successorEntryIt) { - if (!currentStates.get(successorEntryIt->first)) { + if (!currentStates.get(successorEntryIt->getColumn())) { allSuccessorsInCurrentStates = false; break; } @@ -306,8 +306,8 @@ namespace storm { // add it to the set of states for the next iteration and perform a backward search from // that state. if (allSuccessorsInCurrentStates) { - nextStates.set(predecessorEntryIt->first, true); - stack.push_back(predecessorEntryIt->first); + nextStates.set(predecessorEntryIt->getColumn(), true); + stack.push_back(predecessorEntryIt->getColumn()); break; } } @@ -401,14 +401,14 @@ namespace storm { } for(typename storm::storage::SparseMatrix<T>::const_iterator predecessorEntryIt = backwardTransitions.begin(currentState), predecessorEntryIte = backwardTransitions.end(currentState); predecessorEntryIt != predecessorEntryIte; ++predecessorEntryIt) { - if (phiStates.get(predecessorEntryIt->first) && (!statesWithProbabilityGreater0.get(predecessorEntryIt->first) || (useStepBound && remainingSteps[predecessorEntryIt->first] < currentStepBound - 1))) { + if (phiStates.get(predecessorEntryIt->getColumn()) && (!statesWithProbabilityGreater0.get(predecessorEntryIt->getColumn()) || (useStepBound && remainingSteps[predecessorEntryIt->getColumn()] < currentStepBound - 1))) { // Check whether the predecessor has at least one successor in the current state set for every // nondeterministic choice. bool addToStatesWithProbabilityGreater0 = true; - for (uint_fast64_t row = nondeterministicChoiceIndices[predecessorEntryIt->first]; row < nondeterministicChoiceIndices[predecessorEntryIt->first + 1]; ++row) { + for (uint_fast64_t row = nondeterministicChoiceIndices[predecessorEntryIt->getColumn()]; row < nondeterministicChoiceIndices[predecessorEntryIt->getColumn() + 1]; ++row) { bool hasAtLeastOneSuccessorWithProbabilityGreater0 = false; for (typename storm::storage::SparseMatrix<T>::const_iterator successorEntryIt = transitionMatrix.begin(row), successorEntryIte = transitionMatrix.end(row); successorEntryIt != successorEntryIte; ++successorEntryIt) { - if (statesWithProbabilityGreater0.get(successorEntryIt->first)) { + if (statesWithProbabilityGreater0.get(successorEntryIt->getColumn())) { hasAtLeastOneSuccessorWithProbabilityGreater0 = true; break; } @@ -424,13 +424,13 @@ namespace storm { if (addToStatesWithProbabilityGreater0) { // If we don't have a bound on the number of steps to take, just add the state to the stack. if (!useStepBound) { - statesWithProbabilityGreater0.set(predecessorEntryIt->first, true); - stack.push_back(predecessorEntryIt->first); + statesWithProbabilityGreater0.set(predecessorEntryIt->getColumn(), true); + stack.push_back(predecessorEntryIt->getColumn()); } else if (currentStepBound > 0) { // If there is at least one more step to go, we need to push the state and the new number of steps. - remainingSteps[predecessorEntryIt->first] = currentStepBound - 1; - statesWithProbabilityGreater0.set(predecessorEntryIt->first, true); - stack.push_back(predecessorEntryIt->first); + remainingSteps[predecessorEntryIt->getColumn()] = currentStepBound - 1; + statesWithProbabilityGreater0.set(predecessorEntryIt->getColumn(), true); + stack.push_back(predecessorEntryIt->getColumn()); stepStack.push_back(currentStepBound - 1); } } @@ -501,12 +501,12 @@ namespace storm { stack.pop_back(); for(typename storm::storage::SparseMatrix<T>::const_iterator predecessorEntryIt = backwardTransitions.begin(currentState), predecessorEntryIte = backwardTransitions.end(currentState); predecessorEntryIt != predecessorEntryIte; ++predecessorEntryIt) { - if (phiStates.get(predecessorEntryIt->first) && !nextStates.get(predecessorEntryIt->first)) { + if (phiStates.get(predecessorEntryIt->getColumn()) && !nextStates.get(predecessorEntryIt->getColumn())) { // Check whether the predecessor has only successors in the current state set for all of the // nondeterminstic choices. bool allSuccessorsInCurrentStatesForAllChoices = true; - for (typename storm::storage::SparseMatrix<T>::const_iterator successorEntryIt = transitionMatrix.begin(nondeterministicChoiceIndices[predecessorEntryIt->first]), successorEntryIte = transitionMatrix.begin(nondeterministicChoiceIndices[predecessorEntryIt->first + 1]); successorEntryIt != successorEntryIte; ++successorEntryIt) { - if (!currentStates.get(successorEntryIt->first)) { + for (typename storm::storage::SparseMatrix<T>::const_iterator successorEntryIt = transitionMatrix.begin(nondeterministicChoiceIndices[predecessorEntryIt->getColumn()]), successorEntryIte = transitionMatrix.begin(nondeterministicChoiceIndices[predecessorEntryIt->getColumn() + 1]); successorEntryIt != successorEntryIte; ++successorEntryIt) { + if (!currentStates.get(successorEntryIt->getColumn())) { allSuccessorsInCurrentStatesForAllChoices = false; goto afterCheckLoop; } @@ -517,8 +517,8 @@ namespace storm { // add it to the set of states for the next iteration and perform a backward search from // that state. if (allSuccessorsInCurrentStatesForAllChoices) { - nextStates.set(predecessorEntryIt->first, true); - stack.push_back(predecessorEntryIt->first); + nextStates.set(predecessorEntryIt->getColumn(), true); + stack.push_back(predecessorEntryIt->getColumn()); } } } diff --git a/src/utility/matrix.h b/src/utility/matrix.h index 446fb413f..1027e7e42 100644 --- a/src/utility/matrix.h +++ b/src/utility/matrix.h @@ -32,7 +32,7 @@ namespace storm { // If a valid choice for this state is defined, we copy over the corresponding entries. typename storm::storage::SparseMatrix<T>::const_rows selectedRow = transitionMatrix.getRow(choice); for (auto const& entry : selectedRow) { - matrixBuilder.addNextValue(state, entry.first, entry.second); + matrixBuilder.addNextValue(state, entry.getColumn(), entry.getValue()); } } else { // If no valid choice for the state is defined, we insert a self-loop. diff --git a/src/utility/vector.h b/src/utility/vector.h index ab283a4dc..6dca02082 100644 --- a/src/utility/vector.h +++ b/src/utility/vector.h @@ -94,7 +94,6 @@ namespace storm { */ template<class T> void selectVectorValues(std::vector<T>& vector, std::vector<uint_fast64_t> const& rowGroupToRowIndexMapping, std::vector<uint_fast64_t> const& rowGrouping, std::vector<T> const& values) { - uint_fast64_t oldPosition = 0; for (uint_fast64_t i = 0; i < vector.size(); ++i) { vector[i] = values[rowGrouping[i] + rowGroupToRowIndexMapping[i]]; } diff --git a/storm-config.h.in b/storm-config.h.in index 5882ba105..5002c3829 100644 --- a/storm-config.h.in +++ b/storm-config.h.in @@ -8,13 +8,6 @@ #ifndef STORM_GENERATED_STORMCONFIG_H_ #define STORM_GENERATED_STORMCONFIG_H_ -// Version Information -#define STORM_CPP_VERSION_MAJOR @STORM_CPP_VERSION_MAJOR@ // The major version of StoRM -#define STORM_CPP_VERSION_MINOR @STORM_CPP_VERSION_MINOR@ // The minor version of StoRM -#define STORM_CPP_VERSION_PATCH @STORM_CPP_VERSION_PATCH@ // The patch version of StoRM -#define STORM_CPP_VERSION_COMMITS_AHEAD @STORM_CPP_VERSION_COMMITS_AHEAD@ // How many commits passed since the tag was last set -#define STORM_CPP_VERSION_HASH "@STORM_CPP_VERSION_HASH@" // The short hash of the git commit this build is bases on -#define STORM_CPP_VERSION_DIRTY @STORM_CPP_VERSION_DIRTY@ // 0 iff there no files were modified in the checkout, 1 else // The path of the sources from which StoRM will be/was build #define STORM_CPP_BASE_PATH "@PROJECT_SOURCE_DIR@" diff --git a/storm-version.h.in b/storm-version.h.in new file mode 100644 index 000000000..5ccac6125 --- /dev/null +++ b/storm-version.h.in @@ -0,0 +1,12 @@ +#ifndef STORM_GENERATED_VERSION_H_ +#define STORM_GENERATED_VERSION_H_ + +// Version Information +#define STORM_CPP_VERSION_MAJOR @STORM_CPP_VERSION_MAJOR@ // The major version of StoRM +#define STORM_CPP_VERSION_MINOR @STORM_CPP_VERSION_MINOR@ // The minor version of StoRM +#define STORM_CPP_VERSION_PATCH @STORM_CPP_VERSION_PATCH@ // The patch version of StoRM +#define STORM_CPP_VERSION_COMMITS_AHEAD @STORM_CPP_VERSION_COMMITS_AHEAD@ // How many commits passed since the tag was last set +#define STORM_CPP_VERSION_HASH "@STORM_CPP_VERSION_HASH@" // The short hash of the git commit this build is bases on +#define STORM_CPP_VERSION_DIRTY @STORM_CPP_VERSION_DIRTY@ // 0 iff there no files were modified in the checkout, 1 else + +#endif \ No newline at end of file diff --git a/test/functional/modelchecker/GmmxxDtmcPrctlModelCheckerTest.cpp b/test/functional/modelchecker/GmmxxDtmcPrctlModelCheckerTest.cpp index d724ef23d..dc2d6d703 100644 --- a/test/functional/modelchecker/GmmxxDtmcPrctlModelCheckerTest.cpp +++ b/test/functional/modelchecker/GmmxxDtmcPrctlModelCheckerTest.cpp @@ -11,14 +11,14 @@ TEST(GmmxxDtmcPrctlModelCheckerTest, Die) { storm::settings::Settings* s = storm::settings::Settings::getInstance(); storm::settings::InternalOptionMemento deadlockOption("fixDeadlocks", true); ASSERT_TRUE(s->isSet("fixDeadlocks")); - storm::parser::AutoParser<double> parser(STORM_CPP_BASE_PATH "/examples/dtmc/die/die.tra", STORM_CPP_BASE_PATH "/examples/dtmc/die/die.lab", "", STORM_CPP_BASE_PATH "/examples/dtmc/die/die.coin_flips.trans.rew"); + std::shared_ptr<storm::models::AbstractModel<double>> abstractModel = storm::parser::AutoParser::parseModel(STORM_CPP_BASE_PATH "/examples/dtmc/die/die.tra", STORM_CPP_BASE_PATH "/examples/dtmc/die/die.lab", "", STORM_CPP_BASE_PATH "/examples/dtmc/die/die.coin_flips.trans.rew"); - ASSERT_EQ(parser.getType(), storm::models::DTMC); + ASSERT_EQ(abstractModel->getType(), storm::models::DTMC); - std::shared_ptr<storm::models::Dtmc<double>> dtmc = parser.getModel<storm::models::Dtmc<double>>(); + std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>(); ASSERT_EQ(dtmc->getNumberOfStates(), 13ull); - ASSERT_EQ(dtmc->getNumberOfTransitions(), 27ull); + ASSERT_EQ(dtmc->getNumberOfTransitions(), 20ull); storm::modelchecker::prctl::SparseDtmcPrctlModelChecker<double> mc(*dtmc, new storm::solver::GmmxxLinearEquationSolver<double>()); @@ -67,14 +67,14 @@ TEST(GmmxxDtmcPrctlModelCheckerTest, Crowds) { storm::settings::Settings* s = storm::settings::Settings::getInstance(); storm::settings::InternalOptionMemento deadlockOption("fixDeadlocks", true); ASSERT_TRUE(s->isSet("fixDeadlocks")); - storm::parser::AutoParser<double> parser(STORM_CPP_BASE_PATH "/examples/dtmc/crowds/crowds5_5.tra", STORM_CPP_BASE_PATH "/examples/dtmc/crowds/crowds5_5.lab", "", ""); + std::shared_ptr<storm::models::AbstractModel<double>> abstractModel = storm::parser::AutoParser::parseModel(STORM_CPP_BASE_PATH "/examples/dtmc/crowds/crowds5_5.tra", STORM_CPP_BASE_PATH "/examples/dtmc/crowds/crowds5_5.lab", "", ""); - ASSERT_EQ(parser.getType(), storm::models::DTMC); + ASSERT_EQ(abstractModel->getType(), storm::models::DTMC); - std::shared_ptr<storm::models::Dtmc<double>> dtmc = parser.getModel<storm::models::Dtmc<double>>(); + std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>(); ASSERT_EQ(dtmc->getNumberOfStates(), 8607ull); - ASSERT_EQ(dtmc->getNumberOfTransitions(), 22460ull); + ASSERT_EQ(dtmc->getNumberOfTransitions(), 15113ull); storm::modelchecker::prctl::SparseDtmcPrctlModelChecker<double> mc(*dtmc, new storm::solver::GmmxxLinearEquationSolver<double>()); @@ -113,13 +113,13 @@ TEST(GmmxxDtmcPrctlModelCheckerTest, SynchronousLeader) { storm::settings::Settings* s = storm::settings::Settings::getInstance(); storm::settings::InternalOptionMemento deadlockOption("fixDeadlocks", true); ASSERT_TRUE(s->isSet("fixDeadlocks")); - storm::parser::AutoParser<double> parser(STORM_CPP_BASE_PATH "/examples/dtmc/synchronous_leader/leader4_8.tra", STORM_CPP_BASE_PATH "/examples/dtmc/synchronous_leader/leader4_8.lab", "", STORM_CPP_BASE_PATH "/examples/dtmc/synchronous_leader/leader4_8.pick.trans.rew"); + std::shared_ptr<storm::models::AbstractModel<double>> abstractModel = storm::parser::AutoParser::parseModel(STORM_CPP_BASE_PATH "/examples/dtmc/synchronous_leader/leader4_8.tra", STORM_CPP_BASE_PATH "/examples/dtmc/synchronous_leader/leader4_8.lab", "", STORM_CPP_BASE_PATH "/examples/dtmc/synchronous_leader/leader4_8.pick.trans.rew"); - ASSERT_EQ(parser.getType(), storm::models::DTMC); - std::shared_ptr<storm::models::Dtmc<double>> dtmc = parser.getModel<storm::models::Dtmc<double>>(); + ASSERT_EQ(abstractModel->getType(), storm::models::DTMC); + std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>(); ASSERT_EQ(dtmc->getNumberOfStates(), 12400ull); - ASSERT_EQ(dtmc->getNumberOfTransitions(), 28894ull); + ASSERT_EQ(dtmc->getNumberOfTransitions(), 16495ull); storm::modelchecker::prctl::SparseDtmcPrctlModelChecker<double> mc(*dtmc, new storm::solver::GmmxxLinearEquationSolver<double>()); diff --git a/test/functional/modelchecker/SparseMdpPrctlModelCheckerTest.cpp b/test/functional/modelchecker/SparseMdpPrctlModelCheckerTest.cpp index 83b4afdb2..67fe2d63a 100644 --- a/test/functional/modelchecker/SparseMdpPrctlModelCheckerTest.cpp +++ b/test/functional/modelchecker/SparseMdpPrctlModelCheckerTest.cpp @@ -8,11 +8,11 @@ TEST(SparseMdpPrctlModelCheckerTest, Dice) { storm::settings::Settings* s = storm::settings::Settings::getInstance(); - storm::parser::AutoParser<double> parser(STORM_CPP_BASE_PATH "/examples/mdp/two_dice/two_dice.tra", STORM_CPP_BASE_PATH "/examples/mdp/two_dice/two_dice.lab", "", STORM_CPP_BASE_PATH "/examples/mdp/two_dice/two_dice.flip.trans.rew"); + std::shared_ptr<storm::models::AbstractModel<double>> abstractModel = storm::parser::AutoParser::parseModel(STORM_CPP_BASE_PATH "/examples/mdp/two_dice/two_dice.tra", STORM_CPP_BASE_PATH "/examples/mdp/two_dice/two_dice.lab", "", STORM_CPP_BASE_PATH "/examples/mdp/two_dice/two_dice.flip.trans.rew"); - ASSERT_EQ(parser.getType(), storm::models::MDP); + ASSERT_EQ(abstractModel->getType(), storm::models::MDP); - std::shared_ptr<storm::models::Mdp<double>> mdp = parser.getModel<storm::models::Mdp<double>>(); + std::shared_ptr<storm::models::Mdp<double>> mdp = abstractModel->as<storm::models::Mdp<double>>(); ASSERT_EQ(mdp->getNumberOfStates(), 169ull); ASSERT_EQ(mdp->getNumberOfTransitions(), 436ull); @@ -97,11 +97,11 @@ TEST(SparseMdpPrctlModelCheckerTest, Dice) { ASSERT_LT(std::abs(result[0] - 7.333329499), s->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); delete rewardFormula; - storm::parser::AutoParser<double> stateRewardParser(STORM_CPP_BASE_PATH "/examples/mdp/two_dice/two_dice.tra", STORM_CPP_BASE_PATH "/examples/mdp/two_dice/two_dice.lab", STORM_CPP_BASE_PATH "/examples/mdp/two_dice/two_dice.flip.state.rew", ""); + abstractModel = storm::parser::AutoParser::parseModel(STORM_CPP_BASE_PATH "/examples/mdp/two_dice/two_dice.tra", STORM_CPP_BASE_PATH "/examples/mdp/two_dice/two_dice.lab", STORM_CPP_BASE_PATH "/examples/mdp/two_dice/two_dice.flip.state.rew", ""); - ASSERT_EQ(stateRewardParser.getType(), storm::models::MDP); + ASSERT_EQ(abstractModel->getType(), storm::models::MDP); - std::shared_ptr<storm::models::Mdp<double>> stateRewardMdp = stateRewardParser.getModel<storm::models::Mdp<double>>(); + std::shared_ptr<storm::models::Mdp<double>> stateRewardMdp = abstractModel->as<storm::models::Mdp<double>>(); storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double> stateRewardModelChecker(*stateRewardMdp, std::shared_ptr<storm::solver::NativeNondeterministicLinearEquationSolver<double>>(new storm::solver::NativeNondeterministicLinearEquationSolver<double>())); @@ -123,11 +123,11 @@ TEST(SparseMdpPrctlModelCheckerTest, Dice) { ASSERT_LT(std::abs(result[0] - 7.333329499), s->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); delete rewardFormula; - storm::parser::AutoParser<double> stateAndTransitionRewardParser(STORM_CPP_BASE_PATH "/examples/mdp/two_dice/two_dice.tra", STORM_CPP_BASE_PATH "/examples/mdp/two_dice/two_dice.lab", STORM_CPP_BASE_PATH "/examples/mdp/two_dice/two_dice.flip.state.rew", STORM_CPP_BASE_PATH "/examples/mdp/two_dice/two_dice.flip.trans.rew"); + abstractModel = storm::parser::AutoParser::parseModel(STORM_CPP_BASE_PATH "/examples/mdp/two_dice/two_dice.tra", STORM_CPP_BASE_PATH "/examples/mdp/two_dice/two_dice.lab", STORM_CPP_BASE_PATH "/examples/mdp/two_dice/two_dice.flip.state.rew", STORM_CPP_BASE_PATH "/examples/mdp/two_dice/two_dice.flip.trans.rew"); - ASSERT_EQ(stateAndTransitionRewardParser.getType(), storm::models::MDP); + ASSERT_EQ(abstractModel->getType(), storm::models::MDP); - std::shared_ptr<storm::models::Mdp<double>> stateAndTransitionRewardMdp = stateAndTransitionRewardParser.getModel<storm::models::Mdp<double>>(); + std::shared_ptr<storm::models::Mdp<double>> stateAndTransitionRewardMdp = abstractModel->as<storm::models::Mdp<double>>(); storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double> stateAndTransitionRewardModelChecker(*stateAndTransitionRewardMdp, std::shared_ptr<storm::solver::NativeNondeterministicLinearEquationSolver<double>>(new storm::solver::NativeNondeterministicLinearEquationSolver<double>())); @@ -152,11 +152,11 @@ TEST(SparseMdpPrctlModelCheckerTest, Dice) { TEST(SparseMdpPrctlModelCheckerTest, AsynchronousLeader) { storm::settings::Settings* s = storm::settings::Settings::getInstance(); - storm::parser::AutoParser<double> parser(STORM_CPP_BASE_PATH "/examples/mdp/asynchronous_leader/leader4.tra", STORM_CPP_BASE_PATH "/examples/mdp/asynchronous_leader/leader4.lab", "", STORM_CPP_BASE_PATH "/examples/mdp/asynchronous_leader/leader4.trans.rew"); + std::shared_ptr<storm::models::AbstractModel<double>> abstractModel = storm::parser::AutoParser::parseModel(STORM_CPP_BASE_PATH "/examples/mdp/asynchronous_leader/leader4.tra", STORM_CPP_BASE_PATH "/examples/mdp/asynchronous_leader/leader4.lab", "", STORM_CPP_BASE_PATH "/examples/mdp/asynchronous_leader/leader4.trans.rew"); - ASSERT_EQ(parser.getType(), storm::models::MDP); + ASSERT_EQ(abstractModel->getType(), storm::models::MDP); - std::shared_ptr<storm::models::Mdp<double>> mdp = parser.getModel<storm::models::Mdp<double>>(); + std::shared_ptr<storm::models::Mdp<double>> mdp = abstractModel->as<storm::models::Mdp<double>>(); ASSERT_EQ(mdp->getNumberOfStates(), 3172ull); ASSERT_EQ(mdp->getNumberOfTransitions(), 7144ull); diff --git a/test/functional/parser/AtomicPropositionLabelingParserTest.cpp b/test/functional/parser/AtomicPropositionLabelingParserTest.cpp new file mode 100644 index 000000000..538c54936 --- /dev/null +++ b/test/functional/parser/AtomicPropositionLabelingParserTest.cpp @@ -0,0 +1,146 @@ +/* + * AtomicPropositionLabelingParserTest.cpp + * + * Created on: 03.03.2014 + * Author: Manuel Sascha Weiand + */ + +#include "gtest/gtest.h" +#include "storm-config.h" +#include "src/models/AtomicPropositionsLabeling.h" +#include "src/parser/AtomicPropositionLabelingParser.h" +#include "src/exceptions/FileIoException.h" +#include "src/exceptions/WrongFormatException.h" +#include "src/exceptions/OutOfRangeException.h" + +#include <memory> + +TEST(AtomicPropositionLabelingParserTest, NonExistingFile) { + // No matter what happens, please do NOT create a file with the name "nonExistingFile.not"! + ASSERT_THROW(storm::parser::AtomicPropositionLabelingParser::parseAtomicPropositionLabeling(0,STORM_CPP_TESTS_BASE_PATH "/nonExistingFile.not"), storm::exceptions::FileIoException); +} + +TEST(AtomicPropositionLabelingParserTest, BasicParsing) { + // This test is based on a test case from the original MRMC. + + // Parsing the file + storm::models::AtomicPropositionsLabeling labeling = storm::parser::AtomicPropositionLabelingParser::parseAtomicPropositionLabeling(12, STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/pctl_general.lab"); + + // Checking whether all propositions are in the labelling + + char phi[] = "phi", psi[] = "psi", smth[] = "smth"; + + ASSERT_TRUE(labeling.containsAtomicProposition(phi)); + ASSERT_TRUE(labeling.containsAtomicProposition(psi)); + ASSERT_TRUE(labeling.containsAtomicProposition(smth)); + + // Testing whether all and only the correct nodes are labeled with "phi" + ASSERT_TRUE(labeling.getStateHasAtomicProposition(phi,0)); + ASSERT_TRUE(labeling.getStateHasAtomicProposition(phi,1)); + ASSERT_TRUE(labeling.getStateHasAtomicProposition(phi,2)); + + ASSERT_FALSE(labeling.getStateHasAtomicProposition(phi,3)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(phi,4)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(phi,5)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(phi,6)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(phi,7)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(phi,8)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(phi,9)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(phi,10)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(phi,11)); + + //Testing whether all and only the correct nodes are labeled with "psi" + ASSERT_FALSE(labeling.getStateHasAtomicProposition(psi,0)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(psi,1)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(psi,2)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(psi,3)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(psi,4)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(psi,5)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(psi,6)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(psi,7)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(psi,8)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(psi,9)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(psi,10)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(psi,11)); + + //Testing whether all and only the correct nodes are labeled with "smth" + ASSERT_TRUE(labeling.getStateHasAtomicProposition(smth,2)); + + ASSERT_FALSE(labeling.getStateHasAtomicProposition(smth,0)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(smth,1)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(smth,3)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(smth,4)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(smth,5)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(smth,6)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(smth,7)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(smth,8)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(smth,9)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(smth,10)); + ASSERT_FALSE(labeling.getStateHasAtomicProposition(smth,11)); +} + +TEST(AtomicPropositionLabelingParserTest, NoDeclarationTagHeader) { + // No #DECLARATION tag in file + ASSERT_THROW(storm::parser::AtomicPropositionLabelingParser::parseAtomicPropositionLabeling(3, STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/labParser/noDeclarationTag.lab"), storm::exceptions::WrongFormatException); +} + +TEST(AtomicPropositionLabelingParserTest, NoEndTagHeader) { + // No #END tag in file. + ASSERT_THROW(storm::parser::AtomicPropositionLabelingParser::parseAtomicPropositionLabeling(3, STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/labParser/noEndTag.lab"), storm::exceptions::WrongFormatException); +} + +TEST(AtomicPropositionLabelingParserTest, MisspelledDeclarationTagHeader) { + // The #DECLARATION tag is misspelled. + ASSERT_THROW(storm::parser::AtomicPropositionLabelingParser::parseAtomicPropositionLabeling(3, STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/labParser/declarationMisspell.lab"), storm::exceptions::WrongFormatException); +} + +TEST(AtomicPropositionLabelingParserTest, MisspelledEndTagHeader) { + // The #END tag is misspelled. + ASSERT_THROW(storm::parser::AtomicPropositionLabelingParser::parseAtomicPropositionLabeling(3, STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/labParser/endMisspell.lab"), storm::exceptions::WrongFormatException); +} + +TEST(AtomicPropositionLabelingParserTest, NoLabelDeclaredNoneGiven) { + // No label between #DECLARATION and #END and no labels given. + storm::models::AtomicPropositionsLabeling labeling = storm::parser::AtomicPropositionLabelingParser::parseAtomicPropositionLabeling(13, STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/labParser/noLabelsDecNoneGiven.lab"); + ASSERT_EQ(labeling.getNumberOfAtomicPropositions(), 0); + for(uint_fast64_t i = 0; i < 13; i++) { + ASSERT_TRUE(labeling.getPropositionsForState(i).empty()); + } +} + +TEST(AtomicPropositionLabelingParserTest, UndeclaredLabelsGiven) { + // Undeclared labels given. + ASSERT_THROW(storm::parser::AtomicPropositionLabelingParser::parseAtomicPropositionLabeling(3, STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/labParser/undeclaredLabelsGiven.lab"), storm::exceptions::WrongFormatException); +} + +TEST(AtomicPropositionLabelingParserTest, LabelForNonExistentState) { + // The index of one of the state that are to be labeled is higher than the number of states in the model. + ASSERT_THROW(storm::parser::AtomicPropositionLabelingParser::parseAtomicPropositionLabeling(3, STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/labParser/labelForNonexistentState.lab"), storm::exceptions::OutOfRangeException); +} + +// Note: As implemented at the moment, each label given for a state in any line is set to true for that state (logical or over all lines for that state). +// This behavior might not be ideal as multiple lines for one state are not necessary and might indicate a corrupt or wrong file. +TEST(AtomicPropositionLabelingParserTest, DoubledLines) { + // There are multiple lines attributing labels to the same state. + ASSERT_THROW(storm::parser::AtomicPropositionLabelingParser::parseAtomicPropositionLabeling(6, STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/labParser/doubledLines.lab"), storm::exceptions::WrongFormatException); + + // There is a line for a state that has been skipped. + ASSERT_THROW(storm::parser::AtomicPropositionLabelingParser::parseAtomicPropositionLabeling(6, STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/labParser/doubledLinesSkipped.lab"), storm::exceptions::WrongFormatException); +} + +TEST(AtomicPropositionLabelingParserTest, WrongProposition) { + // Swapped the state index and the label at one entry. + ASSERT_THROW(storm::parser::AtomicPropositionLabelingParser::parseAtomicPropositionLabeling(3, STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/labParser/swappedStateAndProposition.lab"), storm::exceptions::WrongFormatException); +} + +TEST(AtomicPropositionLabelingParserTest, Whitespaces) { + // Different configurations of allowed whitespaces are tested. + + // First parse the labeling file without added whitespaces and obtain the hash of its parsed representation. + storm::models::AtomicPropositionsLabeling labeling = storm::parser::AtomicPropositionLabelingParser::parseAtomicPropositionLabeling(13, STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/labParser/withoutWhitespaces.lab"); + uint_fast64_t correctHash = labeling.getHash(); + + // Now parse the labeling file with the added whitespaces and compare the hashes. + labeling = storm::parser::AtomicPropositionLabelingParser::parseAtomicPropositionLabeling(13, STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/labParser/withWhitespaces.lab"); + ASSERT_EQ(correctHash, labeling.getHash()); +} diff --git a/test/functional/parser/AutoParserTest.cpp b/test/functional/parser/AutoParserTest.cpp new file mode 100644 index 000000000..7c3fcb483 --- /dev/null +++ b/test/functional/parser/AutoParserTest.cpp @@ -0,0 +1,91 @@ +/* + * AutoParserTest.cpp + * + * Created on: Feb 10, 2014 + * Author: Manuel Sascha Weiand + */ + +#include "gtest/gtest.h" +#include "storm-config.h" + +#include "src/parser/AutoParser.h" +#include "src/exceptions/FileIoException.h" +#include "src/exceptions/WrongFormatException.h" + +TEST(AutoParserTest, NonExistingFile) { + // No matter what happens, please do NOT create a file with the name "nonExistingFile.not"! + ASSERT_THROW(storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/nonExistingFile.not", STORM_CPP_TESTS_BASE_PATH "/nonExistingFile.not"), storm::exceptions::FileIoException); +} + +TEST(AutoParserTest, BasicParsing) { + // Parse model, which is a Dtmc. + std::shared_ptr<storm::models::AbstractModel<double>> modelPtr = storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/autoParser/dtmc.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/autoParser.lab"); + + // Test if parsed correctly. + ASSERT_EQ(storm::models::DTMC, modelPtr->getType()); + ASSERT_EQ(12, modelPtr->getNumberOfStates()); + ASSERT_EQ(26, modelPtr->getNumberOfTransitions()); + ASSERT_EQ(1, modelPtr->getInitialStates().getNumberOfSetBits()); + ASSERT_TRUE(modelPtr->hasAtomicProposition("three")); + ASSERT_FALSE(modelPtr->hasStateRewards()); + ASSERT_FALSE(modelPtr->hasTransitionRewards()); +} + +TEST(AutoParserTest, Whitespaces) { + // Test different whitespace combinations by comparing the hash of the model parsed from files without whitespaces with the hash of the models parsed from files with whitespaces. + uint_fast64_t correctHash = storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/autoParser/dtmc.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/autoParser.lab")->getHash(); + + ASSERT_EQ(correctHash, storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/autoParser/dtmcWhitespaces1.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/autoParser.lab")->getHash()); + ASSERT_EQ(correctHash, storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/autoParser/dtmcWhitespaces2.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/autoParser.lab")->getHash()); +} + +TEST(AutoParserTest, WrongHint) { + // The hint given describes the content but does not conform to the format. + ASSERT_THROW(storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/autoParser/wrongHint.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/autoParser.lab"), storm::exceptions::WrongFormatException); +} + +TEST(AutoParserTest, NoHint) { + // There is no hint contained in the given file, so the parser cannot decide which kind of model it is. + ASSERT_THROW(storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/autoParser/noHint.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/autoParser.lab"), storm::exceptions::WrongFormatException); +} + +TEST(AutoParserTest, Decision) { + // Test if the AutoParser recognizes each model kind and correctly parses it. + + // Dtmc + std::shared_ptr<storm::models::AbstractModel<double>> modelPtr = storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/autoParser/dtmc.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/autoParser.lab"); + ASSERT_EQ(storm::models::DTMC, modelPtr->getType()); + ASSERT_EQ(12, modelPtr->getNumberOfStates()); + ASSERT_EQ(26, modelPtr->getNumberOfTransitions()); + + // Ctmc + modelPtr.reset(); + modelPtr = storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/autoParser/ctmc.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/autoParser.lab"); + ASSERT_EQ(storm::models::CTMC, modelPtr->getType()); + ASSERT_EQ(12, modelPtr->getNumberOfStates()); + ASSERT_EQ(26, modelPtr->getNumberOfTransitions()); + + // Mdp + modelPtr.reset(); + modelPtr = storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/autoParser/mdp.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/autoParser.lab"); + ASSERT_EQ(storm::models::MDP, modelPtr->getType()); + ASSERT_EQ(12, modelPtr->getNumberOfStates()); + ASSERT_EQ(28, modelPtr->getNumberOfTransitions()); + + // Ctmdp + // Note: For now we use the Mdp from above just given the ctmdp hint, since the implementation of the Ctmdp model seems not Quite right yet. + // We still do this test so that the code responsible for Ctmdps is executed at least once during testing. + // TODO: Fix the Ctmdp implementation and use an actual Ctmdp for testing. + modelPtr.reset(); + modelPtr = storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/autoParser/ctmdp.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/autoParser.lab"); + ASSERT_EQ(storm::models::CTMDP, modelPtr->getType()); + ASSERT_EQ(12, modelPtr->getNumberOfStates()); + ASSERT_EQ(28, modelPtr->getNumberOfTransitions()); + + // MA + modelPtr.reset(); + modelPtr = storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/autoParser/ma.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/autoParser.lab"); + ASSERT_EQ(storm::models::MA, modelPtr->getType()); + ASSERT_EQ(12, modelPtr->getNumberOfStates()); + ASSERT_EQ(27, modelPtr->getNumberOfTransitions()); +} diff --git a/test/functional/parser/CslParserTest.cpp b/test/functional/parser/CslParserTest.cpp index fb758e951..4e4a67af9 100644 --- a/test/functional/parser/CslParserTest.cpp +++ b/test/functional/parser/CslParserTest.cpp @@ -8,6 +8,7 @@ #include "gtest/gtest.h" #include "storm-config.h" #include "src/parser/CslParser.h" +#include "src/exceptions/WrongFormatException.h" TEST(CslParserTest, parseApOnlyTest) { std::string formula = "ap"; diff --git a/test/functional/parser/DeterministicModelParserTest.cpp b/test/functional/parser/DeterministicModelParserTest.cpp new file mode 100644 index 000000000..1b6fe71db --- /dev/null +++ b/test/functional/parser/DeterministicModelParserTest.cpp @@ -0,0 +1,101 @@ +/* + * DeterministicModelParserTest.cpp + * + * Created on: Feb 24, 2014 + * Author: Manuel Sascha Weiand + */ + +#include "gtest/gtest.h" +#include "storm-config.h" + +#include "src/parser/DeterministicModelParser.h" +#include "src/models/Dtmc.h" +#include "src/models/Ctmc.h" +#include "src/exceptions/FileIoException.h" + +TEST(DeterministicModelParserTest, NonExistingFile) { + // No matter what happens, please do NOT create a file with the name "nonExistingFile.not"! + ASSERT_THROW(storm::parser::DeterministicModelParser::parseDtmc(STORM_CPP_TESTS_BASE_PATH "/nonExistingFile.not", STORM_CPP_TESTS_BASE_PATH "/nonExistingFile.not"), storm::exceptions::FileIoException); + + ASSERT_THROW(storm::parser::DeterministicModelParser::parseCtmc(STORM_CPP_TESTS_BASE_PATH "/nonExistingFile.not", STORM_CPP_TESTS_BASE_PATH "/nonExistingFile.not"), storm::exceptions::FileIoException); +} + +TEST(DeterministicModelParserTest, BasicDtmcParsing) { + + // Parse a Dtmc and check the result. + storm::models::Dtmc<double> dtmc(storm::parser::DeterministicModelParser::parseDtmc(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/dtmc_general.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/dtmc_general.lab", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/dtmc_general.state.rew", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/dtmc_general.trans.rew")); + + ASSERT_EQ(8, dtmc.getNumberOfStates()); + ASSERT_EQ(16, dtmc.getNumberOfTransitions()); + + ASSERT_EQ(2, dtmc.getInitialStates().getNumberOfSetBits()); + ASSERT_TRUE(dtmc.getInitialStates().get(0)); + ASSERT_TRUE(dtmc.getInitialStates().get(7)); + ASSERT_EQ(5, dtmc.getStateLabeling().getNumberOfAtomicPropositions()); + ASSERT_EQ(2, dtmc.getLabelsForState(6).size()); + + ASSERT_TRUE(dtmc.hasStateRewards()); + ASSERT_EQ(42, dtmc.getStateRewardVector()[7]); + double rewardSum = 0; + for(uint_fast64_t i = 0; i < dtmc.getStateRewardVector().size(); i++) { + rewardSum += dtmc.getStateRewardVector()[i]; + } + ASSERT_EQ(263.32, rewardSum); + + ASSERT_TRUE(dtmc.hasTransitionRewards()); + ASSERT_EQ(17, dtmc.getTransitionRewardMatrix().getEntryCount()); + rewardSum = 0; + for(uint_fast64_t i = 0; i < dtmc.getTransitionRewardMatrix().getRowCount(); i++) { + rewardSum += dtmc.getTransitionRewardMatrix().getRowSum(i); + } + ASSERT_EQ(125.4, rewardSum); +} + + +TEST(DeterministicModelParserTest, BasicCtmcParsing) { + + // Parse a Ctmc and check the result. + storm::models::Ctmc<double> ctmc(storm::parser::DeterministicModelParser::parseCtmc(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/dtmc_general.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/dtmc_general.lab", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/dtmc_general.state.rew", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/dtmc_general.trans.rew")); + + ASSERT_EQ(8, ctmc.getNumberOfStates()); + ASSERT_EQ(16, ctmc.getNumberOfTransitions()); + + ASSERT_EQ(2, ctmc.getInitialStates().getNumberOfSetBits()); + ASSERT_TRUE(ctmc.getInitialStates().get(0)); + ASSERT_TRUE(ctmc.getInitialStates().get(7)); + ASSERT_EQ(5, ctmc.getStateLabeling().getNumberOfAtomicPropositions()); + ASSERT_EQ(2, ctmc.getLabelsForState(6).size()); + + ASSERT_TRUE(ctmc.hasStateRewards()); + ASSERT_EQ(42, ctmc.getStateRewardVector()[7]); + double rewardSum = 0; + for(uint_fast64_t i = 0; i < ctmc.getStateRewardVector().size(); i++) { + rewardSum += ctmc.getStateRewardVector()[i]; + } + ASSERT_EQ(263.32, rewardSum); + + ASSERT_TRUE(ctmc.hasTransitionRewards()); + ASSERT_EQ(17, ctmc.getTransitionRewardMatrix().getEntryCount()); + rewardSum = 0; + for(uint_fast64_t i = 0; i < ctmc.getTransitionRewardMatrix().getRowCount(); i++) { + rewardSum += ctmc.getTransitionRewardMatrix().getRowSum(i); + } + ASSERT_EQ(125.4, rewardSum); +} + +TEST(DeterministicModelParserTest, MismatchedFiles) { + + // Test file combinations that do not match, i.e. differing number of states, transitions, etc. + + // The labeling file contains a label for a non existent state. + ASSERT_THROW(storm::parser::DeterministicModelParser::parseDtmc(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/dtmc_mismatched.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/dtmc_general.lab"), storm::exceptions::OutOfRangeException); + + // The state reward file contains a reward for a non existent state. + ASSERT_THROW(storm::parser::DeterministicModelParser::parseDtmc(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/dtmc_mismatched.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/dtmc_mismatched.lab", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/dtmc_general.state.rew"), storm::exceptions::OutOfRangeException); + + // The transition reward file contains rewards for a non existent state. + ASSERT_THROW(storm::parser::DeterministicModelParser::parseDtmc(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/dtmc_mismatched.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/dtmc_mismatched.lab", "", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/dtmc_general.trans.rew"), storm::exceptions::OutOfRangeException); + + // The transition reward file contains rewards for a non existent transition + ASSERT_THROW(storm::parser::DeterministicModelParser::parseDtmc(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/dtmc_mismatched.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/dtmc_mismatched.lab", "", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/dtmc_mismatched.trans.rew"), storm::exceptions::OutOfRangeException); +} diff --git a/test/functional/parser/DeterministicSparseTransitionParserTest.cpp b/test/functional/parser/DeterministicSparseTransitionParserTest.cpp new file mode 100644 index 000000000..89c5c9c6e --- /dev/null +++ b/test/functional/parser/DeterministicSparseTransitionParserTest.cpp @@ -0,0 +1,239 @@ +/* + * DeterministicSparseTransitionParserTest.cpp + * + * Created on: Feb 24, 2014 + * Author: Manuel Sascha Weiand + */ + +#include "gtest/gtest.h" +#include "storm-config.h" + +#include "src/parser/DeterministicSparseTransitionParser.h" +#include "src/storage/SparseMatrix.h" +#include "src/settings/InternalOptionMemento.h" +#include "src/exceptions/FileIoException.h" +#include "src/exceptions/WrongFormatException.h" + +TEST(DeterministicSparseTransitionParserTest, NonExistingFile) { + + // No matter what happens, please do NOT create a file with the name "nonExistingFile.not"! + ASSERT_THROW(storm::parser::DeterministicSparseTransitionParser::parseDeterministicTransitions(STORM_CPP_TESTS_BASE_PATH "/nonExistingFile.not"), storm::exceptions::FileIoException); + + storm::storage::SparseMatrix<double> nullMatrix; + ASSERT_THROW(storm::parser::DeterministicSparseTransitionParser::parseDeterministicTransitionRewards(STORM_CPP_TESTS_BASE_PATH "/nonExistingFile.not", nullMatrix), storm::exceptions::FileIoException); +} + + +TEST(DeterministicSparseTransitionParserTest, BasicTransitionsParsing) { + + // Parse a deterministic transitions file and test the resulting matrix. + storm::storage::SparseMatrix<double> transitionMatrix = storm::parser::DeterministicSparseTransitionParser::parseDeterministicTransitions(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/dtmc_general.tra"); + + ASSERT_EQ(8, transitionMatrix.getColumnCount()); + ASSERT_EQ(21, transitionMatrix.getEntryCount()); + + // Test every entry of the matrix. + storm::storage::SparseMatrix<double>::const_iterator cIter = transitionMatrix.begin(0); + + ASSERT_EQ(0, cIter->getColumn()); + ASSERT_EQ(0, cIter->getValue()); + cIter++; + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); + cIter++; + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0, cIter->getValue()); + cIter++; + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(0.5, cIter->getValue()); + cIter++; + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(0.5, cIter->getValue()); + cIter++; + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(0, cIter->getValue()); + cIter++; + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(0.4, cIter->getValue()); + cIter++; + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.4, cIter->getValue()); + cIter++; + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); + cIter++; + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); + cIter++; + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); + cIter++; + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0, cIter->getValue()); + cIter++; + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(0.1, cIter->getValue()); + cIter++; + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.1, cIter->getValue()); + cIter++; + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(0.1, cIter->getValue()); + cIter++; + ASSERT_EQ(6, cIter->getColumn()); + ASSERT_EQ(0.7, cIter->getValue()); + cIter++; + ASSERT_EQ(0, cIter->getColumn()); + ASSERT_EQ(0.9, cIter->getValue()); + cIter++; + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(0, cIter->getValue()); + cIter++; + ASSERT_EQ(6, cIter->getColumn()); + ASSERT_EQ(0.1, cIter->getValue()); + cIter++; + ASSERT_EQ(6, cIter->getColumn()); + ASSERT_EQ(0.224653, cIter->getValue()); + cIter++; + ASSERT_EQ(7, cIter->getColumn()); + ASSERT_EQ(0.775347, cIter->getValue()); +} + +TEST(DeterministicSparseTransitionParserTest, BasicTransitionsRewardsParsing) { + + // First parse a transition file. Then parse a transition reward file for the resulting transition matrix. + storm::storage::SparseMatrix<double> transitionMatrix = storm::parser::DeterministicSparseTransitionParser::parseDeterministicTransitions(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/dtmc_general.tra"); + + storm::storage::SparseMatrix<double> rewardMatrix = storm::parser::DeterministicSparseTransitionParser::parseDeterministicTransitionRewards(STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/dtmc_general.trans.rew", transitionMatrix); + + ASSERT_EQ(8, rewardMatrix.getColumnCount()); + ASSERT_EQ(17, rewardMatrix.getEntryCount()); + + // Test every entry of the matrix. + storm::storage::SparseMatrix<double>::const_iterator cIter = rewardMatrix.begin(0); + + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(10, cIter->getValue()); + cIter++; + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(5, cIter->getValue()); + cIter++; + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(5.5, cIter->getValue()); + cIter++; + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(21.4, cIter->getValue()); + cIter++; + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(4, cIter->getValue()); + cIter++; + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(2, cIter->getValue()); + cIter++; + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); + cIter++; + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); + cIter++; + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(0.1, cIter->getValue()); + cIter++; + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(1.1, cIter->getValue()); + cIter++; + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(9.5, cIter->getValue()); + cIter++; + ASSERT_EQ(6, cIter->getColumn()); + ASSERT_EQ(6.7, cIter->getValue()); + cIter++; + ASSERT_EQ(0, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); + cIter++; + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(0, cIter->getValue()); + cIter++; + ASSERT_EQ(6, cIter->getColumn()); + ASSERT_EQ(12, cIter->getValue()); + cIter++; + ASSERT_EQ(6, cIter->getColumn()); + ASSERT_EQ(35.224653, cIter->getValue()); + cIter++; + ASSERT_EQ(7, cIter->getColumn()); + ASSERT_EQ(9.875347, cIter->getValue()); +} + + +TEST(DeterministicSparseTransitionParserTest, Whitespaces) { + + // Test the resilience of the parser against whitespaces. + // Do so by comparing the hash of the matrix resulting from the file without whitespaces with the hash of the matrix resulting from the file with whitespaces. + uint_fast64_t correctHash = storm::parser::DeterministicSparseTransitionParser::parseDeterministicTransitions(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/dtmc_general.tra").hash(); + storm::storage::SparseMatrix<double> transitionMatrix = storm::parser::DeterministicSparseTransitionParser::parseDeterministicTransitions(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/dtmc_whitespaces.tra"); + ASSERT_EQ(correctHash, transitionMatrix.hash()); + + // Do the same for the corresponding transition rewards file (with and without whitespaces) + correctHash = storm::parser::DeterministicSparseTransitionParser::parseDeterministicTransitionRewards(STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/dtmc_general.trans.rew", transitionMatrix).hash(); + ASSERT_EQ(correctHash, storm::parser::DeterministicSparseTransitionParser::parseDeterministicTransitionRewards(STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/dtmc_whitespaces.trans.rew", transitionMatrix).hash()); +} + +TEST(DeterministicSparseTransitionParserTest, MixedTransitionOrder) { + + // Since the MatrixBuilder needs sequential input of new elements reordering of transitions or states should throw an exception. + ASSERT_THROW(storm::parser::DeterministicSparseTransitionParser::parseDeterministicTransitions(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/dtmc_mixedTransitionOrder.tra"), storm::exceptions::InvalidArgumentException); + ASSERT_THROW(storm::parser::DeterministicSparseTransitionParser::parseDeterministicTransitions(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/dtmc_mixedStateOrder.tra"), storm::exceptions::InvalidArgumentException); + + storm::storage::SparseMatrix<double> transitionMatrix = storm::parser::DeterministicSparseTransitionParser::parseDeterministicTransitions(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/dtmc_general.tra"); + ASSERT_THROW(storm::parser::DeterministicSparseTransitionParser::parseDeterministicTransitionRewards(STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/dtmc_mixedTransitionOrder.trans.rew", transitionMatrix), storm::exceptions::InvalidArgumentException); + ASSERT_THROW(storm::parser::DeterministicSparseTransitionParser::parseDeterministicTransitionRewards(STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/dtmc_mixedStateOrder.trans.rew", transitionMatrix), storm::exceptions::InvalidArgumentException); +} + +TEST(DeterministicSparseTransitionParserTest, FixDeadlocks) { + + // Set the fixDeadlocks flag temporarily. It is set to its old value once the deadlockOption object is destructed. + storm::settings::InternalOptionMemento setDeadlockOption("fixDeadlocks", true); + + // Parse a transitions file with the fixDeadlocks Flag set and test if it works. + storm::storage::SparseMatrix<double> transitionMatrix = storm::parser::DeterministicSparseTransitionParser::parseDeterministicTransitions(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/dtmc_deadlock.tra"); + + ASSERT_EQ(9, transitionMatrix.getColumnCount()); + ASSERT_EQ(23, transitionMatrix.getEntryCount()); + + storm::storage::SparseMatrix<double>::const_iterator cIter = transitionMatrix.begin(7); + ASSERT_EQ(7, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); + cIter++; + ASSERT_EQ(6, cIter->getColumn()); + ASSERT_EQ(0.224653, cIter->getValue()); + cIter++; + ASSERT_EQ(7, cIter->getColumn()); + ASSERT_EQ(0.775347, cIter->getValue()); + cIter++; + ASSERT_EQ(8, cIter->getColumn()); + ASSERT_EQ(0, cIter->getValue()); +} + +TEST(DeterministicSparseTransitionParserTest, DontFixDeadlocks) { + + // Try to parse a transitions file containing a deadlock state with the fixDeadlocksFlag unset. This should throw an exception. + storm::settings::InternalOptionMemento unsetDeadlockOption("fixDeadlocks", false); + + ASSERT_THROW(storm::parser::DeterministicSparseTransitionParser::parseDeterministicTransitions(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/dtmc_deadlock.tra"), storm::exceptions::WrongFormatException); +} + +TEST(DeterministicSparseTransitionParserTest, DoubledLines) { + + // There is a redundant line in the transition file. As the transition already exists this should throw an exception. + // Note: If two consecutive lines are doubled no exception is thrown. + ASSERT_THROW(storm::parser::DeterministicSparseTransitionParser::parseDeterministicTransitions(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/dtmc_doubledLines.tra"), storm::exceptions::InvalidArgumentException); +} + +TEST(DeterministicSparseTransitionParserTest, RewardForNonExistentTransition) { + + // First parse a transition file. Then parse a transition reward file for the resulting transition matrix. + storm::storage::SparseMatrix<double> transitionMatrix = storm::parser::DeterministicSparseTransitionParser::parseDeterministicTransitions(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/dtmc_general.tra"); + + // There is a reward for a transition that does not exist in the transition matrix. + ASSERT_THROW(storm::parser::DeterministicSparseTransitionParser::parseDeterministicTransitionRewards(STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/dtmc_rewardForNonExTrans.trans.rew", transitionMatrix), storm::exceptions::WrongFormatException); +} diff --git a/test/functional/parser/LtlParserTest.cpp b/test/functional/parser/LtlParserTest.cpp index 00df82ff9..1da53769d 100644 --- a/test/functional/parser/LtlParserTest.cpp +++ b/test/functional/parser/LtlParserTest.cpp @@ -8,6 +8,7 @@ #include "gtest/gtest.h" #include "storm-config.h" #include "src/parser/LtlParser.h" +#include "src/exceptions/WrongFormatException.h" TEST(LtlParserTest, parseApOnlyTest) { std::string formula = "ap"; diff --git a/test/functional/parser/MappedFileTest.cpp b/test/functional/parser/MappedFileTest.cpp new file mode 100644 index 000000000..8b4d7bfba --- /dev/null +++ b/test/functional/parser/MappedFileTest.cpp @@ -0,0 +1,52 @@ +/* + * MappedFileTest.cpp + * + * Created on: Feb 25, 2014 + * Author: Manuel Sascha Weiand + */ + +#include "gtest/gtest.h" +#include "storm-config.h" + +#include <string> +#include "src/parser/MappedFile.h" +#include "src/utility/cstring.h" +#include "src/exceptions/FileIoException.h" + +TEST(MappedFileTest, NonExistingFile) { + // No matter what happens, please do NOT create a file with the name "nonExistingFile.not"! + ASSERT_THROW(storm::parser::MappedFile(STORM_CPP_TESTS_BASE_PATH "/nonExistingFile.not"), storm::exceptions::FileIoException); +} + +TEST(MappedFileTest, BasicFunctionality) { + + // Open a file and test if the content is loaded as expected. + storm::parser::MappedFile file(STORM_CPP_TESTS_BASE_PATH "/functional/parser/testStringFile.txt"); + std::string testString = "This is a test string."; + char * dataPtr = file.getData(); + for(char const * testStringPtr = testString.c_str(); testStringPtr - testString.c_str() < 22; testStringPtr++) { + ASSERT_EQ(*testStringPtr, *dataPtr); + dataPtr++; + } + // The next character should be an end of line character (actual character varies between operating systems). + ASSERT_EQ(dataPtr, storm::utility::cstring::forwardToLineEnd(dataPtr)); + + // The newline character should be the last contained in the string. + ASSERT_EQ(file.getDataEnd(), storm::utility::cstring::forwardToNextLine(dataPtr)); + +} + +TEST(MappedFileTest, ExistsAndReadble) { + + // Test the fileExistsAndIsReadable() method under various circumstances. + + // File exists and is readable. + ASSERT_TRUE(storm::parser::MappedFile::fileExistsAndIsReadable(STORM_CPP_TESTS_BASE_PATH "/functional/parser/testStringFile.txt")); + + // File does not exist. + ASSERT_FALSE(storm::parser::MappedFile::fileExistsAndIsReadable(STORM_CPP_TESTS_BASE_PATH "/nonExistingFile.not")); + + // File exists but is not readable. + // TODO: Find portable solution to providing a situation in which a file exists but is not readable. + //ASSERT_FALSE(storm::parser::MappedFile::fileExistsAndIsReadable(STORM_CPP_TESTS_BASE_PATH "/functional/parser/unreadableFile.txt")); +} diff --git a/test/functional/parser/MarkovAutomatonParserTest.cpp b/test/functional/parser/MarkovAutomatonParserTest.cpp new file mode 100644 index 000000000..4668105b9 --- /dev/null +++ b/test/functional/parser/MarkovAutomatonParserTest.cpp @@ -0,0 +1,71 @@ +/* + * MarkovAutomatonParserTest.cpp + * + * Created on: 25.02.2014 + * Author: Manuel Sascha Weiand + */ + +#include "gtest/gtest.h" +#include "storm-config.h" + +#include "src/parser/MarkovAutomatonParser.h" +#include "src/exceptions/FileIoException.h" + +TEST(MarkovAutomatonParserTest, NonExistingFile) { + + // No matter what happens, please do NOT create a file with the name "nonExistingFile.not"! + ASSERT_THROW(storm::parser::MarkovAutomatonParser::parseMarkovAutomaton(STORM_CPP_TESTS_BASE_PATH "/nonExistingFile.not", STORM_CPP_TESTS_BASE_PATH "/nonExistingFile.not", STORM_CPP_TESTS_BASE_PATH "/nonExistingFile.not"), storm::exceptions::FileIoException); +} + +TEST(MarkovAutomatonParserTest, BasicParsing) { + + // Get the parsing result. + storm::models::MarkovAutomaton<double> result = storm::parser::MarkovAutomatonParser::parseMarkovAutomaton(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/ma_general.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/ma_general.lab", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/ma_general.state.rew"); + + // Test sizes and counts. + ASSERT_EQ(6, result.getNumberOfStates()); + ASSERT_EQ(7, result.getNumberOfChoices()); + ASSERT_EQ(12, result.getNumberOfTransitions()); + + // Test the exit rates. These have to be 0 for all non-Markovian states. + std::vector<double> rates = result.getExitRates(); + ASSERT_EQ(2, result.getExitRate(0)); + ASSERT_FALSE(result.isMarkovianState(1)); + ASSERT_EQ(0, result.getExitRate(1)); + ASSERT_EQ(15, result.getExitRate(2)); + ASSERT_FALSE(result.isMarkovianState(3)); + ASSERT_EQ(0, result.getExitRate(3)); + ASSERT_FALSE(result.isMarkovianState(4)); + ASSERT_EQ(0, result.getExitRate(4)); + ASSERT_FALSE(result.isMarkovianState(5)); + ASSERT_EQ(0, result.getExitRate(5)); + + // Test the labeling. + ASSERT_EQ(3, result.getStateLabeling().getNumberOfAtomicPropositions()); + ASSERT_EQ(1, result.getInitialStates().getNumberOfSetBits()); + ASSERT_EQ(0, result.getLabelsForState(4).size()); + ASSERT_EQ(1, result.getStateLabeling().getLabeledStates("goal").getNumberOfSetBits()); + + // Test the state rewards. + ASSERT_TRUE(result.hasStateRewards()); + double rewardSum = 0; + for(uint_fast64_t i = 0; i < result.getStateRewardVector().size(); i++) { + rewardSum += result.getStateRewardVector()[i]; + } + ASSERT_EQ(1015.765099984, rewardSum); + ASSERT_EQ(0, result.getStateRewardVector()[0]); + + // Test the transition rewards. + ASSERT_FALSE(result.hasTransitionRewards()); +} + +TEST(MarkovAutomatonParserTest, MismatchedFiles) { + + // Test file combinations that do not match, i.e. differing number of states, transitions, etc. + + // The labeling file contains a label for a non existent state. + ASSERT_THROW(storm::parser::MarkovAutomatonParser::parseMarkovAutomaton(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/ma_general.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/ma_mismatched.lab"), storm::exceptions::OutOfRangeException); + + // The state reward file contains a reward for a non existent state. + ASSERT_THROW(storm::parser::MarkovAutomatonParser::parseMarkovAutomaton(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/ma_general.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/ma_general.lab", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/ma_mismatched.state.rew"), storm::exceptions::OutOfRangeException); +} diff --git a/test/functional/parser/MarkovAutomatonSparseTransitionParserTest.cpp b/test/functional/parser/MarkovAutomatonSparseTransitionParserTest.cpp new file mode 100644 index 000000000..38b152cd4 --- /dev/null +++ b/test/functional/parser/MarkovAutomatonSparseTransitionParserTest.cpp @@ -0,0 +1,211 @@ +/* + * MarkovAutomatonParserTest.cpp + * + * Created on: 03.12.2013 + * Author: Manuel Sascha Weiand + */ + +#include "gtest/gtest.h" +#include "storm-config.h" +#include "src/settings/Settings.h" + +#include <vector> + +#include "src/parser/MarkovAutomatonSparseTransitionParser.h" +#include "src/utility/cstring.h" +#include "src/parser/MarkovAutomatonParser.h" +#include "src/settings/InternalOptionMemento.h" +#include "src/exceptions/WrongFormatException.h" +#include "src/exceptions/FileIoException.h" + +#define STATE_COUNT 6 +#define CHOICE_COUNT 7 + +TEST(MarkovAutomatonSparseTransitionParserTest, NonExistingFile) { + + // No matter what happens, please do NOT create a file with the name "nonExistingFile.not"! + ASSERT_THROW(storm::parser::MarkovAutomatonSparseTransitionParser::parseMarkovAutomatonTransitions(STORM_CPP_TESTS_BASE_PATH "/nonExistingFile.not"), storm::exceptions::FileIoException); +} + +TEST(MarkovAutomatonSparseTransitionParserTest, BasicParsing) { + + // The file that will be used for the test. + std::string filename = STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/ma_general.tra"; + + // Execute the parser. + storm::parser::MarkovAutomatonSparseTransitionParser::Result result = storm::parser::MarkovAutomatonSparseTransitionParser::parseMarkovAutomatonTransitions(filename); + + // Build the actual transition matrix. + storm::storage::SparseMatrix<double> transitionMatrix(result.transitionMatrixBuilder.build(0,0)); + + // Test all sizes and counts. + ASSERT_EQ(STATE_COUNT, transitionMatrix.getColumnCount()); + ASSERT_EQ(CHOICE_COUNT, transitionMatrix.getRowCount()); + ASSERT_EQ(12, transitionMatrix.getEntryCount()); + ASSERT_EQ(6, transitionMatrix.getRowGroupCount()); + ASSERT_EQ(7, transitionMatrix.getRowGroupIndices().size()); + ASSERT_EQ(CHOICE_COUNT, result.markovianChoices.size()); + ASSERT_EQ(STATE_COUNT, result.markovianStates.size()); + ASSERT_EQ(2, result.markovianStates.getNumberOfSetBits()); + ASSERT_EQ(STATE_COUNT, result.exitRates.size()); + + // Test the general structure of the transition system (that will be an Markov automaton). + + // Test the mapping between states and transition matrix rows. + ASSERT_EQ(0, transitionMatrix.getRowGroupIndices()[0]); + ASSERT_EQ(1, transitionMatrix.getRowGroupIndices()[1]); + ASSERT_EQ(2, transitionMatrix.getRowGroupIndices()[2]); + ASSERT_EQ(3, transitionMatrix.getRowGroupIndices()[3]); + ASSERT_EQ(4, transitionMatrix.getRowGroupIndices()[4]); + ASSERT_EQ(6, transitionMatrix.getRowGroupIndices()[5]); + ASSERT_EQ(7, transitionMatrix.getRowGroupIndices()[6]); + + // Test the Markovian states. + ASSERT_TRUE(result.markovianStates.get(0)); + ASSERT_FALSE(result.markovianStates.get(1)); + ASSERT_TRUE(result.markovianStates.get(2)); + ASSERT_FALSE(result.markovianStates.get(3)); + ASSERT_FALSE(result.markovianStates.get(4)); + ASSERT_FALSE(result.markovianStates.get(5)); + + // Test the exit rates. These have to be 0 for all non-Markovian states. + ASSERT_EQ(2, result.exitRates[0]); + ASSERT_EQ(0, result.exitRates[1]); + ASSERT_EQ(15, result.exitRates[2]); + ASSERT_EQ(0, result.exitRates[3]); + ASSERT_EQ(0, result.exitRates[4]); + ASSERT_EQ(0, result.exitRates[5]); + + // Finally, test the transition matrix itself. + storm::storage::SparseMatrix<double>::const_iterator cIter = transitionMatrix.begin(0); + + ASSERT_EQ(2, cIter->getValue()); + cIter++; + ASSERT_EQ(1, cIter->getValue()); + cIter++; + ASSERT_EQ(1, cIter->getValue()); + cIter++; + ASSERT_EQ(2, cIter->getValue()); + cIter++; + ASSERT_EQ(4, cIter->getValue()); + cIter++; + ASSERT_EQ(8, cIter->getValue()); + cIter++; + ASSERT_EQ(0.5, cIter->getValue()); + cIter++; + ASSERT_EQ(0.5, cIter->getValue()); + cIter++; + ASSERT_EQ(1, cIter->getValue()); + cIter++; + ASSERT_EQ(0.5, cIter->getValue()); + cIter++; + ASSERT_EQ(0.5, cIter->getValue()); + cIter++; + ASSERT_EQ(1, cIter->getValue()); + cIter++; + ASSERT_EQ(transitionMatrix.end(), cIter); +} + +TEST(MarkovAutomatonSparseTransitionParserTest, Whitespaces) { + // The file that will be used for the test. + std::string filename = STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/ma_whitespaces.tra"; + + // Execute the parser. + storm::parser::MarkovAutomatonSparseTransitionParser::Result result = storm::parser::MarkovAutomatonSparseTransitionParser::parseMarkovAutomatonTransitions(filename); + + // Build the actual transition matrix. + storm::storage::SparseMatrix<double> transitionMatrix(result.transitionMatrixBuilder.build()); + + // Test all sizes and counts. + ASSERT_EQ(STATE_COUNT, transitionMatrix.getColumnCount()); + ASSERT_EQ(CHOICE_COUNT, transitionMatrix.getRowCount()); + ASSERT_EQ(12, transitionMatrix.getEntryCount()); + ASSERT_EQ(6, transitionMatrix.getRowGroupCount()); + ASSERT_EQ(7, transitionMatrix.getRowGroupIndices().size()); + ASSERT_EQ(CHOICE_COUNT, result.markovianChoices.size()); + ASSERT_EQ(STATE_COUNT, result.markovianStates.size()); + ASSERT_EQ(2, result.markovianStates.getNumberOfSetBits()); + ASSERT_EQ(STATE_COUNT, result.exitRates.size()); + + // Test the general structure of the transition system (that will be an Markov automaton). + + // Test the mapping between states and transition matrix rows. + ASSERT_EQ(0, transitionMatrix.getRowGroupIndices()[0]); + ASSERT_EQ(1, transitionMatrix.getRowGroupIndices()[1]); + ASSERT_EQ(2, transitionMatrix.getRowGroupIndices()[2]); + ASSERT_EQ(3, transitionMatrix.getRowGroupIndices()[3]); + ASSERT_EQ(4, transitionMatrix.getRowGroupIndices()[4]); + ASSERT_EQ(6, transitionMatrix.getRowGroupIndices()[5]); + ASSERT_EQ(7, transitionMatrix.getRowGroupIndices()[6]); + + // Test the Markovian states. + ASSERT_TRUE(result.markovianStates.get(0)); + ASSERT_FALSE(result.markovianStates.get(1)); + ASSERT_TRUE(result.markovianStates.get(2)); + ASSERT_FALSE(result.markovianStates.get(3)); + ASSERT_FALSE(result.markovianStates.get(4)); + ASSERT_FALSE(result.markovianStates.get(5)); + + // Test the exit rates. These have to be 0 for all non-Markovian states. + ASSERT_EQ(2, result.exitRates[0]); + ASSERT_EQ(0, result.exitRates[1]); + ASSERT_EQ(15, result.exitRates[2]); + ASSERT_EQ(0, result.exitRates[3]); + ASSERT_EQ(0, result.exitRates[4]); + ASSERT_EQ(0, result.exitRates[5]); + + // Finally, test the transition matrix itself. + storm::storage::SparseMatrix<double>::const_iterator cIter = transitionMatrix.begin(0); + + ASSERT_EQ(2, cIter->getValue()); + cIter++; + ASSERT_EQ(1, cIter->getValue()); + cIter++; + ASSERT_EQ(1, cIter->getValue()); + cIter++; + ASSERT_EQ(2, cIter->getValue()); + cIter++; + ASSERT_EQ(4, cIter->getValue()); + cIter++; + ASSERT_EQ(8, cIter->getValue()); + cIter++; + ASSERT_EQ(0.5, cIter->getValue()); + cIter++; + ASSERT_EQ(0.5, cIter->getValue()); + cIter++; + ASSERT_EQ(1, cIter->getValue()); + cIter++; + ASSERT_EQ(0.5, cIter->getValue()); + cIter++; + ASSERT_EQ(0.5, cIter->getValue()); + cIter++; + ASSERT_EQ(1, cIter->getValue()); + cIter++; + ASSERT_EQ(transitionMatrix.end(), cIter); +} + +TEST(MarkovAutomatonSparseTransitionParserTest, FixDeadlocks) { + // Set the fixDeadlocks flag temporarily. It is set to its old value once the deadlockOption object is destructed. + storm::settings::InternalOptionMemento setDeadlockOption("fixDeadlocks", true); + + // Parse a Markov Automaton transition file with the fixDeadlocks Flag set and test if it works. + storm::parser::MarkovAutomatonSparseTransitionParser::Result result = storm::parser::MarkovAutomatonSparseTransitionParser::parseMarkovAutomatonTransitions(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/ma_deadlock.tra"); + + // Test if the result is consistent with the parsed Markov Automaton. + storm::storage::SparseMatrix<double> resultMatrix(result.transitionMatrixBuilder.build()); + ASSERT_EQ(STATE_COUNT + 1, resultMatrix.getColumnCount()); + ASSERT_EQ(13, resultMatrix.getEntryCount()); + ASSERT_EQ(7, resultMatrix.getRowGroupCount()); + ASSERT_EQ(8, resultMatrix.getRowGroupIndices().size()); + ASSERT_EQ(CHOICE_COUNT +1, result.markovianChoices.size()); + ASSERT_EQ(STATE_COUNT +1, result.markovianStates.size()); + ASSERT_EQ(2, result.markovianStates.getNumberOfSetBits()); + ASSERT_EQ(STATE_COUNT + 1, result.exitRates.size()); +} + +TEST(MarkovAutomatonSparseTransitionParserTest, DontFixDeadlocks) { + // Try to parse a Markov Automaton transition file containing a deadlock state with the fixDeadlocksFlag unset. This should throw an exception. + storm::settings::InternalOptionMemento unsetDeadlockOption("fixDeadlocks", false); + + ASSERT_THROW(storm::parser::MarkovAutomatonSparseTransitionParser::parseMarkovAutomatonTransitions(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/ma_deadlock.tra"), storm::exceptions::WrongFormatException); +} diff --git a/test/functional/parser/NondeterministicModelParserTest.cpp b/test/functional/parser/NondeterministicModelParserTest.cpp new file mode 100644 index 000000000..30e4acc61 --- /dev/null +++ b/test/functional/parser/NondeterministicModelParserTest.cpp @@ -0,0 +1,103 @@ +/* + * NondeterministicModelParserTest.cpp + * + * Created on: Feb 26, 2014 + * Author: Manuel Sascha Weiand + */ + +#include "gtest/gtest.h" +#include "storm-config.h" + +#include "src/parser/NondeterministicModelParser.h" +#include "src/models/Mdp.h" +#include "src/models/Ctmdp.h" +#include "src/exceptions/FileIoException.h" + +TEST(NondeterministicModelParserTest, NonExistingFile) { + // No matter what happens, please do NOT create a file with the name "nonExistingFile.not"! + ASSERT_THROW(storm::parser::NondeterministicModelParser::parseMdp(STORM_CPP_TESTS_BASE_PATH "/nonExistingFile.not", STORM_CPP_TESTS_BASE_PATH "/nonExistingFile.not"), storm::exceptions::FileIoException); + + ASSERT_THROW(storm::parser::NondeterministicModelParser::parseCtmdp(STORM_CPP_TESTS_BASE_PATH "/nonExistingFile.not", STORM_CPP_TESTS_BASE_PATH "/nonExistingFile.not"), storm::exceptions::FileIoException); +} + +TEST(NondeterministicModelParserTest, BasicMdpParsing) { + + // Parse a Mdp and check the result. + storm::models::Mdp<double> mdp(storm::parser::NondeterministicModelParser::parseMdp(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/mdp_general.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/mdp_general.lab", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/mdp_general.state.rew", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/mdp_general.trans.rew")); + + ASSERT_EQ(6, mdp.getNumberOfStates()); + ASSERT_EQ(22, mdp.getNumberOfTransitions()); + ASSERT_EQ(11, mdp.getNumberOfChoices()); + + ASSERT_EQ(2, mdp.getInitialStates().getNumberOfSetBits()); + ASSERT_TRUE(mdp.getInitialStates().get(0)); + ASSERT_TRUE(mdp.getInitialStates().get(4)); + ASSERT_EQ(4, mdp.getStateLabeling().getNumberOfAtomicPropositions()); + ASSERT_EQ(3, mdp.getLabelsForState(0).size()); + + ASSERT_TRUE(mdp.hasStateRewards()); + ASSERT_EQ(0, mdp.getStateRewardVector()[0]); + ASSERT_EQ(42, mdp.getStateRewardVector()[4]); + double rewardSum = 0; + for(uint_fast64_t i = 0; i < mdp.getStateRewardVector().size(); i++) { + rewardSum += mdp.getStateRewardVector()[i]; + } + ASSERT_EQ(158.32, rewardSum); + + ASSERT_TRUE(mdp.hasTransitionRewards()); + ASSERT_EQ(17, mdp.getTransitionRewardMatrix().getEntryCount()); + rewardSum = 0; + for(uint_fast64_t i = 0; i < mdp.getTransitionRewardMatrix().getRowCount(); i++) { + rewardSum += mdp.getTransitionRewardMatrix().getRowSum(i); + } + ASSERT_EQ(1376.864, rewardSum); +} + + +TEST(NondeterministicModelParserTest, BasicCtmdpParsing) { + // Parse a Ctmdp and check the result. + storm::models::Ctmdp<double> ctmdp(storm::parser::NondeterministicModelParser::parseCtmdp(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/mdp_general.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/mdp_general.lab", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/mdp_general.state.rew", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/mdp_general.trans.rew")); + + ASSERT_EQ(6, ctmdp.getNumberOfStates()); + ASSERT_EQ(22, ctmdp.getNumberOfTransitions()); + ASSERT_EQ(11, ctmdp.getNumberOfChoices()); + + ASSERT_EQ(2, ctmdp.getInitialStates().getNumberOfSetBits()); + ASSERT_TRUE(ctmdp.getInitialStates().get(0)); + ASSERT_TRUE(ctmdp.getInitialStates().get(4)); + ASSERT_EQ(4, ctmdp.getStateLabeling().getNumberOfAtomicPropositions()); + ASSERT_EQ(3, ctmdp.getLabelsForState(0).size()); + + ASSERT_TRUE(ctmdp.hasStateRewards()); + ASSERT_EQ(0, ctmdp.getStateRewardVector()[0]); + ASSERT_EQ(42, ctmdp.getStateRewardVector()[4]); + double rewardSum = 0; + for(uint_fast64_t i = 0; i < ctmdp.getStateRewardVector().size(); i++) { + rewardSum += ctmdp.getStateRewardVector()[i]; + } + ASSERT_EQ(158.32, rewardSum); + + ASSERT_TRUE(ctmdp.hasTransitionRewards()); + ASSERT_EQ(ctmdp.getTransitionRewardMatrix().getEntryCount(), 17); + rewardSum = 0; + for(uint_fast64_t i = 0; i < ctmdp.getTransitionRewardMatrix().getRowCount(); i++) { + rewardSum += ctmdp.getTransitionRewardMatrix().getRowSum(i); + } + ASSERT_EQ(1376.864, rewardSum); +} + +TEST(NondeterministicModelParserTest, MismatchedFiles) { + // Test file combinations that do not match, i.e. differing number of states, transitions, etc. + + // The labeling file contains a label for a non existent state. + ASSERT_THROW(storm::parser::NondeterministicModelParser::parseMdp(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/mdp_mismatched.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/mdp_general.lab"), storm::exceptions::OutOfRangeException); + + // The state reward file contains a reward for a non existent state. + ASSERT_THROW(storm::parser::NondeterministicModelParser::parseMdp(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/mdp_mismatched.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/mdp_mismatched.lab", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/mdp_general.state.rew"), storm::exceptions::OutOfRangeException); + + // The transition reward file contains rewards for a non existent state. + ASSERT_THROW(storm::parser::NondeterministicModelParser::parseMdp(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/mdp_mismatched.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/mdp_mismatched.lab", "", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/mdp_general.trans.rew"), storm::exceptions::OutOfRangeException); + + // The transition reward file contains rewards for a non existent transition + ASSERT_THROW(storm::parser::NondeterministicModelParser::parseMdp(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/mdp_mismatched.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/mdp_mismatched.lab", "", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/mdp_mismatched.trans.rew"), storm::exceptions::OutOfRangeException); +} diff --git a/test/functional/parser/NondeterministicSparseTransitionParserTest.cpp b/test/functional/parser/NondeterministicSparseTransitionParserTest.cpp new file mode 100644 index 000000000..9494c983f --- /dev/null +++ b/test/functional/parser/NondeterministicSparseTransitionParserTest.cpp @@ -0,0 +1,269 @@ +/* + * NondeterministicSparseTransitionParserTest.cpp + * + * Created on: Feb 26, 2014 + * Author: Manuel Sascha Weiand + */ + +#include "gtest/gtest.h" +#include "storm-config.h" + +#include "src/parser/NondeterministicSparseTransitionParser.h" +#include "src/storage/SparseMatrix.h" +#include "src/settings/InternalOptionMemento.h" +#include "src/exceptions/FileIoException.h" +#include "src/exceptions/WrongFormatException.h" + +TEST(NondeterministicSparseTransitionParserTest, NonExistingFile) { + + // No matter what happens, please do NOT create a file with the name "nonExistingFile.not"! + ASSERT_THROW(storm::parser::NondeterministicSparseTransitionParser::parseNondeterministicTransitions(STORM_CPP_TESTS_BASE_PATH "/nonExistingFile.not"), storm::exceptions::FileIoException); + + storm::storage::SparseMatrix<double> nullInformation; + ASSERT_THROW(storm::parser::NondeterministicSparseTransitionParser::parseNondeterministicTransitionRewards(STORM_CPP_TESTS_BASE_PATH "/nonExistingFile.not", nullInformation), storm::exceptions::FileIoException); +} + + +TEST(NondeterministicSparseTransitionParserTest, BasicTransitionsParsing) { + + // Parse a nondeterministic transitions file and test the result. + storm::storage::SparseMatrix<double> result(storm::parser::NondeterministicSparseTransitionParser::parseNondeterministicTransitions(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/mdp_general.tra")); + + // Test the row mapping, i.e. at which row which state starts. + ASSERT_EQ(6, result.getRowGroupCount()); + ASSERT_EQ(7, result.getRowGroupIndices().size()); + ASSERT_EQ(0, result.getRowGroupIndices()[0]); + ASSERT_EQ(4, result.getRowGroupIndices()[1]); + ASSERT_EQ(5, result.getRowGroupIndices()[2]); + ASSERT_EQ(7, result.getRowGroupIndices()[3]); + ASSERT_EQ(8, result.getRowGroupIndices()[4]); + ASSERT_EQ(9, result.getRowGroupIndices()[5]); + ASSERT_EQ(11, result.getRowGroupIndices()[6]); + + // Test the transition matrix. + ASSERT_EQ(6, result.getColumnCount()); + ASSERT_EQ(11, result.getRowCount()); + ASSERT_EQ(22, result.getEntryCount()); + + // Test every entry of the matrix. + storm::storage::SparseMatrix<double>::const_iterator cIter = result.begin(0); + + ASSERT_EQ(0, cIter->getColumn()); + ASSERT_EQ(0.9, cIter->getValue()); + cIter++; + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0.1, cIter->getValue()); + cIter++; + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); + cIter++; + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); + cIter++; + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); + cIter++; + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); + cIter++; + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); + cIter++; + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); + cIter++; + ASSERT_EQ(0, cIter->getColumn()); + ASSERT_EQ(0.1, cIter->getValue()); + cIter++; + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.9, cIter->getValue()); + cIter++; + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); + cIter++; + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(0.5, cIter->getValue()); + cIter++; + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(0.5, cIter->getValue()); + cIter++; + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); + cIter++; + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(0.001, cIter->getValue()); + cIter++; + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(0.999, cIter->getValue()); + cIter++; + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0.7, cIter->getValue()); + cIter++; + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.3, cIter->getValue()); + cIter++; + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); + cIter++; + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); + cIter++; + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(0.6, cIter->getValue()); + cIter++; + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); +} + +TEST(NondeterministicSparseTransitionParserTest, BasicTransitionsRewardsParsing) { + // Parse a nondeterministic transitions file and test the result. + storm::storage::SparseMatrix<double> modelInformation(storm::parser::NondeterministicSparseTransitionParser::parseNondeterministicTransitions(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/mdp_general.tra")); + storm::storage::SparseMatrix<double> result(storm::parser::NondeterministicSparseTransitionParser::parseNondeterministicTransitionRewards(STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/mdp_general.trans.rew", modelInformation)); + + // Test the transition matrix. + ASSERT_EQ(6, result.getColumnCount()); + ASSERT_EQ(11, result.getRowCount()); + ASSERT_EQ(17, result.getEntryCount()); + + // Test every entry of the matrix. + storm::storage::SparseMatrix<double>::const_iterator cIter = result.begin(0); + + ASSERT_EQ(0, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); + cIter++; + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(30, cIter->getValue()); + cIter++; + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(15.2, cIter->getValue()); + cIter++; + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(75, cIter->getValue()); + cIter++; + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(2.45, cIter->getValue()); + cIter++; + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); + cIter++; + ASSERT_EQ(0, cIter->getColumn()); + ASSERT_EQ(0.114, cIter->getValue()); + cIter++; + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(90, cIter->getValue()); + cIter++; + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); + cIter++; + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(55, cIter->getValue()); + cIter++; + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(87, cIter->getValue()); + cIter++; + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(13, cIter->getValue()); + cIter++; + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(999, cIter->getValue()); + cIter++; + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0.7, cIter->getValue()); + cIter++; + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.3, cIter->getValue()); + cIter++; + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0.1, cIter->getValue()); + cIter++; + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(6, cIter->getValue()); +} + +TEST(NondeterministicSparseTransitionParserTest, Whitespaces) { + // Test the resilience of the parser against whitespaces. + // Do so by comparing the hashes of the transition matices and the rowMapping vectors element by element. + storm::storage::SparseMatrix<double> correctResult(storm::parser::NondeterministicSparseTransitionParser::parseNondeterministicTransitions(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/mdp_general.tra")); + storm::storage::SparseMatrix<double> whitespaceResult = storm::parser::NondeterministicSparseTransitionParser::parseNondeterministicTransitions(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/mdp_whitespaces.tra"); + ASSERT_EQ(correctResult.hash(), whitespaceResult.hash()); + ASSERT_EQ(correctResult.getRowGroupIndices().size(), whitespaceResult.getRowGroupIndices().size()); + for(uint_fast64_t i = 0; i < correctResult.getRowGroupIndices().size(); i++) { + ASSERT_EQ(correctResult.getRowGroupIndices()[i], whitespaceResult.getRowGroupIndices()[i]); + } + + // Do the same (minus the unused rowMapping) for the corresponding transition rewards file (with and without whitespaces) + uint_fast64_t correctHash = storm::parser::NondeterministicSparseTransitionParser::parseNondeterministicTransitionRewards(STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/mdp_general.trans.rew", correctResult).hash(); + ASSERT_EQ(correctHash, storm::parser::NondeterministicSparseTransitionParser::parseNondeterministicTransitionRewards(STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/mdp_whitespaces.trans.rew", whitespaceResult).hash()); +} + +TEST(NondeterministicSparseTransitionParserTest, MixedTransitionOrder) { + // Since the MatrixBuilder needs sequential input of new elements reordering of transitions or states should throw an exception. + ASSERT_THROW(storm::parser::NondeterministicSparseTransitionParser::parseNondeterministicTransitions(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/mdp_mixedTransitionOrder.tra"), storm::exceptions::InvalidArgumentException); + ASSERT_THROW(storm::parser::NondeterministicSparseTransitionParser::parseNondeterministicTransitions(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/mdp_mixedStateOrder.tra"), storm::exceptions::InvalidArgumentException); + + storm::storage::SparseMatrix<double> modelInformation = storm::parser::NondeterministicSparseTransitionParser::parseNondeterministicTransitions(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/mdp_general.tra"); + ASSERT_THROW(storm::parser::NondeterministicSparseTransitionParser::parseNondeterministicTransitionRewards(STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/mdp_mixedTransitionOrder.trans.rew", modelInformation), storm::exceptions::InvalidArgumentException); + ASSERT_THROW(storm::parser::NondeterministicSparseTransitionParser::parseNondeterministicTransitionRewards(STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/mdp_mixedStateOrder.trans.rew", modelInformation), storm::exceptions::InvalidArgumentException); +} + +TEST(NondeterministicSparseTransitionParserTest, FixDeadlocks) { + // Set the fixDeadlocks flag temporarily. It is set to its old value once the deadlockOption object is destructed. + storm::settings::InternalOptionMemento setDeadlockOption("fixDeadlocks", true); + + // Parse a transitions file with the fixDeadlocks Flag set and test if it works. + storm::storage::SparseMatrix<double> result(storm::parser::NondeterministicSparseTransitionParser::parseNondeterministicTransitions(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/mdp_deadlock.tra")); + + ASSERT_EQ(8, result.getRowGroupIndices().size()); + ASSERT_EQ(9, result.getRowGroupIndices()[5]); + ASSERT_EQ(10, result.getRowGroupIndices()[6]); + ASSERT_EQ(12, result.getRowGroupIndices()[7]); + + ASSERT_EQ(7, result.getColumnCount()); + ASSERT_EQ(12, result.getRowCount()); + ASSERT_EQ(23, result.getEntryCount()); + + storm::storage::SparseMatrix<double>::const_iterator cIter = result.begin(8); + + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0.7, cIter->getValue()); + cIter++; + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.3, cIter->getValue()); + cIter++; + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); + cIter++; + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); + cIter++; + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); + cIter++; + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(0.6, cIter->getValue()); + cIter++; + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); + +} + +TEST(NondeterministicSparseTransitionParserTest, DontFixDeadlocks) { + // Try to parse a transitions file containing a deadlock state with the fixDeadlocksFlag unset. This should throw an exception. + storm::settings::InternalOptionMemento unsetDeadlockOption("fixDeadlocks", false); + + ASSERT_THROW(storm::parser::NondeterministicSparseTransitionParser::parseNondeterministicTransitions(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/mdp_deadlock.tra"), storm::exceptions::WrongFormatException); +} + +TEST(NondeterministicSparseTransitionParserTest, DoubledLines) { + // There is a redundant line in the transition file. As the transition already exists this should throw an exception. + ASSERT_THROW(storm::parser::NondeterministicSparseTransitionParser::parseNondeterministicTransitions(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/mdp_doubledLines.tra"), storm::exceptions::InvalidArgumentException); +} + +TEST(NondeterministicSparseTransitionParserTest, RewardForNonExistentTransition) { + + // First parse a transition file. Then parse a transition reward file for the resulting transition matrix. + storm::storage::SparseMatrix<double> transitionResult = storm::parser::NondeterministicSparseTransitionParser::parseNondeterministicTransitions(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/mdp_general.tra"); + + // There is a reward for a transition that does not exist in the transition matrix. + ASSERT_THROW(storm::parser::NondeterministicSparseTransitionParser::parseNondeterministicTransitionRewards(STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/mdp_rewardForNonExTrans.trans.rew", transitionResult), storm::exceptions::WrongFormatException); +} diff --git a/test/functional/parser/ParseMdpTest.cpp b/test/functional/parser/ParseMdpTest.cpp deleted file mode 100644 index 58673269d..000000000 --- a/test/functional/parser/ParseMdpTest.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/* - * ParseMdpTest.cpp - * - * Created on: 14.01.2013 - * Author: Thomas Heinemann - */ - - -#include "gtest/gtest.h" -#include "storm-config.h" -#include "src/parser/NondeterministicModelParser.h" - -TEST(ParseMdpTest, parseAndOutput) { - storm::models::Mdp<double> mdp = storm::parser::NondeterministicModelParserAsMdp( - STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/mdp_general_input_01.tra", - STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/pctl_general_input_01.lab"); - storm::storage::SparseMatrix<double> const& matrix = mdp.getTransitionMatrix(); - - ASSERT_EQ(mdp.getNumberOfStates(), (uint_fast64_t)3); - ASSERT_EQ(mdp.getNumberOfTransitions(), (uint_fast64_t)11); - ASSERT_EQ(matrix.getRowCount(), (uint_fast64_t)(2 * 3)); - ASSERT_EQ(matrix.getColumnCount(), (uint_fast64_t)3); -} - - diff --git a/test/functional/parser/ParsePrismTest.cpp b/test/functional/parser/ParsePrismTest.cpp deleted file mode 100644 index 0a8abb420..000000000 --- a/test/functional/parser/ParsePrismTest.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "gtest/gtest.h" -#include "storm-config.h" -#include "src/parser/PrismParser.h" -//#include "src/utility/IoUtility.h" -#include "src/ir/Program.h" -#include "src/adapters/ExplicitModelAdapter.h" -#include "src/models/Dtmc.h" -#include "src/models/Mdp.h" -#include "src/settings/Settings.h" -#include "src/settings/InternalOptionMemento.h" - -TEST(ParsePrismTest, parseCrowds5_5) { - storm::settings::InternalOptionMemento deadlockOption("fixDeadlocks", true); - ASSERT_TRUE(storm::settings::Settings::getInstance()->isSet("fixDeadlocks")); - storm::ir::Program program; - ASSERT_NO_THROW(program = storm::parser::PrismParserFromFile(STORM_CPP_BASE_PATH "/examples/dtmc/crowds/crowds5_5.pm")); - std::shared_ptr<storm::models::AbstractModel<double>> model = storm::adapters::ExplicitModelAdapter<double>::translateProgram(program); - - ASSERT_EQ(model->getNumberOfStates(), 8607ull); - ASSERT_EQ(model->getNumberOfTransitions(), 15113ull); -} - -TEST(ParsePrismTest, parseTwoDice) { - storm::ir::Program program; - ASSERT_NO_THROW(program = storm::parser::PrismParserFromFile(STORM_CPP_BASE_PATH "/examples/mdp/two_dice/two_dice.nm")); - - std::shared_ptr<storm::models::AbstractModel<double>> model = storm::adapters::ExplicitModelAdapter<double>::translateProgram(program, "", "coinflips"); - - ASSERT_EQ(model->getNumberOfStates(), 169ull); - ASSERT_EQ(model->as<storm::models::AbstractNondeterministicModel<double>>()->getNumberOfChoices(), 254ull); - ASSERT_EQ(model->getNumberOfTransitions(), 436ull); -} diff --git a/test/functional/parser/PrctlParserTest.cpp b/test/functional/parser/PrctlParserTest.cpp index 923b5f941..c51cf3164 100644 --- a/test/functional/parser/PrctlParserTest.cpp +++ b/test/functional/parser/PrctlParserTest.cpp @@ -9,6 +9,7 @@ #include "gtest/gtest.h" #include "storm-config.h" #include "src/parser/PrctlParser.h" +#include "src/exceptions/WrongFormatException.h" TEST(PrctlParserTest, parseApOnlyTest) { std::string ap = "ap"; diff --git a/test/functional/parser/PrismParserTest.cpp b/test/functional/parser/PrismParserTest.cpp new file mode 100644 index 000000000..3621149b8 --- /dev/null +++ b/test/functional/parser/PrismParserTest.cpp @@ -0,0 +1,224 @@ +#include "gtest/gtest.h" +#include "storm-config.h" +#include "src/parser/PrismParser.h" + +TEST(PrismParser, StandardModelTest) { + storm::prism::Program result; + result = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/parser/prism/coin2.nm"); + EXPECT_NO_THROW(result = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/parser/prism/coin2.nm")); + EXPECT_NO_THROW(result = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/parser/prism/crowds5_5.pm")); + EXPECT_NO_THROW(result = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/parser/prism/csma2_2.nm")); + EXPECT_NO_THROW(result = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/parser/prism/die.pm")); + EXPECT_NO_THROW(result = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/parser/prism/firewire.nm")); + EXPECT_NO_THROW(result = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/parser/prism/leader3.nm")); + EXPECT_NO_THROW(result = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/parser/prism/leader3_5.pm")); + EXPECT_NO_THROW(result = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/parser/prism/two_dice.nm")); + EXPECT_NO_THROW(result = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/parser/prism/wlan0_collide.nm")); +} + +TEST(PrismParser, SimpleTest) { + std::string testInput = + R"(dtmc + module mod1 + b : bool; + [a] true -> 1: (b'=true != false = b => false); + endmodule)"; + + storm::prism::Program result; + EXPECT_NO_THROW(result = storm::parser::PrismParser::parseFromString(testInput, "testfile")); + EXPECT_EQ(1, result.getNumberOfModules()); + EXPECT_EQ(storm::prism::Program::ModelType::DTMC, result.getModelType()); + + testInput = + R"(mdp + + module main + x : [1..5] init 1; + [] x=1 -> 1:(x'=2); + [] x=2 -> 1:(x'=3); + [] x=3 -> 1:(x'=1); + [] x=3 -> 1:(x'=4); + [] x=4 -> 1:(x'=5); + endmodule)"; + EXPECT_NO_THROW(result = storm::parser::PrismParser::parseFromString(testInput, "testfile")); + EXPECT_EQ(1, result.getNumberOfModules()); + EXPECT_EQ(storm::prism::Program::ModelType::MDP, result.getModelType()); +} + +TEST(PrismParser, ComplexTest) { + std::string testInput = + R"(ma + + const int a; + const int b = 10; + const bool c; + const bool d = true | false; + const double e; + const double f = 9; + + formula test = a >= 10 & (max(a,b) > floor(e)); + formula test2 = a+b; + formula test3 = (a + b > 10 ? floor(a) : h) + a; + + global g : bool init false; + global h : [0 .. b]; + + module mod1 + i : bool; + j : bool init c; + k : [125..a] init a; + + [a] test&false -> (i'=true)&(k'=1+1) + 1 : (k'=floor(a) + max(k, b) - 1 + k); + endmodule + + module mod2 + [b] (k > 3) & false & (min(a, 0) < max(h, k)) -> 1-e: (g'=(1-a) * 2 + floor(f) > 2); + endmodule + + module mod3 = mod1 [ i = i1, j = j1, k = k1 ] endmodule + + label "mal" = max(a, 10) > 0; + + init + true + endinit + + rewards "testrewards" + [stateRew] true : a + 7; + max(f, a) <= 8 : 2*b; + endrewards + + rewards "testrewards2" + [stateRew] true : a + 7; + max(f, a) <= 8 : 2*b; + endrewards)"; + + storm::prism::Program result; + result = storm::parser::PrismParser::parseFromString(testInput, "testfile"); + EXPECT_EQ(storm::prism::Program::ModelType::MA, result.getModelType()); + EXPECT_EQ(3, result.getNumberOfModules()); + EXPECT_EQ(2, result.getNumberOfRewardModels()); + EXPECT_EQ(1, result.getNumberOfLabels()); +} + +TEST(PrismParser, IllegalInputTest) { + std::string testInput = + R"(ctmc + + const int a; + const bool a = true; + + module mod1 + c : [0 .. 8] init 1; + [] c < 3 -> 2: (c' = c+1); + endmodule + )"; + + storm::prism::Program result; + EXPECT_THROW(result = storm::parser::PrismParser::parseFromString(testInput, "testfile"), storm::exceptions::WrongFormatException); + + testInput = + R"(dtmc + + const int a; + + module mod1 + a : [0 .. 8] init 1; + [] a < 3 -> 1: (a' = a+1); + endmodule)"; + + EXPECT_THROW(result = storm::parser::PrismParser::parseFromString(testInput, "testfile"), storm::exceptions::WrongFormatException); + + testInput = + R"(dtmc + + const int a = 2; + formula a = 41; + + module mod1 + c : [0 .. 8] init 1; + [] c < 3 -> 1: (c' = c+1); + endmodule)"; + + EXPECT_THROW(result = storm::parser::PrismParser::parseFromString(testInput, "testfile"), storm::exceptions::WrongFormatException); + + testInput = + R"(dtmc + + const int a = 2; + + init + c > 3 + endinit + + module mod1 + c : [0 .. 8] init 1; + [] c < 3 -> 1: (c' = c+1); + endmodule + + init + c > 3 + endinit + + )"; + + EXPECT_THROW(result = storm::parser::PrismParser::parseFromString(testInput, "testfile"), storm::exceptions::WrongFormatException); + + testInput = + R"(dtmc + + module mod1 + c : [0 .. 8] init 1; + [] c < 3 -> 1: (c' = c+1); + endmodule + + module mod2 + [] c < 3 -> 1: (c' = c+1); + endmodule)"; + + EXPECT_THROW(result = storm::parser::PrismParser::parseFromString(testInput, "testfile"), storm::exceptions::WrongFormatException); + + testInput = + R"(dtmc + + module mod1 + c : [0 .. 8] init 1; + [] c < 3 -> 1: (c' = c+1)&(c'=c-1); + endmodule)"; + + EXPECT_THROW(result = storm::parser::PrismParser::parseFromString(testInput, "testfile"), storm::exceptions::WrongFormatException); + + testInput = + R"(dtmc + + module mod1 + c : [0 .. 8] init 1; + [] c < 3 -> 1: (c' = true || false); + endmodule)"; + + EXPECT_THROW(result = storm::parser::PrismParser::parseFromString(testInput, "testfile"), storm::exceptions::WrongFormatException); + + testInput = + R"(dtmc + + module mod1 + c : [0 .. 8] init 1; + [] c + 3 -> 1: (c' = 1); + endmodule)"; + + EXPECT_THROW(result = storm::parser::PrismParser::parseFromString(testInput, "testfile"), storm::exceptions::WrongFormatException); + + testInput = + R"(dtmc + + module mod1 + c : [0 .. 8] init 1; + [] c + 3 -> 1: (c' = 1); + endmodule + + label "test" = c + 1; + + )"; + + EXPECT_THROW(result = storm::parser::PrismParser::parseFromString(testInput, "testfile"), storm::exceptions::WrongFormatException); +} \ No newline at end of file diff --git a/test/functional/parser/ReadLabFileTest.cpp b/test/functional/parser/ReadLabFileTest.cpp deleted file mode 100644 index 2e35ee1ae..000000000 --- a/test/functional/parser/ReadLabFileTest.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * ReadLabFileTest.cpp - * - * Created on: 12.09.2012 - * Author: Thomas Heinemann - */ - -#include "gtest/gtest.h" -#include "storm-config.h" -#include "src/models/AtomicPropositionsLabeling.h" -#include "src/parser/AtomicPropositionLabelingParser.h" -#include "src/exceptions/FileIoException.h" -#include "src/exceptions/WrongFormatException.h" - -#include <memory> - -TEST(ReadLabFileTest, NonExistingFileTest) { - // No matter what happens, please do NOT create a file with the name "nonExistingFile.not"! - ASSERT_THROW(storm::parser::AtomicPropositionLabelingParser(0,STORM_CPP_TESTS_BASE_PATH "/nonExistingFile.not"), storm::exceptions::FileIoException); -} - -TEST(ReadLabFileTest, ParseTest) { - // This test is based on a test case from the original MRMC. - - // Parsing the file - storm::models::AtomicPropositionsLabeling labeling = storm::parser::AtomicPropositionLabelingParser(12, STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/pctl_general_input_01.lab"); - - // Checking whether all propositions are in the labelling - - char phi[] = "phi", psi[] = "psi", smth[] = "smth"; - - ASSERT_TRUE(labeling.containsAtomicProposition(phi)); - ASSERT_TRUE(labeling.containsAtomicProposition(psi)); - ASSERT_TRUE(labeling.containsAtomicProposition(smth)); - - // Testing whether all and only the correct nodes are labeled with "phi" - ASSERT_TRUE(labeling.getStateHasAtomicProposition(phi,0)); - ASSERT_TRUE(labeling.getStateHasAtomicProposition(phi,1)); - ASSERT_TRUE(labeling.getStateHasAtomicProposition(phi,2)); - - ASSERT_FALSE(labeling.getStateHasAtomicProposition(phi,3)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(phi,4)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(phi,5)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(phi,6)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(phi,7)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(phi,8)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(phi,9)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(phi,10)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(phi,11)); - - //Testing whether all and only the correct nodes are labeled with "psi" - ASSERT_FALSE(labeling.getStateHasAtomicProposition(psi,0)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(psi,1)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(psi,2)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(psi,3)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(psi,4)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(psi,5)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(psi,6)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(psi,7)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(psi,8)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(psi,9)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(psi,10)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(psi,11)); - - //Testing whether all and only the correct nodes are labeled with "smth" - ASSERT_TRUE(labeling.getStateHasAtomicProposition(smth,2)); - - ASSERT_FALSE(labeling.getStateHasAtomicProposition(smth,0)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(smth,1)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(smth,3)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(smth,4)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(smth,5)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(smth,6)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(smth,7)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(smth,8)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(smth,9)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(smth,10)); - ASSERT_FALSE(labeling.getStateHasAtomicProposition(smth,11)); -} - -TEST(ReadLabFileTest, WrongHeaderTest1) { - ASSERT_THROW(storm::parser::AtomicPropositionLabelingParser(3, STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/wrong_format_header1.lab"), storm::exceptions::WrongFormatException); -} - -TEST(ReadLabFileTest, WrongHeaderTest2) { - ASSERT_THROW(storm::parser::AtomicPropositionLabelingParser(3, STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/wrong_format_header2.lab"), storm::exceptions::WrongFormatException); -} - -TEST(ReadLabFileTest, WrongPropositionTest) { - ASSERT_THROW(storm::parser::AtomicPropositionLabelingParser(3, STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/wrong_format_proposition.lab"), storm::exceptions::WrongFormatException); -} - diff --git a/test/functional/parser/SparseStateRewardParserTest.cpp b/test/functional/parser/SparseStateRewardParserTest.cpp new file mode 100644 index 000000000..7a1d9120c --- /dev/null +++ b/test/functional/parser/SparseStateRewardParserTest.cpp @@ -0,0 +1,65 @@ +/* + * SparseStateRewardParserTest.cpp + * + * Created on: 26.02.2014 + * Author: Manuel Sascha Weiand + */ + +#include "gtest/gtest.h" +#include "storm-config.h" + +#include <cmath> + +#include "src/parser/SparseStateRewardParser.h" +#include "src/exceptions/FileIoException.h" +#include "src/exceptions/WrongFormatException.h" +#include "src/exceptions/OutOfRangeException.h" + +TEST(SparseStateRewardParserTest, NonExistingFile) { + + // No matter what happens, please do NOT create a file with the name "nonExistingFile.not"! + ASSERT_THROW(storm::parser::SparseStateRewardParser::parseSparseStateReward(42, STORM_CPP_TESTS_BASE_PATH "/nonExistingFile.not"), storm::exceptions::FileIoException); +} + +double round(double val, int precision) +{ + std::stringstream s; + s << std::setprecision(precision) << std::setiosflags(std::ios_base::fixed) << val; + s >> val; + return val; +} + +TEST(SparseStateRewardParserTest, BasicParsing) { + + // Get the parsing result. + std::vector<double> result = storm::parser::SparseStateRewardParser::parseSparseStateReward(100, STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/state_reward_parser_basic.state.rew"); + + // Now test if the correct value were parsed. + for(int i = 0; i < 100; i++) { + ASSERT_EQ(std::round(2*i + 15/13*i*i - 1.5/(i+0.1) + 15.7), std::round(result[i])); + } +} + +TEST(SparseStateRewardParserTest, Whitespaces) { + + // Get the parsing result. + std::vector<double> result = storm::parser::SparseStateRewardParser::parseSparseStateReward(100, STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/state_reward_parser_whitespaces.state.rew"); + + // Now test if the correct value were parsed. + for(int i = 0; i < 100; i++) { + ASSERT_EQ(std::round(2*i + 15/13*i*i - 1.5/(i+0.1) + 15.7), std::round(result[i])); + } +} + +TEST(SparseStateRewardParserTest, DoubledLines) { + // There are multiple lines attributing a reward to the same state. + ASSERT_THROW(storm::parser::SparseStateRewardParser::parseSparseStateReward(11, STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/state_reward_parser_doubledLines.state.rew"), storm::exceptions::WrongFormatException); + + // There is a line for a state that has been skipped. + ASSERT_THROW(storm::parser::SparseStateRewardParser::parseSparseStateReward(11, STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/state_reward_parser_doubledLinesSkipped.state.rew"), storm::exceptions::WrongFormatException); +} + +TEST(SparseStateRewardParserTest, RewardForNonExistentState) { + // The index of one of the state that are to be given rewards is higher than the number of states in the model. + ASSERT_THROW(storm::parser::SparseStateRewardParser::parseSparseStateReward(99, STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/state_reward_parser_basic.state.rew"), storm::exceptions::OutOfRangeException); +} diff --git a/test/functional/parser/prism/coin2.nm b/test/functional/parser/prism/coin2.nm new file mode 100644 index 000000000..a08bb8741 --- /dev/null +++ b/test/functional/parser/prism/coin2.nm @@ -0,0 +1,60 @@ +// COIN FLIPPING PROTOCOL FOR POLYNOMIAL RANDOMIZED CONSENSUS [AH90] +// gxn/dxp 20/11/00 + +mdp + +// constants +const int N=2; +const int K; +const int range = 2*(K+1)*N; +const int counter_init = (K+1)*N; +const int left = N; +const int right = 2*(K+1)*N - N; + +// shared coin +global counter : [0..range] init counter_init; + +module process1 + + // program counter + pc1 : [0..3]; + // 0 - flip + // 1 - write + // 2 - check + // 3 - finished + + // local coin + coin1 : [0..1]; + + // flip coin + [] (pc1=0) -> 0.5 : (coin1'=0) & (pc1'=1) + 0.5 : (coin1'=1) & (pc1'=1); + // write tails -1 (reset coin to add regularity) + [] (pc1=1) & (coin1=0) & (counter>0) -> 1 : (counter'=counter-1) & (pc1'=2) & (coin1'=0); + // write heads +1 (reset coin to add regularity) + [] (pc1=1) & (coin1=1) & (counter<range) -> 1 : (counter'=counter+1) & (pc1'=2) & (coin1'=0); + // check + // decide tails + [] (pc1=2) & (counter<=left) -> 1 : (pc1'=3) & (coin1'=0); + // decide heads + [] (pc1=2) & (counter>=right) -> 1 : (pc1'=3) & (coin1'=1); + // flip again + [] (pc1=2) & (counter>left) & (counter<right) -> 1 : (pc1'=0); + // loop (all loop together when done) + [done] (pc1=3) -> 1 : (pc1'=3); + +endmodule + +// construct remaining processes through renaming +module process2 = process1[pc1=pc2,coin1=coin2] endmodule + +// labels +label "finished" = pc1=3 & pc2=3 ; +label "all_coins_equal_0" = coin1=0 & coin2=0 ; +label "all_coins_equal_1" = coin1=1 & coin2=1 ; +label "agree" = coin1=coin2 ; + +// rewards +rewards "steps" + true : 1; +endrewards + diff --git a/test/functional/parser/prism/crowds5_5.pm b/test/functional/parser/prism/crowds5_5.pm new file mode 100644 index 000000000..60bdaa7ea --- /dev/null +++ b/test/functional/parser/prism/crowds5_5.pm @@ -0,0 +1,69 @@ +dtmc + +// probability of forwarding +const double PF = 0.8; +const double notPF = .2; // must be 1-PF +// probability that a crowd member is bad +const double badC = .167; + // probability that a crowd member is good +const double goodC = 0.833; +// Total number of protocol runs to analyze +const int TotalRuns = 5; +// size of the crowd +const int CrowdSize = 5; + +module crowds + // protocol phase + phase: [0..4] init 0; + + // crowd member good (or bad) + good: bool init false; + + // number of protocol runs + runCount: [0..TotalRuns] init 0; + + // observe_i is the number of times the attacker observed crowd member i + observe0: [0..TotalRuns] init 0; + + observe1: [0..TotalRuns] init 0; + + observe2: [0..TotalRuns] init 0; + + observe3: [0..TotalRuns] init 0; + + observe4: [0..TotalRuns] init 0; + + // the last seen crowd member + lastSeen: [0..CrowdSize - 1] init 0; + + // get the protocol started + [] phase=0 & runCount<TotalRuns -> 1: (phase'=1) & (runCount'=runCount+1) & (lastSeen'=0); + + // decide whether crowd member is good or bad according to given probabilities + [] phase=1 -> goodC : (phase'=2) & (good'=true) + badC : (phase'=2) & (good'=false); + + // if the current member is a good member, update the last seen index (chosen uniformly) + [] phase=2 & good -> 1/5 : (lastSeen'=0) & (phase'=3) + 1/5 : (lastSeen'=1) & (phase'=3) + 1/5 : (lastSeen'=2) & (phase'=3) + 1/5 : (lastSeen'=3) & (phase'=3) + 1/5 : (lastSeen'=4) & (phase'=3); + + // if the current member is a bad member, record the most recently seen index + [] phase=2 & !good & lastSeen=0 & observe0 < TotalRuns -> 1: (observe0'=observe0+1) & (phase'=4); + [] phase=2 & !good & lastSeen=1 & observe1 < TotalRuns -> 1: (observe1'=observe1+1) & (phase'=4); + [] phase=2 & !good & lastSeen=2 & observe2 < TotalRuns -> 1: (observe2'=observe2+1) & (phase'=4); + [] phase=2 & !good & lastSeen=3 & observe3 < TotalRuns -> 1: (observe3'=observe3+1) & (phase'=4); + [] phase=2 & !good & lastSeen=4 & observe4 < TotalRuns -> 1: (observe4'=observe4+1) & (phase'=4); + + // good crowd members forward with probability PF and deliver otherwise + [] phase=3 -> PF : (phase'=1) + notPF : (phase'=4); + + // deliver the message and start over + [] phase=4 -> 1: (phase'=0); + +endmodule + +label "observe0Greater1" = observe0>1; +label "observe1Greater1" = observe1>1; +label "observe2Greater1" = observe2>1; +label "observe3Greater1" = observe3>1; +label "observe4Greater1" = observe4>1; +label "observeIGreater1" = observe1>1|observe2>1|observe3>1|observe4>1; +label "observeOnlyTrueSender" = observe0>1&observe1<=1 & observe2<=1 & observe3<=1 & observe4<=1; diff --git a/test/functional/parser/prism/csma2_2.nm b/test/functional/parser/prism/csma2_2.nm new file mode 100644 index 000000000..18ec5c897 --- /dev/null +++ b/test/functional/parser/prism/csma2_2.nm @@ -0,0 +1,130 @@ +// CSMA/CD protocol - probabilistic version of kronos model (3 stations) +// gxn/dxp 04/12/01 + +mdp + +// note made changes since cannot have strict inequalities +// in digital clocks approach and suppose a station only sends one message + +// simplified parameters scaled +const int sigma=1; // time for messages to propagate along the bus +const int lambda=30; // time to send a message + +// actual parameters +const int N = 2; // number of processes +const int K = 2; // exponential backoff limit +const int slot = 2*sigma; // length of slot +// const int M = floor(pow(2, K))-1 ; // max number of slots to wait +const int M = 3 ; // max number of slots to wait +//const int lambda=782; +//const int sigma=26; + +// formula min_backoff_after_success = min(s1=4?cd1:K+1,s2=4?cd2:K+1); +// formula min_collisions = min(cd1,cd2); +// formula max_collisions = max(cd1,cd2); + +//---------------------------------------------------------------------------------------------------------------------------- +// the bus +module bus + + b : [0..2]; + // b=0 - idle + // b=1 - active + // b=2 - collision + + // clocks of bus + y1 : [0..sigma+1]; // time since first send (used find time until channel sensed busy) + y2 : [0..sigma+1]; // time since second send (used to find time until collision detected) + + // a sender sends (ok - no other message being sent) + [send1] (b=0) -> (b'=1); + [send2] (b=0) -> (b'=1); + + // a sender sends (bus busy - collision) + [send1] (b=1|b=2) & (y1<sigma) -> (b'=2); + [send2] (b=1|b=2) & (y1<sigma) -> (b'=2); + + // finish sending + [end1] (b=1) -> (b'=0) & (y1'=0); + [end2] (b=1) -> (b'=0) & (y1'=0); + + // bus busy + [busy1] (b=1|b=2) & (y1>=sigma) -> (b'=b); + [busy2] (b=1|b=2) & (y1>=sigma) -> (b'=b); + + // collision detected + [cd] (b=2) & (y2<=sigma) -> (b'=0) & (y1'=0) & (y2'=0); + + // time passage + [time] (b=0) -> (y1'=0); // value of y1/y2 does not matter in state 0 + [time] (b=1) -> (y1'=min(y1+1,sigma+1)); // no invariant in state 1 + [time] (b=2) & (y2<sigma) -> (y1'=min(y1+1,sigma+1)) & (y2'=min(y2+1,sigma+1)); // invariant in state 2 (time until collision detected) + +endmodule + +//---------------------------------------------------------------------------------------------------------------------------- +// model of first sender +module station1 + + // LOCAL STATE + s1 : [0..5]; + // s1=0 - initial state + // s1=1 - transmit + // s1=2 - collision (set backoff) + // s1=3 - wait (bus busy) + // s1=4 - successfully sent + + // LOCAL CLOCK + x1 : [0..max(lambda,slot)]; + + // BACKOFF COUNTER (number of slots to wait) + bc1 : [0..M]; + + // COLLISION COUNTER + cd1 : [0..K]; + + // start sending + [send1] (s1=0) -> (s1'=1) & (x1'=0); // start sending + [busy1] (s1=0) -> (s1'=2) & (x1'=0) & (cd1'=min(K,cd1+1)); // detects channel is busy so go into backoff + + // transmitting + [time] (s1=1) & (x1<lambda) -> (x1'=min(x1+1,lambda)); // let time pass + [end1] (s1=1) & (x1=lambda) -> (s1'=4) & (x1'=0); // finished + [cd] (s1=1) -> (s1'=2) & (x1'=0) & (cd1'=min(K,cd1+1)); // collision detected (increment backoff counter) + [cd] !(s1=1) -> (s1'=s1); // add loop for collision detection when not important + + // set backoff (no time can pass in this state) + // probability depends on which transmission this is (cd1) + [] s1=2 & cd1=1 -> 1/2 : (s1'=3) & (bc1'=0) + 1/2 : (s1'=3) & (bc1'=1) ; + [] s1=2 & cd1=2 -> 1/4 : (s1'=3) & (bc1'=0) + 1/4 : (s1'=3) & (bc1'=1) + 1/4 : (s1'=3) & (bc1'=2) + 1/4 : (s1'=3) & (bc1'=3) ; + + // wait until backoff counter reaches 0 then send again + [time] (s1=3) & (x1<slot) -> (x1'=x1+1); // let time pass (in slot) + [time] (s1=3) & (x1=slot) & (bc1>0) -> (x1'=1) & (bc1'=bc1-1); // let time pass (move slots) + [send1] (s1=3) & (x1=slot) & (bc1=0) -> (s1'=1) & (x1'=0); // finished backoff (bus appears free) + [busy1] (s1=3) & (x1=slot) & (bc1=0) -> (s1'=2) & (x1'=0) & (cd1'=min(K,cd1+1)); // finished backoff (bus busy) + + // once finished nothing matters + [time] (s1>=4) -> (x1'=0); + +endmodule + +//---------------------------------------------------------------------------------------------------------------------------- + +// construct further stations through renaming +module station2=station1[s1=s2,x1=x2,cd1=cd2,bc1=bc2,send1=send2,busy1=busy2,end1=end2] endmodule + +//---------------------------------------------------------------------------------------------------------------------------- + +// reward structure for expected time +rewards "time" + [time] true : 1; +endrewards + +//---------------------------------------------------------------------------------------------------------------------------- + +// labels/formulae +label "all_delivered" = s1=4&s2=4; +label "one_delivered" = s1=4|s2=4; +label "collision_max_backoff" = (cd1=K & s1=1 & b=2)|(cd2=K & s2=1 & b=2); + diff --git a/test/functional/parser/prism/die.pm b/test/functional/parser/prism/die.pm new file mode 100644 index 000000000..700951a05 --- /dev/null +++ b/test/functional/parser/prism/die.pm @@ -0,0 +1,24 @@ +// Knuth's model of a fair die using only fair coins +dtmc + +module die + + // local state + s : [0..7] init 0; + // value of the dice + d : [0..6] init 0; + + [] s=0 -> 0.5 : (s'=1) + 0.5 : (s'=2); + [] s=1 -> 0.5 : (s'=3) + 0.5 : (s'=4); + [] s=2 -> 0.5 : (s'=5) + 0.5 : (s'=6); + [] s=3 -> 0.5 : (s'=1) + 0.5 : (s'=7) & (d'=1); + [] s=4 -> 0.5 : (s'=7) & (d'=2) + 0.5 : (s'=7) & (d'=3); + [] s=5 -> 0.5 : (s'=7) & (d'=4) + 0.5 : (s'=7) & (d'=5); + [] s=6 -> 0.5 : (s'=2) + 0.5 : (s'=7) & (d'=6); + [] s=7 -> 1: (s'=7); + +endmodule + +rewards "coin_flips" + [] s<7 : 1; +endrewards diff --git a/test/functional/parser/prism/firewire.nm b/test/functional/parser/prism/firewire.nm new file mode 100644 index 000000000..26fa210b4 --- /dev/null +++ b/test/functional/parser/prism/firewire.nm @@ -0,0 +1,170 @@ +// firewire protocol with integer semantics +// dxp/gxn 14/06/01 + +// CLOCKS +// x1 (x2) clock for node1 (node2) +// y1 and y2 (z1 and z2) clocks for wire12 (wire21) +mdp + +// maximum and minimum delays +// fast +const int rc_fast_max = 85; +const int rc_fast_min = 76; +// slow +const int rc_slow_max = 167; +const int rc_slow_min = 159; +// delay caused by the wire length +const int delay; +// probability of choosing fast +const double fast; +const double slow=1-fast; + +module wire12 + + // local state + w12 : [0..9]; + // 0 - empty + // 1 - rec_req + // 2 - rec_req_ack + // 3 - rec_ack + // 4 - rec_ack_idle + // 5 - rec_idle + // 6 - rec_idle_req + // 7 - rec_ack_req + // 8 - rec_req_idle + // 9 - rec_idle_ack + + // clock for wire12 + y1 : [0..delay+1]; + y2 : [0..delay+1]; + + // empty + // do not need y1 and y2 to increase as always reset when this state is left + // similarly can reset y1 and y2 when we re-enter this state + [snd_req12] w12=0 -> (w12'=1) & (y1'=0) & (y2'=0); + [snd_ack12] w12=0 -> (w12'=3) & (y1'=0) & (y2'=0); + [snd_idle12] w12=0 -> (w12'=5) & (y1'=0) & (y2'=0); + [time] w12=0 -> (w12'=w12); + // rec_req + [snd_req12] w12=1 -> (w12'=1); + [rec_req12] w12=1 -> (w12'=0) & (y1'=0) & (y2'=0); + [snd_ack12] w12=1 -> (w12'=2) & (y2'=0); + [snd_idle12] w12=1 -> (w12'=8) & (y2'=0); + [time] w12=1 & y2<delay -> (y1'=min(y1+1,delay+1)) & (y2'=min(y2+1,delay+1)); + // rec_req_ack + [snd_ack12] w12=2 -> (w12'=2); + [rec_req12] w12=2 -> (w12'=3); + [time] w12=2 & y1<delay -> (y1'=min(y1+1,delay+1)) & (y2'=min(y2+1,delay+1)); + // rec_ack + [snd_ack12] w12=3 -> (w12'=3); + [rec_ack12] w12=3 -> (w12'=0) & (y1'=0) & (y2'=0); + [snd_idle12] w12=3 -> (w12'=4) & (y2'=0); + [snd_req12] w12=3 -> (w12'=7) & (y2'=0); + [time] w12=3 & y2<delay -> (y1'=min(y1+1,delay+1)) & (y2'=min(y2+1,delay+1)); + // rec_ack_idle + [snd_idle12] w12=4 -> (w12'=4); + [rec_ack12] w12=4 -> (w12'=5); + [time] w12=4 & y1<delay -> (y1'=min(y1+1,delay+1)) & (y2'=min(y2+1,delay+1)); + // rec_idle + [snd_idle12] w12=5 -> (w12'=5); + [rec_idle12] w12=5 -> (w12'=0) & (y1'=0) & (y2'=0); + [snd_req12] w12=5 -> (w12'=6) & (y2'=0); + [snd_ack12] w12=5 -> (w12'=9) & (y2'=0); + [time] w12=5 & y2<delay -> (y1'=min(y1+1,delay+1)) & (y2'=min(y2+1,delay+1)); + // rec_idle_req + [snd_req12] w12=6 -> (w12'=6); + [rec_idle12] w12=6 -> (w12'=1); + [time] w12=6 & y1<delay -> (y1'=min(y1+1,delay+1)) & (y2'=min(y2+1,delay+1)); + // rec_ack_req + [snd_req12] w12=7 -> (w12'=7); + [rec_ack12] w12=7 -> (w12'=1); + [time] w12=7 & y1<delay -> (y1'=min(y1+1,delay+1)) & (y2'=min(y2+1,delay+1)); + // rec_req_idle + [snd_idle12] w12=8 -> (w12'=8); + [rec_req12] w12=8 -> (w12'=5); + [time] w12=8 & y1<delay -> (y1'=min(y1+1,delay+1)) & (y2'=min(y2+1,delay+1)); + // rec_idle_ack + [snd_ack12] w12=9 -> (w12'=9); + [rec_idle12] w12=9 -> (w12'=3); + [time] w12=9 & y1<delay -> (y1'=min(y1+1,delay+1)) & (y2'=min(y2+1,delay+1)); + +endmodule + +module node1 + + // clock for node1 + x1 : [0..168]; + + // local state + s1 : [0..8]; + // 0 - root contention + // 1 - rec_idle + // 2 - rec_req_fast + // 3 - rec_req_slow + // 4 - rec_idle_fast + // 5 - rec_idle_slow + // 6 - snd_req + // 7- almost_root + // 8 - almost_child + + // added resets to x1 when not considered again until after rest + // removed root and child (using almost root and almost child) + + // root contention immediate state) + [snd_idle12] s1=0 -> fast : (s1'=2) & (x1'=0) + slow : (s1'=3) & (x1'=0); + [rec_idle21] s1=0 -> (s1'=1); + // rec_idle immediate state) + [snd_idle12] s1=1 -> fast : (s1'=4) & (x1'=0) + slow : (s1'=5) & (x1'=0); + [rec_req21] s1=1 -> (s1'=0); + // rec_req_fast + [rec_idle21] s1=2 -> (s1'=4); + [snd_ack12] s1=2 & x1>=rc_fast_min -> (s1'=7) & (x1'=0); + [time] s1=2 & x1<rc_fast_max -> (x1'=min(x1+1,168)); + // rec_req_slow + [rec_idle21] s1=3 -> (s1'=5); + [snd_ack12] s1=3 & x1>=rc_slow_min -> (s1'=7) & (x1'=0); + [time] s1=3 & x1<rc_slow_max -> (x1'=min(x1+1,168)); + // rec_idle_fast + [rec_req21] s1=4 -> (s1'=2); + [snd_req12] s1=4 & x1>=rc_fast_min -> (s1'=6) & (x1'=0); + [time] s1=4 & x1<rc_fast_max -> (x1'=min(x1+1,168)); + // rec_idle_slow + [rec_req21] s1=5 -> (s1'=3); + [snd_req12] s1=5 & x1>=rc_slow_min -> (s1'=6) & (x1'=0); + [time] s1=5 & x1<rc_slow_max -> (x1'=min(x1+1,168)); + // snd_req + // do not use x1 until reset (in state 0 or in state 1) so do not need to increase x1 + // also can set x1 to 0 upon entering this state + [rec_req21] s1=6 -> (s1'=0); + [rec_ack21] s1=6 -> (s1'=8); + [time] s1=6 -> (s1'=s1); + // almost root (immediate) + // loop in final states to remove deadlock + [] s1=7 & s2=8 -> (s1'=s1); + [] s1=8 & s2=7 -> (s1'=s1); + [time] s1=7 -> (s1'=s1); + [time] s1=8 -> (s1'=s1); + +endmodule + +// construct remaining automata through renaming +module wire21=wire12[w12=w21, y1=z1, y2=z2, + snd_req12=snd_req21, snd_idle12=snd_idle21, snd_ack12=snd_ack21, + rec_req12=rec_req21, rec_idle12=rec_idle21, rec_ack12=rec_ack21] +endmodule +module node2=node1[s1=s2, s2=s1, x1=x2, + rec_req21=rec_req12, rec_idle21=rec_idle12, rec_ack21=rec_ack12, + snd_req12=snd_req21, snd_idle12=snd_idle21, snd_ack12=snd_ack21] +endmodule + +// reward structures +// time +rewards "time" + [time] true : 1; +endrewards +// time nodes sending +rewards "time_sending" + [time] (w12>0 | w21>0) : 1; +endrewards + +label "elected" = ((s1=8) & (s2=7)) | ((s1=7) & (s2=8)); \ No newline at end of file diff --git a/test/functional/parser/prism/leader3.nm b/test/functional/parser/prism/leader3.nm new file mode 100644 index 000000000..5a8cd34b5 --- /dev/null +++ b/test/functional/parser/prism/leader3.nm @@ -0,0 +1,96 @@ +// asynchronous leader election +// 4 processes +// gxn/dxp 29/01/01 + +mdp + +const int N = 3; // number of processes + +//---------------------------------------------------------------------------------------------------------------------------- +module process1 + + // COUNTER + c1 : [0..3-1]; + + // STATES + s1 : [0..4]; + // 0 make choice + // 1 have not received neighbours choice + // 2 active + // 3 inactive + // 4 leader + + // PREFERENCE + p1 : [0..1]; + + // VARIABLES FOR SENDING AND RECEIVING + receive1 : [0..2]; + // not received anything + // received choice + // received counter + sent1 : [0..2]; + // not send anything + // sent choice + // sent counter + + // pick value + [] (s1=0) -> 0.5 : (s1'=1) & (p1'=0) + 0.5 : (s1'=1) & (p1'=1); + + // send preference + [p12] (s1=1) & (sent1=0) -> (sent1'=1); + // receive preference + // stay active + [p31] (s1=1) & (receive1=0) & !( (p1=0) & (p3=1) ) -> (s1'=2) & (receive1'=1); + // become inactive + [p31] (s1=1) & (receive1=0) & (p1=0) & (p3=1) -> (s1'=3) & (receive1'=1); + + // send preference (can now reset preference) + [p12] (s1=2) & (sent1=0) -> (sent1'=1) & (p1'=0); + // send counter (already sent preference) + // not received counter yet + [c12] (s1=2) & (sent1=1) & (receive1=1) -> (sent1'=2); + // received counter (pick again) + [c12] (s1=2) & (sent1=1) & (receive1=2) -> (s1'=0) & (p1'=0) & (c1'=0) & (sent1'=0) & (receive1'=0); + + // receive counter and not sent yet (note in this case do not pass it on as will send own counter) + [c31] (s1=2) & (receive1=1) & (sent1<2) -> (receive1'=2); + // receive counter and sent counter + // only active process (decide) + [c31] (s1=2) & (receive1=1) & (sent1=2) & (c3=N-1) -> (s1'=4) & (p1'=0) & (c1'=0) & (sent1'=0) & (receive1'=0); + // other active process (pick again) + [c31] (s1=2) & (receive1=1) & (sent1=2) & (c3<N-1) -> (s1'=0) & (p1'=0) & (c1'=0) & (sent1'=0) & (receive1'=0); + + // send preference (must have received preference) and can now reset + [p12] (s1=3) & (receive1>0) & (sent1=0) -> (sent1'=1) & (p1'=0); + // send counter (must have received counter first) and can now reset + [c12] (s1=3) & (receive1=2) & (sent1=1) -> (s1'=3) & (p1'=0) & (c1'=0) & (sent1'=0) & (receive1'=0); + + // receive preference + [p31] (s1=3) & (receive1=0) -> (p1'=p3) & (receive1'=1); + // receive counter + [c31] (s1=3) & (receive1=1) & (c3<N-1) -> (c1'=c3+1) & (receive1'=2); + + // done + [done] (s1=4) -> (s1'=s1); + // add loop for processes who are inactive + [done] (s1=3) -> (s1'=s1); + +endmodule + +//---------------------------------------------------------------------------------------------------------------------------- + +// construct further stations through renaming +module process2=process1[s1=s2,p1=p2,c1=c2,sent1=sent2,receive1=receive2,p12=p23,p31=p12,c12=c23,c31=c12,p3=p1,c3=c1] endmodule +module process3=process1[s1=s3,p1=p3,c1=c3,sent1=sent3,receive1=receive3,p12=p31,p31=p23,c12=c31,c31=c23,p3=p2,c3=c2] endmodule + +//---------------------------------------------------------------------------------------------------------------------------- + +// reward - expected number of rounds (equals the number of times a process receives a counter) +rewards "name" + [c12] true : 1; +endrewards + +//---------------------------------------------------------------------------------------------------------------------------- +formula leaders = (s1=4?1:0)+(s2=4?1:0)+(s3=4?1:0); +label "elected" = s1=4|s2=4|s3=4; + diff --git a/test/functional/parser/prism/leader3_5.pm b/test/functional/parser/prism/leader3_5.pm new file mode 100644 index 000000000..0703e733d --- /dev/null +++ b/test/functional/parser/prism/leader3_5.pm @@ -0,0 +1,85 @@ +// synchronous leader election protocol (itai & Rodeh) +// dxp/gxn 25/01/01 + +dtmc + +// CONSTANTS +const int N = 3; // number of processes +const int K = 5; // range of probabilistic choice + +// counter module used to count the number of processes that have been read +// and to know when a process has decided +module counter + + // counter (c=i means process j reading process (i-1)+j next) + c : [1..N-1]; + + // reading + [read] c<N-1 -> 1:(c'=c+1); + // finished reading + [read] c=N-1 -> 1:(c'=c); + //decide + [done] u1|u2|u3 -> 1:(c'=c); + // pick again reset counter + [retry] !(u1|u2|u3) -> 1:(c'=1); + // loop (when finished to avoid deadlocks) + [loop] s1=3 -> 1:(c'=c); + +endmodule + +// processes form a ring and suppose: +// process 1 reads process 2 +// process 2 reads process 3 +// process 3 reads process 1 +module process1 + + // local state + s1 : [0..3]; + // s1=0 make random choice + // s1=1 reading + // s1=2 deciding + // s1=3 finished + + // has a unique id so far (initially true) + u1 : bool; + + // value to be sent to next process in the ring (initially sets this to its own value) + v1 : [0..K-1]; + + // random choice + p1 : [0..K-1]; + + // pick value + [pick] s1=0 -> 1/K : (s1'=1) & (p1'=0) & (v1'=0) & (u1'=true) + + 1/K : (s1'=1) & (p1'=1) & (v1'=1) & (u1'=true) + + 1/K : (s1'=1) & (p1'=2) & (v1'=2) & (u1'=true) + + 1/K : (s1'=1) & (p1'=3) & (v1'=3) & (u1'=true) + + 1/K : (s1'=1) & (p1'=4) & (v1'=4) & (u1'=true); + // read + [read] s1=1 & u1 & c<N-1 -> 1:(u1'=(p1!=v2)) & (v1'=v2); + [read] s1=1 & !u1 & c<N-1 -> 1:(u1'=false) & (v1'=v2) & (p1'=0); + // read and move to decide + [read] s1=1 & u1 & c=N-1 -> 1:(s1'=2) & (u1'=(p1!=v2)) & (v1'=0) & (p1'=0); + [read] s1=1 & !u1 & c=N-1 -> 1:(s1'=2) & (u1'=false) & (v1'=0); + // deciding + // done + [done] s1=2 -> 1:(s1'=3) & (u1'=false) & (v1'=0) & (p1'=0); + //retry + [retry] s1=2 -> 1:(s1'=0) & (u1'=false) & (v1'=0) & (p1'=0); + // loop (when finished to avoid deadlocks) + [loop] s1=3 -> 1:(s1'=3); + +endmodule + +// construct remaining processes through renaming +module process2 = process1 [ s1=s2,p1=p2,v1=v2,u1=u2,v2=v3 ] endmodule +module process3 = process1 [ s1=s3,p1=p3,v1=v3,u1=u3,v2=v1 ] endmodule + +// expected number of rounds +rewards "num_rounds" + [pick] true : 1; +endrewards + +// labels +label "elected" = s1=3&s2=3&s3=3; + diff --git a/test/functional/parser/prism/two_dice.nm b/test/functional/parser/prism/two_dice.nm new file mode 100644 index 000000000..778153138 --- /dev/null +++ b/test/functional/parser/prism/two_dice.nm @@ -0,0 +1,40 @@ +// sum of two dice as the asynchronous parallel composition of +// two copies of Knuth's model of a fair die using only fair coins + +mdp + +module die1 + + // local state + s1 : [0..7] init 0; + // value of the dice + d1 : [0..6] init 0; + + [] s1=0 -> 0.5 : (s1'=1) + 0.5 : (s1'=2); + [] s1=1 -> 0.5 : (s1'=3) + 0.5 : (s1'=4); + [] s1=2 -> 0.5 : (s1'=5) + 0.5 : (s1'=6); + [] s1=3 -> 0.5 : (s1'=1) + 0.5 : (s1'=7) & (d1'=1); + [] s1=4 -> 0.5 : (s1'=7) & (d1'=2) + 0.5 : (s1'=7) & (d1'=3); + [] s1=5 -> 0.5 : (s1'=7) & (d1'=4) + 0.5 : (s1'=7) & (d1'=5); + [] s1=6 -> 0.5 : (s1'=2) + 0.5 : (s1'=7) & (d1'=6); + [] s1=7 & s2=7 -> 1: (s1'=7); +endmodule + +module die2 = die1 [ s1=s2, s2=s1, d1=d2 ] endmodule + +rewards "coinflips" + [] s1<7 | s2<7 : 1; +endrewards + +label "done" = s1=7 & s2=7; +label "two" = s1=7 & s2=7 & d1+d2=2; +label "three" = s1=7 & s2=7 & d1+d2=3; +label "four" = s1=7 & s2=7 & d1+d2=4; +label "five" = s1=7 & s2=7 & d1+d2=5; +label "six" = s1=7 & s2=7 & d1+d2=6; +label "seven" = s1=7 & s2=7 & d1+d2=7; +label "eight" = s1=7 & s2=7 & d1+d2=8; +label "nine" = s1=7 & s2=7 & d1+d2=9; +label "ten" = s1=7 & s2=7 & d1+d2=10; +label "eleven" = s1=7 & s2=7 & d1+d2=11; +label "twelve" = s1=7 & s2=7 & d1+d2=12; diff --git a/test/functional/parser/prism/wlan0_collide.nm b/test/functional/parser/prism/wlan0_collide.nm new file mode 100644 index 000000000..1c14704e5 --- /dev/null +++ b/test/functional/parser/prism/wlan0_collide.nm @@ -0,0 +1,219 @@ +// WLAN PROTOCOL (two stations) +// discrete time model +// gxn/jzs 20/02/02 + +mdp + +// COLLISIONS +const int COL; // maximum number of collisions + +// TIMING CONSTRAINTS +// we have used the FHSS parameters +// then scaled by the value of ASLOTTIME +const int ASLOTTIME = 1; +const int DIFS = 3; // due to scaling can be either 2 or 3 which is modelled by a non-deterministic choice +const int VULN = 1; // due to scaling can be either 0 or 1 which is modelled by a non-deterministic choice +const int TRANS_TIME_MAX; // scaling up +const int TRANS_TIME_MIN = 4; // scaling down +const int ACK_TO = 6; +const int ACK = 4; // due to scaling can be either 3 or 4 which is modelled by a non-deterministic choice +const int SIFS = 1; // due to scaling can be either 0 or 1 which is modelled by a non-deterministic choice +// maximum constant used in timing constraints + 1 +const int TIME_MAX = max(ACK_TO,TRANS_TIME_MAX)+1; + +// CONTENTION WINDOW +// CWMIN =15 & CWMAX =16 +// this means that MAX_BACKOFF IS 2 +const int MAX_BACKOFF = 0; + +//-----------------------------------------------------------------// +// THE MEDIUM/CHANNEL + +// FORMULAE FOR THE CHANNEL +// channel is busy +formula busy = c1>0 | c2>0; +// channel is free +formula free = c1=0 & c2=0; + +module medium + + // number of collisions + col : [0..COL]; + + // medium status + c1 : [0..2]; + c2 : [0..2]; + // ci corresponds to messages associated with station i + // 0 nothing being sent + // 1 being sent correctly + // 2 being sent garbled + + // begin sending message and nothing else currently being sent + [send1] c1=0 & c2=0 -> (c1'=1); + [send2] c2=0 & c1=0 -> (c2'=1); + + // begin sending message and something is already being sent + // in this case both messages become garbled + [send1] c1=0 & c2>0 -> (c1'=2) & (c2'=2) & (col'=min(col+1,COL)); + [send2] c2=0 & c1>0 -> (c1'=2) & (c2'=2) & (col'=min(col+1,COL)); + + // finish sending message + [finish1] c1>0 -> (c1'=0); + [finish2] c2>0 -> (c2'=0); + +endmodule + +//-----------------------------------------------------------------// +// STATION 1 +module station1 + // clock for station 1 + x1 : [0..TIME_MAX]; + + // local state + s1 : [1..12]; + // 1 sense + // 2 wait until free before setting backoff + // 3 wait for DIFS then set slot + // 4 set backoff + // 5 backoff + // 6 wait until free in backoff + // 7 wait for DIFS then resume backoff + // 8 vulnerable + // 9 transmit + // 11 wait for SIFS and then ACK + // 10 wait for ACT_TO + // 12 done + // BACKOFF + // separate into slots + slot1 : [0..1]; + backoff1 : [0..15]; + + // BACKOFF COUNTER + bc1 : [0..1]; + // SENSE + // let time pass + [time] s1=1 & x1<DIFS & free -> (x1'=min(x1+1,TIME_MAX)); + // ready to transmit + [] s1=1 & (x1=DIFS | x1=DIFS-1) -> (s1'=8) & (x1'=0); + // found channel busy so wait until free + [] s1=1 & busy -> (s1'=2) & (x1'=0); + // WAIT UNTIL FREE BEFORE SETTING BACKOFF + // let time pass (no need for the clock x1 to change) + [time] s1=2 & busy -> (s1'=2); + // find that channel is free so check its free for DIFS before setting backoff + [] s1=2 & free -> (s1'=3); + // WAIT FOR DIFS THEN SET BACKOFF + // let time pass + [time] s1=3 & x1<DIFS & free -> (x1'=min(x1+1,TIME_MAX)); + // found channel busy so wait until free + [] s1=3 & busy -> (s1'=2) & (x1'=0); + // start backoff first uniformly choose slot + // backoff counter 0 + [] s1=3 & (x1=DIFS | x1=DIFS-1) & bc1=0 -> (s1'=4) & (x1'=0) & (slot1'=0) & (bc1'=min(bc1+1,MAX_BACKOFF)); + // SET BACKOFF (no time can pass) + // chosen slot now set backoff + [] s1=4 -> 1/16 : (s1'=5) & (backoff1'=0 ) + + 1/16 : (s1'=5) & (backoff1'=1 ) + + 1/16 : (s1'=5) & (backoff1'=2 ) + + 1/16 : (s1'=5) & (backoff1'=3 ) + + 1/16 : (s1'=5) & (backoff1'=4 ) + + 1/16 : (s1'=5) & (backoff1'=5 ) + + 1/16 : (s1'=5) & (backoff1'=6 ) + + 1/16 : (s1'=5) & (backoff1'=7 ) + + 1/16 : (s1'=5) & (backoff1'=8 ) + + 1/16 : (s1'=5) & (backoff1'=9 ) + + 1/16 : (s1'=5) & (backoff1'=10) + + 1/16 : (s1'=5) & (backoff1'=11) + + 1/16 : (s1'=5) & (backoff1'=12) + + 1/16 : (s1'=5) & (backoff1'=13) + + 1/16 : (s1'=5) & (backoff1'=14) + + 1/16 : (s1'=5) & (backoff1'=15); + // BACKOFF + // let time pass + [time] s1=5 & x1<ASLOTTIME & free -> (x1'=min(x1+1,TIME_MAX)); + // decrement backoff + [] s1=5 & x1=ASLOTTIME & backoff1>0 -> (s1'=5) & (x1'=0) & (backoff1'=backoff1-1); + [] s1=5 & x1=ASLOTTIME & backoff1=0 & slot1>0 -> (s1'=5) & (x1'=0) & (backoff1'=15) & (slot1'=slot1-1); + // finish backoff + [] s1=5 & x1=ASLOTTIME & backoff1=0 & slot1=0 -> (s1'=8) & (x1'=0); + // found channel busy + [] s1=5 & busy -> (s1'=6) & (x1'=0); + // WAIT UNTIL FREE IN BACKOFF + // let time pass (no need for the clock x1 to change) + [time] s1=6 & busy -> (s1'=6); + // find that channel is free + [] s1=6 & free -> (s1'=7); + + // WAIT FOR DIFS THEN RESUME BACKOFF + // let time pass + [time] s1=7 & x1<DIFS & free -> (x1'=min(x1+1,TIME_MAX)); + // resume backoff (start again from previous backoff) + [] s1=7 & (x1=DIFS | x1=DIFS-1) -> (s1'=5) & (x1'=0); + // found channel busy + [] s1=7 & busy -> (s1'=6) & (x1'=0); + + // VULNERABLE + // let time pass + [time] s1=8 & x1<VULN -> (x1'=min(x1+1,TIME_MAX)); + // move to transmit + [send1] s1=8 & (x1=VULN | x1=VULN-1) -> (s1'=9) & (x1'=0); + // TRANSMIT + // let time pass + [time] s1=9 & x1<TRANS_TIME_MAX -> (x1'=min(x1+1,TIME_MAX)); + // finish transmission successful + [finish1] s1=9 & x1>=TRANS_TIME_MIN & c1=1 -> (s1'=10) & (x1'=0); + // finish transmission garbled + [finish1] s1=9 & x1>=TRANS_TIME_MIN & c1=2 -> (s1'=11) & (x1'=0); + // WAIT FOR SIFS THEN WAIT FOR ACK + + // WAIT FOR SIFS i.e. c1=0 + // check channel and busy: go into backoff + [] s1=10 & c1=0 & x1=0 & busy -> (s1'=2); + // check channel and free: let time pass + [time] s1=10 & c1=0 & x1=0 & free -> (x1'=min(x1+1,TIME_MAX)); + // let time pass + // following guard is always false as SIFS=1 + // [time] s1=10 & c1=0 & x1>0 & x1<SIFS -> (x1'=min(x1+1,TIME_MAX)); + // ack is sent after SIFS (since SIFS-1=0 add condition that channel is free) + [send1] s1=10 & c1=0 & (x1=SIFS | (x1=SIFS-1 & free)) -> (s1'=10) & (x1'=0); + + // WAIT FOR ACK i.e. c1=1 + // let time pass + [time] s1=10 & c1=1 & x1<ACK -> (x1'=min(x1+1,TIME_MAX)); + // get acknowledgement so packet sent correctly and move to done + [finish1] s1=10 & c1=1 & (x1=ACK | x1=ACK-1) -> (s1'=12) & (x1'=0) & (bc1'=0); + + // WAIT FOR ACK_TO + // check channel and busy: go into backoff + [] s1=11 & x1=0 & busy -> (s1'=2); + // check channel and free: let time pass + [time] s1=11 & x1=0 & free -> (x1'=min(x1+1,TIME_MAX)); + // let time pass + [time] s1=11 & x1>0 & x1<ACK_TO -> (x1'=min(x1+1,TIME_MAX)); + // no acknowledgement (go to backoff waiting DIFS first) + [] s1=11 & x1=ACK_TO -> (s1'=3) & (x1'=0); + + // DONE + [time] s1=12 -> (s1'=12); + +endmodule + +// ---------------------------------------------------------------------------- // +// STATION 2 (rename STATION 1) +module +station2=station1[x1=x2, + s1=s2, + s2=s1, + c1=c2, + c2=c1, + slot1=slot2, + backoff1=backoff2, + bc1=bc2, + send1=send2, + finish1=finish2] +endmodule +// ---------------------------------------------------------------------------- // + +label "twoCollisions" = col=2; +label "fourCollisions" = col=4; +label "sixCollisions" = col=6; \ No newline at end of file diff --git a/test/functional/parser/testStringFile.txt b/test/functional/parser/testStringFile.txt new file mode 100644 index 000000000..fe200f008 --- /dev/null +++ b/test/functional/parser/testStringFile.txt @@ -0,0 +1 @@ +This is a test string. diff --git a/test/functional/solver/GlpkLpSolverTest.cpp b/test/functional/solver/GlpkLpSolverTest.cpp index 30c3e10f9..a6410611d 100644 --- a/test/functional/solver/GlpkLpSolverTest.cpp +++ b/test/functional/solver/GlpkLpSolverTest.cpp @@ -3,34 +3,34 @@ #include "src/solver/GlpkLpSolver.h" #include "src/exceptions/InvalidStateException.h" +#include "src/exceptions/InvalidAccessException.h" #include "src/settings/Settings.h" TEST(GlpkLpSolver, LPOptimizeMax) { #ifdef STORM_HAVE_GLPK - storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - + storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_TRUE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); double xValue = 0; - ASSERT_NO_THROW(xValue = solver.getContinuousValue(xIndex)); + ASSERT_NO_THROW(xValue = solver.getContinuousValue("x")); ASSERT_LT(std::abs(xValue - 1), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double yValue = 0; - ASSERT_NO_THROW(yValue = solver.getContinuousValue(yIndex)); + ASSERT_NO_THROW(yValue = solver.getContinuousValue("y")); ASSERT_LT(std::abs(yValue - 6.5), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double zValue = 0; - ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex)); + ASSERT_NO_THROW(zValue = solver.getContinuousValue("z")); ASSERT_LT(std::abs(zValue - 2.75), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double objectiveValue = 0; ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue()); @@ -42,30 +42,29 @@ TEST(GlpkLpSolver, LPOptimizeMax) { TEST(GlpkLpSolver, LPOptimizeMin) { #ifdef STORM_HAVE_GLPK - storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MINIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::BOUNDED, 1, 5.7, -1)); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - + storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Minimize); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("z", 1, 5.7, -1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_TRUE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); double xValue = 0; - ASSERT_NO_THROW(xValue = solver.getContinuousValue(xIndex)); + ASSERT_NO_THROW(xValue = solver.getContinuousValue("x")); ASSERT_LT(std::abs(xValue - 1), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double yValue = 0; - ASSERT_NO_THROW(yValue = solver.getContinuousValue(yIndex)); + ASSERT_NO_THROW(yValue = solver.getContinuousValue("y")); ASSERT_LT(std::abs(yValue - 0), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double zValue = 0; - ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex)); + ASSERT_NO_THROW(zValue = solver.getContinuousValue("z")); ASSERT_LT(std::abs(zValue - 5.7), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double objectiveValue = 0; ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue()); @@ -77,30 +76,29 @@ TEST(GlpkLpSolver, LPOptimizeMin) { TEST(GlpkLpSolver, MILPOptimizeMax) { #ifdef STORM_HAVE_GLPK - storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - + storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBinaryVariable("x", -1)); + ASSERT_NO_THROW(solver.addLowerBoundedIntegerVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_TRUE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); bool xValue = false; - ASSERT_NO_THROW(xValue = solver.getBinaryValue(xIndex)); + ASSERT_NO_THROW(xValue = solver.getBinaryValue("x")); ASSERT_EQ(true, xValue); int_fast64_t yValue = 0; - ASSERT_NO_THROW(yValue = solver.getIntegerValue(yIndex)); + ASSERT_NO_THROW(yValue = solver.getIntegerValue("y")); ASSERT_EQ(6, yValue); double zValue = 0; - ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex)); + ASSERT_NO_THROW(zValue = solver.getContinuousValue("z")); ASSERT_LT(std::abs(zValue - 3), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double objectiveValue = 0; ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue()); @@ -112,30 +110,29 @@ TEST(GlpkLpSolver, MILPOptimizeMax) { TEST(GlpkLpSolver, MILPOptimizeMin) { #ifdef STORM_HAVE_GLPK - storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MINIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createIntegerVariable("z", storm::solver::LpSolver::VariableType::BOUNDED, 0, 5, -1)); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - + storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Minimize); + ASSERT_NO_THROW(solver.addBinaryVariable("x", -1)); + ASSERT_NO_THROW(solver.addLowerBoundedIntegerVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("z", 0, 5, -1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_TRUE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); bool xValue = false; - ASSERT_NO_THROW(xValue = solver.getBinaryValue(xIndex)); + ASSERT_NO_THROW(xValue = solver.getBinaryValue("x")); ASSERT_EQ(true, xValue); int_fast64_t yValue = 0; - ASSERT_NO_THROW(yValue = solver.getIntegerValue(yIndex)); + ASSERT_NO_THROW(yValue = solver.getIntegerValue("y")); ASSERT_EQ(0, yValue); double zValue = 0; - ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex)); + ASSERT_NO_THROW(zValue = solver.getContinuousValue("z")); ASSERT_LT(std::abs(zValue - 5), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double objectiveValue = 0; ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue()); @@ -147,31 +144,30 @@ TEST(GlpkLpSolver, MILPOptimizeMin) { TEST(GlpkLpSolver, LPInfeasible) { #ifdef STORM_HAVE_GLPK - storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex}, {1}, storm::solver::LpSolver::BoundType::GREATER_EQUAL, 7)); - + storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") > storm::expressions::Expression::createDoubleLiteral(7))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_FALSE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_TRUE(solver.isInfeasible()); double xValue = 0; - ASSERT_THROW(xValue = solver.getContinuousValue(xIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(xValue = solver.getContinuousValue("x"), storm::exceptions::InvalidAccessException); double yValue = 0; - ASSERT_THROW(yValue = solver.getContinuousValue(yIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(yValue = solver.getContinuousValue("y"), storm::exceptions::InvalidAccessException); double zValue = 0; - ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException); double objectiveValue = 0; - ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException); + ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException); #else ASSERT_TRUE(false) << "StoRM built without glpk support."; #endif @@ -179,31 +175,30 @@ TEST(GlpkLpSolver, LPInfeasible) { TEST(GlpkLpSolver, MILPInfeasible) { #ifdef STORM_HAVE_GLPK - storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex}, {1}, storm::solver::LpSolver::BoundType::GREATER_EQUAL, 7)); - + storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBinaryVariable("x", -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") > storm::expressions::Expression::createDoubleLiteral(7))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_FALSE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_TRUE(solver.isInfeasible()); bool xValue = false; - ASSERT_THROW(xValue = solver.getBinaryValue(xIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(xValue = solver.getBinaryValue("x"), storm::exceptions::InvalidAccessException); int_fast64_t yValue = 0; - ASSERT_THROW(yValue = solver.getIntegerValue(yIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(yValue = solver.getIntegerValue("y"), storm::exceptions::InvalidAccessException); double zValue = 0; - ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException); double objectiveValue = 0; - ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException); + ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException); #else ASSERT_TRUE(false) << "StoRM built without glpk support."; #endif @@ -211,29 +206,28 @@ TEST(GlpkLpSolver, MILPInfeasible) { TEST(GlpkLpSolver, LPUnbounded) { #ifdef STORM_HAVE_GLPK - storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - + storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_FALSE(solver.isOptimal()); ASSERT_TRUE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); double xValue = 0; - ASSERT_THROW(xValue = solver.getContinuousValue(xIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(xValue = solver.getContinuousValue("x"), storm::exceptions::InvalidAccessException); double yValue = 0; - ASSERT_THROW(yValue = solver.getContinuousValue(yIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(yValue = solver.getContinuousValue("y"), storm::exceptions::InvalidAccessException); double zValue = 0; - ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException); double objectiveValue = 0; - ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException); + ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException); #else ASSERT_TRUE(false) << "StoRM built without glpk support."; #endif @@ -241,30 +235,29 @@ TEST(GlpkLpSolver, LPUnbounded) { TEST(GlpkLpSolver, MILPUnbounded) { #ifdef STORM_HAVE_GLPK - storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - + storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBinaryVariable("x", -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_FALSE(solver.isOptimal()); ASSERT_TRUE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); bool xValue = false; - ASSERT_THROW(xValue = solver.getBinaryValue(xIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(xValue = solver.getBinaryValue("x"), storm::exceptions::InvalidAccessException); int_fast64_t yValue = 0; - ASSERT_THROW(yValue = solver.getIntegerValue(yIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(yValue = solver.getIntegerValue("y"), storm::exceptions::InvalidAccessException); double zValue = 0; - ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException); double objectiveValue = 0; - ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException); + ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException); #else ASSERT_TRUE(false) << "StoRM built without glpk support."; #endif -} \ No newline at end of file +} diff --git a/test/functional/solver/GmmxxLinearEquationSolverTest.cpp b/test/functional/solver/GmmxxLinearEquationSolverTest.cpp index 077920f13..0381be434 100644 --- a/test/functional/solver/GmmxxLinearEquationSolverTest.cpp +++ b/test/functional/solver/GmmxxLinearEquationSolverTest.cpp @@ -81,7 +81,7 @@ TEST(GmmxxLinearEquationSolver, qmr) { ASSERT_NO_THROW(storm::solver::GmmxxLinearEquationSolver<double> solver(storm::solver::GmmxxLinearEquationSolver<double>::QMR, 1e-6, 10000, storm::solver::GmmxxLinearEquationSolver<double>::NONE)); - storm::solver::GmmxxLinearEquationSolver<double> solver(storm::solver::GmmxxLinearEquationSolver<double>::QMR, 1e-6, 10000, storm::solver::GmmxxLinearEquationSolver<double>::NONE, 50); + storm::solver::GmmxxLinearEquationSolver<double> solver(storm::solver::GmmxxLinearEquationSolver<double>::QMR, 1e-6, 10000, storm::solver::GmmxxLinearEquationSolver<double>::NONE, true, 50); ASSERT_NO_THROW(solver.solveEquationSystem(A, x, b)); ASSERT_LT(std::abs(x[0] - 1), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); ASSERT_LT(std::abs(x[1] - 3), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); diff --git a/test/functional/solver/GurobiLpSolverTest.cpp b/test/functional/solver/GurobiLpSolverTest.cpp index 1ca524d8e..ee0ebfa03 100644 --- a/test/functional/solver/GurobiLpSolverTest.cpp +++ b/test/functional/solver/GurobiLpSolverTest.cpp @@ -3,36 +3,34 @@ #include "src/solver/GurobiLpSolver.h" #include "src/exceptions/InvalidStateException.h" +#include "src/exceptions/InvalidAccessException.h" #include "src/settings/Settings.h" TEST(GurobiLpSolver, LPOptimizeMax) { #ifdef STORM_HAVE_GUROBI - storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.update()); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); + storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_TRUE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); double xValue = 0; - ASSERT_NO_THROW(xValue = solver.getContinuousValue(xIndex)); + ASSERT_NO_THROW(xValue = solver.getContinuousValue("x")); ASSERT_LT(std::abs(xValue - 1), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double yValue = 0; - ASSERT_NO_THROW(yValue = solver.getContinuousValue(yIndex)); + ASSERT_NO_THROW(yValue = solver.getContinuousValue("y")); ASSERT_LT(std::abs(yValue - 6.5), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double zValue = 0; - ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex)); + ASSERT_NO_THROW(zValue = solver.getContinuousValue("z")); ASSERT_LT(std::abs(zValue - 2.75), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double objectiveValue = 0; ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue()); @@ -44,32 +42,29 @@ TEST(GurobiLpSolver, LPOptimizeMax) { TEST(GurobiLpSolver, LPOptimizeMin) { #ifdef STORM_HAVE_GUROBI - storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MINIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::BOUNDED, 1, 5.7, -1)); - - ASSERT_NO_THROW(solver.update()); + storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Minimize); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1)); + ASSERT_NO_THROW(solver.addLowerBoundedIntegerVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("z", 1, 5.7, -1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - ASSERT_NO_THROW(solver.optimize()); ASSERT_TRUE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); double xValue = 0; - ASSERT_NO_THROW(xValue = solver.getContinuousValue(xIndex)); + ASSERT_NO_THROW(xValue = solver.getContinuousValue("x")); ASSERT_LT(std::abs(xValue - 1), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double yValue = 0; - ASSERT_NO_THROW(yValue = solver.getContinuousValue(yIndex)); + ASSERT_NO_THROW(yValue = solver.getContinuousValue("y")); ASSERT_LT(std::abs(yValue - 0), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double zValue = 0; - ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex)); + ASSERT_NO_THROW(zValue = solver.getContinuousValue("z")); ASSERT_LT(std::abs(zValue - 5.7), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double objectiveValue = 0; ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue()); @@ -81,32 +76,29 @@ TEST(GurobiLpSolver, LPOptimizeMin) { TEST(GurobiLpSolver, MILPOptimizeMax) { #ifdef STORM_HAVE_GUROBI - storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.update()); + storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBinaryVariable("x", -1)); + ASSERT_NO_THROW(solver.addLowerBoundedIntegerVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - ASSERT_NO_THROW(solver.optimize()); ASSERT_TRUE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); bool xValue = false; - ASSERT_NO_THROW(xValue = solver.getBinaryValue(xIndex)); + ASSERT_NO_THROW(xValue = solver.getBinaryValue("x")); ASSERT_EQ(true, xValue); int_fast64_t yValue = 0; - ASSERT_NO_THROW(yValue = solver.getIntegerValue(yIndex)); + ASSERT_NO_THROW(yValue = solver.getIntegerValue("y")); ASSERT_EQ(6, yValue); double zValue = 0; - ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex)); + ASSERT_NO_THROW(zValue = solver.getContinuousValue("z")); ASSERT_LT(std::abs(zValue - 3), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double objectiveValue = 0; ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue()); @@ -118,32 +110,29 @@ TEST(GurobiLpSolver, MILPOptimizeMax) { TEST(GurobiLpSolver, MILPOptimizeMin) { #ifdef STORM_HAVE_GUROBI - storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MINIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createIntegerVariable("z", storm::solver::LpSolver::VariableType::BOUNDED, 0, 5.7, -1)); - - ASSERT_NO_THROW(solver.update()); + storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Minimize); + ASSERT_NO_THROW(solver.addBinaryVariable("x", -1)); + ASSERT_NO_THROW(solver.addLowerBoundedIntegerVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("z", 0, 5, -1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - ASSERT_NO_THROW(solver.optimize()); ASSERT_TRUE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); bool xValue = false; - ASSERT_NO_THROW(xValue = solver.getBinaryValue(xIndex)); + ASSERT_NO_THROW(xValue = solver.getBinaryValue("x")); ASSERT_EQ(true, xValue); int_fast64_t yValue = 0; - ASSERT_NO_THROW(yValue = solver.getIntegerValue(yIndex)); + ASSERT_NO_THROW(yValue = solver.getIntegerValue("y")); ASSERT_EQ(0, yValue); double zValue = 0; - ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex)); + ASSERT_NO_THROW(zValue = solver.getContinuousValue("z")); ASSERT_LT(std::abs(zValue - 5), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double objectiveValue = 0; ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue()); @@ -155,33 +144,30 @@ TEST(GurobiLpSolver, MILPOptimizeMin) { TEST(GurobiLpSolver, LPInfeasible) { #ifdef STORM_HAVE_GUROBI - storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.update()); + storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") > storm::expressions::Expression::createDoubleLiteral(7))); + ASSERT_NO_THROW(solver.update()); - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex}, {1}, storm::solver::LpSolver::BoundType::GREATER_EQUAL, 7)); - ASSERT_NO_THROW(solver.optimize()); ASSERT_FALSE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_TRUE(solver.isInfeasible()); double xValue = 0; - ASSERT_THROW(xValue = solver.getContinuousValue(xIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(xValue = solver.getContinuousValue("x"), storm::exceptions::InvalidAccessException); double yValue = 0; - ASSERT_THROW(yValue = solver.getContinuousValue(yIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(yValue = solver.getContinuousValue("y"), storm::exceptions::InvalidAccessException); double zValue = 0; - ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException); double objectiveValue = 0; - ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException); + ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException); #else ASSERT_TRUE(false) << "StoRM built without Gurobi support."; #endif @@ -189,33 +175,30 @@ TEST(GurobiLpSolver, LPInfeasible) { TEST(GurobiLpSolver, MILPInfeasible) { #ifdef STORM_HAVE_GUROBI - storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.update()); + storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBinaryVariable("x", -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") > storm::expressions::Expression::createDoubleLiteral(7))); + ASSERT_NO_THROW(solver.update()); - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex}, {1}, storm::solver::LpSolver::BoundType::GREATER_EQUAL, 7)); - ASSERT_NO_THROW(solver.optimize()); ASSERT_FALSE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_TRUE(solver.isInfeasible()); bool xValue = false; - ASSERT_THROW(xValue = solver.getBinaryValue(xIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(xValue = solver.getBinaryValue("x"), storm::exceptions::InvalidAccessException); int_fast64_t yValue = 0; - ASSERT_THROW(yValue = solver.getIntegerValue(yIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(yValue = solver.getIntegerValue("y"), storm::exceptions::InvalidAccessException); double zValue = 0; - ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException); double objectiveValue = 0; - ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException); + ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException); #else ASSERT_TRUE(false) << "StoRM built without Gurobi support."; #endif @@ -223,31 +206,28 @@ TEST(GurobiLpSolver, MILPInfeasible) { TEST(GurobiLpSolver, LPUnbounded) { #ifdef STORM_HAVE_GUROBI - storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.update()); + storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - ASSERT_NO_THROW(solver.optimize()); ASSERT_FALSE(solver.isOptimal()); ASSERT_TRUE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); double xValue = 0; - ASSERT_THROW(xValue = solver.getContinuousValue(xIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(xValue = solver.getContinuousValue("x"), storm::exceptions::InvalidAccessException); double yValue = 0; - ASSERT_THROW(yValue = solver.getContinuousValue(yIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(yValue = solver.getContinuousValue("y"), storm::exceptions::InvalidAccessException); double zValue = 0; - ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException); double objectiveValue = 0; - ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException); + ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException); #else ASSERT_TRUE(false) << "StoRM built without Gurobi support."; #endif @@ -255,32 +235,28 @@ TEST(GurobiLpSolver, LPUnbounded) { TEST(GurobiLpSolver, MILPUnbounded) { #ifdef STORM_HAVE_GUROBI - storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.update()); + storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBinaryVariable("x", -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); ASSERT_NO_THROW(solver.optimize()); ASSERT_FALSE(solver.isOptimal()); ASSERT_TRUE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); bool xValue = false; - ASSERT_THROW(xValue = solver.getBinaryValue(xIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(xValue = solver.getBinaryValue("x"), storm::exceptions::InvalidAccessException); int_fast64_t yValue = 0; - ASSERT_THROW(yValue = solver.getIntegerValue(yIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(yValue = solver.getIntegerValue("y"), storm::exceptions::InvalidAccessException); double zValue = 0; - ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException); double objectiveValue = 0; - ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException); + ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException); #else ASSERT_TRUE(false) << "StoRM built without Gurobi support."; #endif -} \ No newline at end of file +} diff --git a/test/functional/storage/CuddDdTest.cpp b/test/functional/storage/CuddDdTest.cpp new file mode 100644 index 000000000..e95348625 --- /dev/null +++ b/test/functional/storage/CuddDdTest.cpp @@ -0,0 +1,423 @@ +#include "gtest/gtest.h" +#include "storm-config.h" +#include "src/exceptions/InvalidArgumentException.h" +#include "src/storage/dd/CuddDdManager.h" +#include "src/storage/dd/CuddDd.h" +#include "src/storage/dd/CuddOdd.h" +#include "src/storage/dd/DdMetaVariable.h" + +TEST(CuddDdManager, Constants) { + std::shared_ptr<storm::dd::DdManager<storm::dd::DdType::CUDD>> manager(new storm::dd::DdManager<storm::dd::DdType::CUDD>()); + storm::dd::Dd<storm::dd::DdType::CUDD> zero; + ASSERT_NO_THROW(zero = manager->getZero()); + + EXPECT_EQ(0, zero.getNonZeroCount()); + EXPECT_EQ(1, zero.getLeafCount()); + EXPECT_EQ(1, zero.getNodeCount()); + EXPECT_EQ(0, zero.getMin()); + EXPECT_EQ(0, zero.getMax()); + + storm::dd::Dd<storm::dd::DdType::CUDD> one; + ASSERT_NO_THROW(one = manager->getOne()); + + EXPECT_EQ(1, one.getNonZeroCount()); + EXPECT_EQ(1, one.getLeafCount()); + EXPECT_EQ(1, one.getNodeCount()); + EXPECT_EQ(1, one.getMin()); + EXPECT_EQ(1, one.getMax()); + + storm::dd::Dd<storm::dd::DdType::CUDD> two; + ASSERT_NO_THROW(two = manager->getConstant(2)); + + EXPECT_EQ(1, two.getNonZeroCount()); + EXPECT_EQ(1, two.getLeafCount()); + EXPECT_EQ(1, two.getNodeCount()); + EXPECT_EQ(2, two.getMin()); + EXPECT_EQ(2, two.getMax()); +} + +TEST(CuddDdManager, AddGetMetaVariableTest) { + std::shared_ptr<storm::dd::DdManager<storm::dd::DdType::CUDD>> manager(new storm::dd::DdManager<storm::dd::DdType::CUDD>()); + ASSERT_NO_THROW(manager->addMetaVariable("x", 1, 9)); + EXPECT_EQ(2, manager->getNumberOfMetaVariables()); + + ASSERT_THROW(manager->addMetaVariable("x", 0, 3), storm::exceptions::InvalidArgumentException); + + ASSERT_NO_THROW(manager->addMetaVariable("y", 0, 3)); + EXPECT_EQ(4, manager->getNumberOfMetaVariables()); + + EXPECT_TRUE(manager->hasMetaVariable("x'")); + EXPECT_TRUE(manager->hasMetaVariable("y'")); + + std::set<std::string> metaVariableSet = {"x", "x'", "y", "y'"}; + EXPECT_EQ(metaVariableSet, manager->getAllMetaVariableNames()); +} + +TEST(CuddDdManager, EncodingTest) { + std::shared_ptr<storm::dd::DdManager<storm::dd::DdType::CUDD>> manager(new storm::dd::DdManager<storm::dd::DdType::CUDD>()); + manager->addMetaVariable("x", 1, 9); + + storm::dd::Dd<storm::dd::DdType::CUDD> encoding; + ASSERT_THROW(encoding = manager->getEncoding("x", 0), storm::exceptions::InvalidArgumentException); + ASSERT_THROW(encoding = manager->getEncoding("x", 10), storm::exceptions::InvalidArgumentException); + ASSERT_NO_THROW(encoding = manager->getEncoding("x", 4)); + EXPECT_EQ(1, encoding.getNonZeroCount()); + EXPECT_EQ(6, encoding.getNodeCount()); + EXPECT_EQ(2, encoding.getLeafCount()); +} + +TEST(CuddDdManager, RangeTest) { + std::shared_ptr<storm::dd::DdManager<storm::dd::DdType::CUDD>> manager(new storm::dd::DdManager<storm::dd::DdType::CUDD>()); + ASSERT_NO_THROW(manager->addMetaVariable("x", 1, 9)); + + storm::dd::Dd<storm::dd::DdType::CUDD> range; + ASSERT_THROW(range = manager->getRange("y"), storm::exceptions::InvalidArgumentException); + ASSERT_NO_THROW(range = manager->getRange("x")); + + EXPECT_EQ(9, range.getNonZeroCount()); + EXPECT_EQ(2, range.getLeafCount()); + EXPECT_EQ(6, range.getNodeCount()); +} + +TEST(CuddDdManager, IdentityTest) { + std::shared_ptr<storm::dd::DdManager<storm::dd::DdType::CUDD>> manager(new storm::dd::DdManager<storm::dd::DdType::CUDD>()); + manager->addMetaVariable("x", 1, 9); + + storm::dd::Dd<storm::dd::DdType::CUDD> range; + ASSERT_THROW(range = manager->getIdentity("y"), storm::exceptions::InvalidArgumentException); + ASSERT_NO_THROW(range = manager->getIdentity("x")); + + EXPECT_EQ(9, range.getNonZeroCount()); + EXPECT_EQ(10, range.getLeafCount()); + EXPECT_EQ(21, range.getNodeCount()); +} + +TEST(CuddDd, OperatorTest) { + std::shared_ptr<storm::dd::DdManager<storm::dd::DdType::CUDD>> manager(new storm::dd::DdManager<storm::dd::DdType::CUDD>()); + manager->addMetaVariable("x", 1, 9); + EXPECT_TRUE(manager->getZero() == manager->getZero()); + EXPECT_FALSE(manager->getZero() == manager->getOne()); + + EXPECT_FALSE(manager->getZero() != manager->getZero()); + EXPECT_TRUE(manager->getZero() != manager->getOne()); + + storm::dd::Dd<storm::dd::DdType::CUDD> dd1 = manager->getOne(); + storm::dd::Dd<storm::dd::DdType::CUDD> dd2 = manager->getOne(); + storm::dd::Dd<storm::dd::DdType::CUDD> dd3 = dd1 + dd2; + EXPECT_TRUE(dd3 == manager->getConstant(2)); + + dd3 += manager->getZero(); + EXPECT_TRUE(dd3 == manager->getConstant(2)); + + dd3 = dd1 && manager->getConstant(3); + EXPECT_TRUE(dd1 == manager->getOne()); + + dd3 = dd1 * manager->getConstant(3); + EXPECT_TRUE(dd3 == manager->getConstant(3)); + + dd3 *= manager->getConstant(2); + EXPECT_TRUE(dd3 == manager->getConstant(6)); + + dd3 = dd1 - dd2; + EXPECT_TRUE(dd3 == manager->getZero()); + + dd3 -= manager->getConstant(-2); + EXPECT_TRUE(dd3 == manager->getConstant(2)); + + dd3 /= manager->getConstant(2); + EXPECT_TRUE(dd3 == manager->getOne()); + + dd3.complement(); + EXPECT_TRUE(dd3 == manager->getZero()); + + dd1 = !dd3; + EXPECT_TRUE(dd1 == manager->getOne()); + + dd3 = dd1 || dd2; + EXPECT_TRUE(dd3 == manager->getOne()); + + dd1 = manager->getIdentity("x"); + dd2 = manager->getConstant(5); + + dd3 = dd1.equals(dd2); + EXPECT_EQ(1, dd3.getNonZeroCount()); + + storm::dd::Dd<storm::dd::DdType::CUDD> dd4 = dd1.notEquals(dd2); + EXPECT_TRUE(dd4 == !dd3); + + dd3 = dd1.less(dd2); + EXPECT_EQ(11, dd3.getNonZeroCount()); + + dd3 = dd1.lessOrEqual(dd2); + EXPECT_EQ(12, dd3.getNonZeroCount()); + + dd3 = dd1.greater(dd2); + EXPECT_EQ(4, dd3.getNonZeroCount()); + + dd3 = dd1.greaterOrEqual(dd2); + EXPECT_EQ(5, dd3.getNonZeroCount()); + + dd3 = (manager->getEncoding("x", 2)).ite(dd2, dd1); + dd4 = dd3.less(dd2); + EXPECT_EQ(10, dd4.getNonZeroCount()); + + dd4 = dd3.minimum(dd1); + dd4 *= manager->getEncoding("x", 2); + dd4 = dd4.sumAbstract({"x"}); + EXPECT_EQ(2, dd4.getValue()); + + dd4 = dd3.maximum(dd1); + dd4 *= manager->getEncoding("x", 2); + dd4 = dd4.sumAbstract({"x"}); + EXPECT_EQ(5, dd4.getValue()); + + dd1 = manager->getConstant(0.01); + dd2 = manager->getConstant(0.01 + 1e-6); + EXPECT_TRUE(dd1.equalModuloPrecision(dd2, 1e-6, false)); + EXPECT_FALSE(dd1.equalModuloPrecision(dd2, 1e-6)); +} + +TEST(CuddDd, AbstractionTest) { + std::shared_ptr<storm::dd::DdManager<storm::dd::DdType::CUDD>> manager(new storm::dd::DdManager<storm::dd::DdType::CUDD>()); + manager->addMetaVariable("x", 1, 9); + storm::dd::Dd<storm::dd::DdType::CUDD> dd1; + storm::dd::Dd<storm::dd::DdType::CUDD> dd2; + storm::dd::Dd<storm::dd::DdType::CUDD> dd3; + + dd1 = manager->getIdentity("x"); + dd2 = manager->getConstant(5); + dd3 = dd1.equals(dd2); + EXPECT_EQ(1, dd3.getNonZeroCount()); + ASSERT_THROW(dd3 = dd3.existsAbstract({"x'"}), storm::exceptions::InvalidArgumentException); + ASSERT_NO_THROW(dd3 = dd3.existsAbstract({"x"})); + EXPECT_EQ(1, dd3.getNonZeroCount()); + EXPECT_EQ(1, dd3.getMax()); + + dd3 = dd1.equals(dd2); + dd3 *= manager->getConstant(3); + EXPECT_EQ(1, dd3.getNonZeroCount()); + ASSERT_THROW(dd3 = dd3.existsAbstract({"x'"}), storm::exceptions::InvalidArgumentException); + ASSERT_NO_THROW(dd3 = dd3.existsAbstract({"x"})); + EXPECT_TRUE(dd3 == manager->getZero()); + + dd3 = dd1.equals(dd2); + dd3 *= manager->getConstant(3); + ASSERT_THROW(dd3 = dd3.sumAbstract({"x'"}), storm::exceptions::InvalidArgumentException); + ASSERT_NO_THROW(dd3 = dd3.sumAbstract({"x"})); + EXPECT_EQ(1, dd3.getNonZeroCount()); + EXPECT_EQ(3, dd3.getMax()); + + dd3 = dd1.equals(dd2); + dd3 *= manager->getConstant(3); + ASSERT_THROW(dd3 = dd3.minAbstract({"x'"}), storm::exceptions::InvalidArgumentException); + ASSERT_NO_THROW(dd3 = dd3.minAbstract({"x"})); + EXPECT_EQ(0, dd3.getNonZeroCount()); + EXPECT_EQ(0, dd3.getMax()); + + dd3 = dd1.equals(dd2); + dd3 *= manager->getConstant(3); + ASSERT_THROW(dd3 = dd3.maxAbstract({"x'"}), storm::exceptions::InvalidArgumentException); + ASSERT_NO_THROW(dd3 = dd3.maxAbstract({"x"})); + EXPECT_EQ(1, dd3.getNonZeroCount()); + EXPECT_EQ(3, dd3.getMax()); +} + +TEST(CuddDd, SwapTest) { + std::shared_ptr<storm::dd::DdManager<storm::dd::DdType::CUDD>> manager(new storm::dd::DdManager<storm::dd::DdType::CUDD>()); + + manager->addMetaVariable("x", 1, 9); + manager->addMetaVariable("z", 2, 8); + storm::dd::Dd<storm::dd::DdType::CUDD> dd1; + storm::dd::Dd<storm::dd::DdType::CUDD> dd2; + + dd1 = manager->getIdentity("x"); + ASSERT_THROW(dd1.swapVariables({std::make_pair("x", "z")}), storm::exceptions::InvalidArgumentException); + ASSERT_NO_THROW(dd1.swapVariables({std::make_pair("x", "x'")})); + EXPECT_TRUE(dd1 == manager->getIdentity("x'")); +} + +TEST(CuddDd, MultiplyMatrixTest) { + std::shared_ptr<storm::dd::DdManager<storm::dd::DdType::CUDD>> manager(new storm::dd::DdManager<storm::dd::DdType::CUDD>()); + manager->addMetaVariable("x", 1, 9); + + storm::dd::Dd<storm::dd::DdType::CUDD> dd1 = manager->getIdentity("x").equals(manager->getIdentity("x'")); + storm::dd::Dd<storm::dd::DdType::CUDD> dd2 = manager->getRange("x'"); + storm::dd::Dd<storm::dd::DdType::CUDD> dd3; + dd1 *= manager->getConstant(2); + + ASSERT_NO_THROW(dd3 = dd1.multiplyMatrix(dd2, {"x'"})); + ASSERT_NO_THROW(dd3.swapVariables({std::make_pair("x", "x'")})); + EXPECT_TRUE(dd3 == dd2 * manager->getConstant(2)); +} + +TEST(CuddDd, GetSetValueTest) { + std::shared_ptr<storm::dd::DdManager<storm::dd::DdType::CUDD>> manager(new storm::dd::DdManager<storm::dd::DdType::CUDD>()); + manager->addMetaVariable("x", 1, 9); + + storm::dd::Dd<storm::dd::DdType::CUDD> dd1 = manager->getOne(); + ASSERT_NO_THROW(dd1.setValue("x", 4, 2)); + EXPECT_EQ(2, dd1.getLeafCount()); + + std::map<std::string, int_fast64_t> metaVariableToValueMap; + metaVariableToValueMap.emplace("x", 1); + EXPECT_EQ(1, dd1.getValue(metaVariableToValueMap)); + + metaVariableToValueMap.clear(); + metaVariableToValueMap.emplace("x", 4); + EXPECT_EQ(2, dd1.getValue(metaVariableToValueMap)); +} + +TEST(CuddDd, ForwardIteratorTest) { + std::shared_ptr<storm::dd::DdManager<storm::dd::DdType::CUDD>> manager(new storm::dd::DdManager<storm::dd::DdType::CUDD>()); + manager->addMetaVariable("x", 1, 9); + manager->addMetaVariable("y", 0, 3); + + storm::dd::Dd<storm::dd::DdType::CUDD> dd; + ASSERT_NO_THROW(dd = manager->getRange("x")); + + storm::dd::DdForwardIterator<storm::dd::DdType::CUDD> it, ite; + ASSERT_NO_THROW(it = dd.begin()); + ASSERT_NO_THROW(ite = dd.end()); + std::pair<storm::expressions::SimpleValuation, double> valuationValuePair; + uint_fast64_t numberOfValuations = 0; + while (it != ite) { + ASSERT_NO_THROW(valuationValuePair = *it); + ASSERT_NO_THROW(++it); + ++numberOfValuations; + } + EXPECT_EQ(9, numberOfValuations); + + dd = manager->getRange("x"); + dd = dd.ite(manager->getOne(), manager->getOne()); + ASSERT_NO_THROW(it = dd.begin()); + ASSERT_NO_THROW(ite = dd.end()); + numberOfValuations = 0; + while (it != ite) { + ASSERT_NO_THROW(valuationValuePair = *it); + ASSERT_NO_THROW(++it); + ++numberOfValuations; + } + EXPECT_EQ(16, numberOfValuations); + + ASSERT_NO_THROW(it = dd.begin(false)); + ASSERT_NO_THROW(ite = dd.end()); + numberOfValuations = 0; + while (it != ite) { + ASSERT_NO_THROW(valuationValuePair = *it); + ASSERT_NO_THROW(++it); + ++numberOfValuations; + } + EXPECT_EQ(1, numberOfValuations); +} + +TEST(CuddDd, ToExpressionTest) { + std::shared_ptr<storm::dd::DdManager<storm::dd::DdType::CUDD>> manager(new storm::dd::DdManager<storm::dd::DdType::CUDD>()); + manager->addMetaVariable("x", 1, 9); + + storm::dd::Dd<storm::dd::DdType::CUDD> dd; + ASSERT_NO_THROW(dd = manager->getIdentity("x")); + + storm::expressions::Expression ddAsExpression; + ASSERT_NO_THROW(ddAsExpression = dd.toExpression()); + + storm::expressions::SimpleValuation valuation; + for (std::size_t bit = 0; bit < manager->getMetaVariable("x").getNumberOfDdVariables(); ++bit) { + valuation.addBooleanIdentifier("x." + std::to_string(bit)); + } + + storm::dd::DdMetaVariable<storm::dd::DdType::CUDD> const& metaVariable = manager->getMetaVariable("x"); + + for (auto valuationValuePair : dd) { + for (std::size_t i = 0; i < metaVariable.getNumberOfDdVariables(); ++i) { + // Check if the i-th bit is set or not and modify the valuation accordingly. + if (((valuationValuePair.first.getIntegerValue("x") - metaVariable.getLow()) & (1ull << (metaVariable.getNumberOfDdVariables() - i - 1))) != 0) { + valuation.setBooleanValue("x." + std::to_string(i), true); + } else { + valuation.setBooleanValue("x." + std::to_string(i), false); + } + } + + // At this point, the constructed valuation should make the expression obtained from the DD evaluate to the very + // same value as the current value obtained from the DD. + EXPECT_EQ(valuationValuePair.second, ddAsExpression.evaluateAsDouble(&valuation)); + } + + storm::expressions::Expression mintermExpression = dd.getMintermExpression(); + + // Check whether all minterms are covered. + for (auto valuationValuePair : dd) { + for (std::size_t i = 0; i < metaVariable.getNumberOfDdVariables(); ++i) { + // Check if the i-th bit is set or not and modify the valuation accordingly. + if (((valuationValuePair.first.getIntegerValue("x") - metaVariable.getLow()) & (1ull << (metaVariable.getNumberOfDdVariables() - i - 1))) != 0) { + valuation.setBooleanValue("x." + std::to_string(i), true); + } else { + valuation.setBooleanValue("x." + std::to_string(i), false); + } + } + + // At this point, the constructed valuation should make the expression obtained from the DD evaluate to the very + // same value as the current value obtained from the DD. + EXPECT_TRUE(mintermExpression.evaluateAsBool(&valuation)); + } + + // Now check no additional minterms are covered. + dd = !dd; + for (auto valuationValuePair : dd) { + for (std::size_t i = 0; i < metaVariable.getNumberOfDdVariables(); ++i) { + // Check if the i-th bit is set or not and modify the valuation accordingly. + if (((valuationValuePair.first.getIntegerValue("x") - metaVariable.getLow()) & (1ull << (metaVariable.getNumberOfDdVariables() - i - 1))) != 0) { + valuation.setBooleanValue("x." + std::to_string(i), true); + } else { + valuation.setBooleanValue("x." + std::to_string(i), false); + } + } + + // At this point, the constructed valuation should make the expression obtained from the DD evaluate to the very + // same value as the current value obtained from the DD. + EXPECT_FALSE(mintermExpression.evaluateAsBool(&valuation)); + } +} + +TEST(CuddDd, OddTest) { + std::shared_ptr<storm::dd::DdManager<storm::dd::DdType::CUDD>> manager(new storm::dd::DdManager<storm::dd::DdType::CUDD>()); + manager->addMetaVariable("a"); + manager->addMetaVariable("x", 1, 9); + + storm::dd::Dd<storm::dd::DdType::CUDD> dd = manager->getIdentity("x"); + storm::dd::Odd<storm::dd::DdType::CUDD> odd; + ASSERT_NO_THROW(odd = storm::dd::Odd<storm::dd::DdType::CUDD>(dd)); + EXPECT_EQ(9, odd.getTotalOffset()); + EXPECT_EQ(12, odd.getNodeCount()); + + std::vector<double> ddAsVector; + ASSERT_NO_THROW(ddAsVector = dd.toVector<double>()); + EXPECT_EQ(9, ddAsVector.size()); + for (uint_fast64_t i = 0; i < ddAsVector.size(); ++i) { + EXPECT_TRUE(i+1 == ddAsVector[i]); + } + + // Create a non-trivial matrix. + dd = manager->getIdentity("x").equals(manager->getIdentity("x'")) * manager->getRange("x"); + dd += manager->getEncoding("x", 1) * manager->getRange("x'") + manager->getEncoding("x'", 1) * manager->getRange("x"); + + // Create the ODDs. + storm::dd::Odd<storm::dd::DdType::CUDD> rowOdd; + ASSERT_NO_THROW(rowOdd = storm::dd::Odd<storm::dd::DdType::CUDD>(manager->getRange("x"))); + storm::dd::Odd<storm::dd::DdType::CUDD> columnOdd; + ASSERT_NO_THROW(columnOdd = storm::dd::Odd<storm::dd::DdType::CUDD>(manager->getRange("x'"))); + + // Try to translate the matrix. + storm::storage::SparseMatrix<double> matrix; + ASSERT_NO_THROW(matrix = dd.toMatrix({"x"}, {"x'"}, rowOdd, columnOdd)); + + EXPECT_EQ(9, matrix.getRowCount()); + EXPECT_EQ(9, matrix.getColumnCount()); + EXPECT_EQ(25, matrix.getNonzeroEntryCount()); + + dd = manager->getRange("x") * manager->getRange("x'") * manager->getEncoding("a", 0).ite(dd, dd + manager->getConstant(1)); + ASSERT_NO_THROW(matrix = dd.toMatrix({"x"}, {"x'"}, {"a"}, rowOdd, columnOdd)); + EXPECT_EQ(18, matrix.getRowCount()); + EXPECT_EQ(9, matrix.getRowGroupCount()); + EXPECT_EQ(9, matrix.getColumnCount()); + EXPECT_EQ(106, matrix.getNonzeroEntryCount()); +} \ No newline at end of file diff --git a/test/functional/storage/ExpressionTest.cpp b/test/functional/storage/ExpressionTest.cpp new file mode 100644 index 000000000..642456995 --- /dev/null +++ b/test/functional/storage/ExpressionTest.cpp @@ -0,0 +1,358 @@ +#include <map> +#include <string> + +#include "gtest/gtest.h" +#include "src/storage/expressions/Expression.h" +#include "src/storage/expressions/LinearityCheckVisitor.h" +#include "src/storage/expressions/SimpleValuation.h" +#include "src/exceptions/InvalidTypeException.h" + +TEST(Expression, FactoryMethodTest) { + EXPECT_NO_THROW(storm::expressions::Expression::createBooleanLiteral(true)); + EXPECT_NO_THROW(storm::expressions::Expression::createTrue()); + EXPECT_NO_THROW(storm::expressions::Expression::createFalse()); + EXPECT_NO_THROW(storm::expressions::Expression::createIntegerLiteral(3)); + EXPECT_NO_THROW(storm::expressions::Expression::createDoubleLiteral(3.14)); + EXPECT_NO_THROW(storm::expressions::Expression::createBooleanVariable("x")); + EXPECT_NO_THROW(storm::expressions::Expression::createIntegerVariable("y")); + EXPECT_NO_THROW(storm::expressions::Expression::createDoubleVariable("z")); +} + +TEST(Expression, AccessorTest) { + storm::expressions::Expression trueExpression; + storm::expressions::Expression falseExpression; + storm::expressions::Expression threeExpression; + storm::expressions::Expression piExpression; + storm::expressions::Expression boolVarExpression; + storm::expressions::Expression intVarExpression; + storm::expressions::Expression doubleVarExpression; + + ASSERT_NO_THROW(trueExpression = storm::expressions::Expression::createTrue()); + ASSERT_NO_THROW(falseExpression = storm::expressions::Expression::createFalse()); + ASSERT_NO_THROW(threeExpression = storm::expressions::Expression::createIntegerLiteral(3)); + ASSERT_NO_THROW(piExpression = storm::expressions::Expression::createDoubleLiteral(3.14)); + ASSERT_NO_THROW(boolVarExpression = storm::expressions::Expression::createBooleanVariable("x")); + ASSERT_NO_THROW(intVarExpression = storm::expressions::Expression::createIntegerVariable("y")); + ASSERT_NO_THROW(doubleVarExpression = storm::expressions::Expression::createDoubleVariable("z")); + + EXPECT_TRUE(trueExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + EXPECT_TRUE(trueExpression.isLiteral()); + EXPECT_TRUE(trueExpression.isTrue()); + EXPECT_FALSE(trueExpression.isFalse()); + EXPECT_TRUE(trueExpression.getVariables() == std::set<std::string>()); + + EXPECT_TRUE(falseExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + EXPECT_TRUE(falseExpression.isLiteral()); + EXPECT_FALSE(falseExpression.isTrue()); + EXPECT_TRUE(falseExpression.isFalse()); + EXPECT_TRUE(falseExpression.getVariables() == std::set<std::string>()); + + EXPECT_TRUE(threeExpression.getReturnType() == storm::expressions::ExpressionReturnType::Int); + EXPECT_TRUE(threeExpression.isLiteral()); + EXPECT_FALSE(threeExpression.isTrue()); + EXPECT_FALSE(threeExpression.isFalse()); + EXPECT_TRUE(threeExpression.getVariables() == std::set<std::string>()); + + EXPECT_TRUE(piExpression.getReturnType() == storm::expressions::ExpressionReturnType::Double); + EXPECT_TRUE(piExpression.isLiteral()); + EXPECT_FALSE(piExpression.isTrue()); + EXPECT_FALSE(piExpression.isFalse()); + EXPECT_TRUE(piExpression.getVariables() == std::set<std::string>()); + + EXPECT_TRUE(boolVarExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + EXPECT_FALSE(boolVarExpression.isLiteral()); + EXPECT_FALSE(boolVarExpression.isTrue()); + EXPECT_FALSE(boolVarExpression.isFalse()); + EXPECT_TRUE(boolVarExpression.getVariables() == std::set<std::string>({"x"})); + + EXPECT_TRUE(intVarExpression.getReturnType() == storm::expressions::ExpressionReturnType::Int); + EXPECT_FALSE(intVarExpression.isLiteral()); + EXPECT_FALSE(intVarExpression.isTrue()); + EXPECT_FALSE(intVarExpression.isFalse()); + EXPECT_TRUE(intVarExpression.getVariables() == std::set<std::string>({"y"})); + + EXPECT_TRUE(doubleVarExpression.getReturnType() == storm::expressions::ExpressionReturnType::Double); + EXPECT_FALSE(doubleVarExpression.isLiteral()); + EXPECT_FALSE(doubleVarExpression.isTrue()); + EXPECT_FALSE(doubleVarExpression.isFalse()); + EXPECT_TRUE(doubleVarExpression.getVariables() == std::set<std::string>({"z"})); +} + +TEST(Expression, OperatorTest) { + storm::expressions::Expression trueExpression; + storm::expressions::Expression falseExpression; + storm::expressions::Expression threeExpression; + storm::expressions::Expression piExpression; + storm::expressions::Expression boolVarExpression; + storm::expressions::Expression intVarExpression; + storm::expressions::Expression doubleVarExpression; + + ASSERT_NO_THROW(trueExpression = storm::expressions::Expression::createTrue()); + ASSERT_NO_THROW(falseExpression = storm::expressions::Expression::createFalse()); + ASSERT_NO_THROW(threeExpression = storm::expressions::Expression::createIntegerLiteral(3)); + ASSERT_NO_THROW(piExpression = storm::expressions::Expression::createDoubleLiteral(3.14)); + ASSERT_NO_THROW(boolVarExpression = storm::expressions::Expression::createBooleanVariable("x")); + ASSERT_NO_THROW(intVarExpression = storm::expressions::Expression::createIntegerVariable("y")); + ASSERT_NO_THROW(doubleVarExpression = storm::expressions::Expression::createDoubleVariable("z")); + + storm::expressions::Expression tempExpression; + + ASSERT_THROW(tempExpression = trueExpression.ite(falseExpression, piExpression), storm::exceptions::InvalidTypeException); + ASSERT_NO_THROW(tempExpression = boolVarExpression.ite(threeExpression, doubleVarExpression)); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Double); + ASSERT_NO_THROW(tempExpression = boolVarExpression.ite(threeExpression, intVarExpression)); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Int); + ASSERT_NO_THROW(tempExpression = boolVarExpression.ite(trueExpression, falseExpression)); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + + ASSERT_THROW(tempExpression = trueExpression + piExpression, storm::exceptions::InvalidTypeException); + ASSERT_NO_THROW(tempExpression = threeExpression + threeExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Int); + ASSERT_NO_THROW(tempExpression = threeExpression + piExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Double); + ASSERT_NO_THROW(tempExpression = doubleVarExpression + doubleVarExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Double); + + ASSERT_THROW(tempExpression = trueExpression - piExpression, storm::exceptions::InvalidTypeException); + ASSERT_NO_THROW(tempExpression = threeExpression - threeExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Int); + ASSERT_NO_THROW(tempExpression = threeExpression - piExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Double); + ASSERT_NO_THROW(tempExpression = doubleVarExpression - doubleVarExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Double); + + ASSERT_THROW(tempExpression = -trueExpression, storm::exceptions::InvalidTypeException); + ASSERT_NO_THROW(tempExpression = -threeExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Int); + ASSERT_NO_THROW(tempExpression = -piExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Double); + ASSERT_NO_THROW(tempExpression = -doubleVarExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Double); + + ASSERT_THROW(tempExpression = trueExpression * piExpression, storm::exceptions::InvalidTypeException); + ASSERT_NO_THROW(tempExpression = threeExpression * threeExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Int); + ASSERT_NO_THROW(tempExpression = threeExpression * piExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Double); + ASSERT_NO_THROW(tempExpression = intVarExpression * intVarExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Int); + + ASSERT_THROW(tempExpression = trueExpression / piExpression, storm::exceptions::InvalidTypeException); + ASSERT_NO_THROW(tempExpression = threeExpression / threeExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Int); + ASSERT_NO_THROW(tempExpression = threeExpression / piExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Double); + ASSERT_NO_THROW(tempExpression = doubleVarExpression / intVarExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Double); + + ASSERT_THROW(tempExpression = trueExpression && piExpression, storm::exceptions::InvalidTypeException); + ASSERT_NO_THROW(tempExpression = trueExpression && falseExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + ASSERT_NO_THROW(tempExpression = boolVarExpression && boolVarExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + + ASSERT_THROW(tempExpression = trueExpression || piExpression, storm::exceptions::InvalidTypeException); + ASSERT_NO_THROW(tempExpression = trueExpression || falseExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + ASSERT_NO_THROW(tempExpression = boolVarExpression || boolVarExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + + ASSERT_THROW(tempExpression = !threeExpression, storm::exceptions::InvalidTypeException); + ASSERT_NO_THROW(tempExpression = !trueExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + ASSERT_NO_THROW(tempExpression = !boolVarExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + + ASSERT_THROW(tempExpression = trueExpression == piExpression, storm::exceptions::InvalidTypeException); + ASSERT_NO_THROW(tempExpression = threeExpression == threeExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + ASSERT_NO_THROW(tempExpression = intVarExpression == doubleVarExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + + ASSERT_THROW(tempExpression = trueExpression != piExpression, storm::exceptions::InvalidTypeException); + ASSERT_NO_THROW(tempExpression = threeExpression != threeExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + ASSERT_NO_THROW(tempExpression = intVarExpression != doubleVarExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + + ASSERT_THROW(tempExpression = trueExpression > piExpression, storm::exceptions::InvalidTypeException); + ASSERT_NO_THROW(tempExpression = threeExpression > threeExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + ASSERT_NO_THROW(tempExpression = intVarExpression > doubleVarExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + + ASSERT_THROW(tempExpression = trueExpression >= piExpression, storm::exceptions::InvalidTypeException); + ASSERT_NO_THROW(tempExpression = threeExpression >= threeExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + ASSERT_NO_THROW(tempExpression = intVarExpression >= doubleVarExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + + ASSERT_THROW(tempExpression = trueExpression < piExpression, storm::exceptions::InvalidTypeException); + ASSERT_NO_THROW(tempExpression = threeExpression < threeExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + ASSERT_NO_THROW(tempExpression = intVarExpression < doubleVarExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + + ASSERT_THROW(tempExpression = trueExpression <= piExpression, storm::exceptions::InvalidTypeException); + ASSERT_NO_THROW(tempExpression = threeExpression <= threeExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + ASSERT_NO_THROW(tempExpression = intVarExpression <= doubleVarExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + + ASSERT_THROW(tempExpression = storm::expressions::Expression::minimum(trueExpression, piExpression), storm::exceptions::InvalidTypeException); + ASSERT_NO_THROW(tempExpression = storm::expressions::Expression::minimum(threeExpression, threeExpression)); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Int); + ASSERT_NO_THROW(tempExpression = storm::expressions::Expression::minimum(intVarExpression, doubleVarExpression)); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Double); + + ASSERT_THROW(tempExpression = storm::expressions::Expression::maximum(trueExpression, piExpression), storm::exceptions::InvalidTypeException); + ASSERT_NO_THROW(tempExpression = storm::expressions::Expression::maximum(threeExpression, threeExpression)); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Int); + ASSERT_NO_THROW(tempExpression = storm::expressions::Expression::maximum(intVarExpression, doubleVarExpression)); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Double); + + ASSERT_THROW(tempExpression = trueExpression.implies(piExpression), storm::exceptions::InvalidTypeException); + ASSERT_NO_THROW(tempExpression = trueExpression.implies(falseExpression)); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + ASSERT_NO_THROW(tempExpression = boolVarExpression.implies(boolVarExpression)); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + + ASSERT_THROW(tempExpression = trueExpression.iff(piExpression), storm::exceptions::InvalidTypeException); + ASSERT_NO_THROW(tempExpression = trueExpression.iff(falseExpression)); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + ASSERT_NO_THROW(tempExpression = boolVarExpression.iff(boolVarExpression)); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + + ASSERT_THROW(tempExpression = trueExpression != piExpression, storm::exceptions::InvalidTypeException); + ASSERT_NO_THROW(tempExpression = trueExpression != falseExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + ASSERT_NO_THROW(tempExpression = boolVarExpression != boolVarExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Bool); + + ASSERT_THROW(tempExpression = trueExpression.floor(), storm::exceptions::InvalidTypeException); + ASSERT_NO_THROW(tempExpression = threeExpression.floor()); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Int); + ASSERT_NO_THROW(tempExpression = doubleVarExpression.floor()); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Int); + + ASSERT_THROW(tempExpression = trueExpression.ceil(), storm::exceptions::InvalidTypeException); + ASSERT_NO_THROW(tempExpression = threeExpression.ceil()); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Int); + ASSERT_NO_THROW(tempExpression = doubleVarExpression.ceil()); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Int); + + ASSERT_THROW(tempExpression = trueExpression ^ piExpression, storm::exceptions::InvalidTypeException); + ASSERT_NO_THROW(tempExpression = threeExpression ^ threeExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Int); + ASSERT_NO_THROW(tempExpression = intVarExpression ^ doubleVarExpression); + EXPECT_TRUE(tempExpression.getReturnType() == storm::expressions::ExpressionReturnType::Double); +} + +TEST(Expression, SubstitutionTest) { + storm::expressions::Expression trueExpression; + storm::expressions::Expression falseExpression; + storm::expressions::Expression threeExpression; + storm::expressions::Expression piExpression; + storm::expressions::Expression boolVarExpression; + storm::expressions::Expression intVarExpression; + storm::expressions::Expression doubleVarExpression; + + ASSERT_NO_THROW(trueExpression = storm::expressions::Expression::createTrue()); + ASSERT_NO_THROW(falseExpression = storm::expressions::Expression::createFalse()); + ASSERT_NO_THROW(threeExpression = storm::expressions::Expression::createIntegerLiteral(3)); + ASSERT_NO_THROW(piExpression = storm::expressions::Expression::createDoubleLiteral(3.14)); + ASSERT_NO_THROW(boolVarExpression = storm::expressions::Expression::createBooleanVariable("x")); + ASSERT_NO_THROW(intVarExpression = storm::expressions::Expression::createIntegerVariable("y")); + ASSERT_NO_THROW(doubleVarExpression = storm::expressions::Expression::createDoubleVariable("z")); + + storm::expressions::Expression tempExpression; + ASSERT_NO_THROW(tempExpression = (intVarExpression < threeExpression || boolVarExpression) && boolVarExpression); + + std::map<std::string, storm::expressions::Expression> substution = { std::make_pair("y", doubleVarExpression), std::make_pair("x", storm::expressions::Expression::createTrue()), std::make_pair("a", storm::expressions::Expression::createTrue()) }; + storm::expressions::Expression substitutedExpression; + ASSERT_NO_THROW(substitutedExpression = tempExpression.substitute(substution)); + EXPECT_TRUE(substitutedExpression.simplify().isTrue()); +} + +TEST(Expression, SimplificationTest) { + storm::expressions::Expression trueExpression; + storm::expressions::Expression falseExpression; + storm::expressions::Expression threeExpression; + storm::expressions::Expression intVarExpression; + + ASSERT_NO_THROW(trueExpression = storm::expressions::Expression::createTrue()); + ASSERT_NO_THROW(falseExpression = storm::expressions::Expression::createFalse()); + ASSERT_NO_THROW(threeExpression = storm::expressions::Expression::createIntegerLiteral(3)); + ASSERT_NO_THROW(intVarExpression = storm::expressions::Expression::createIntegerVariable("y")); + + storm::expressions::Expression tempExpression; + storm::expressions::Expression simplifiedExpression; + + ASSERT_NO_THROW(tempExpression = trueExpression || intVarExpression > threeExpression); + ASSERT_NO_THROW(simplifiedExpression = tempExpression.simplify()); + EXPECT_TRUE(simplifiedExpression.isTrue()); + + ASSERT_NO_THROW(tempExpression = falseExpression && intVarExpression > threeExpression); + ASSERT_NO_THROW(simplifiedExpression = tempExpression.simplify()); + EXPECT_TRUE(simplifiedExpression.isFalse()); +} + +TEST(Expression, SimpleEvaluationTest) { + storm::expressions::Expression trueExpression; + storm::expressions::Expression falseExpression; + storm::expressions::Expression threeExpression; + storm::expressions::Expression piExpression; + storm::expressions::Expression boolVarExpression; + storm::expressions::Expression intVarExpression; + storm::expressions::Expression doubleVarExpression; + + ASSERT_NO_THROW(trueExpression = storm::expressions::Expression::createTrue()); + ASSERT_NO_THROW(falseExpression = storm::expressions::Expression::createFalse()); + ASSERT_NO_THROW(threeExpression = storm::expressions::Expression::createIntegerLiteral(3)); + ASSERT_NO_THROW(piExpression = storm::expressions::Expression::createDoubleLiteral(3.14)); + ASSERT_NO_THROW(boolVarExpression = storm::expressions::Expression::createBooleanVariable("x")); + ASSERT_NO_THROW(intVarExpression = storm::expressions::Expression::createIntegerVariable("y")); + ASSERT_NO_THROW(doubleVarExpression = storm::expressions::Expression::createDoubleVariable("z")); + + storm::expressions::Expression tempExpression; + + ASSERT_NO_THROW(tempExpression = (intVarExpression < threeExpression || boolVarExpression) && boolVarExpression); + + ASSERT_NO_THROW(storm::expressions::SimpleValuation valuation); + storm::expressions::SimpleValuation valuation; + ASSERT_NO_THROW(valuation.addBooleanIdentifier("x")); + ASSERT_NO_THROW(valuation.addBooleanIdentifier("a")); + ASSERT_NO_THROW(valuation.addIntegerIdentifier("y")); + ASSERT_NO_THROW(valuation.addIntegerIdentifier("b")); + ASSERT_NO_THROW(valuation.addDoubleIdentifier("z")); + ASSERT_NO_THROW(valuation.addDoubleIdentifier("c")); + + ASSERT_THROW(tempExpression.evaluateAsDouble(&valuation), storm::exceptions::InvalidTypeException); + ASSERT_THROW(tempExpression.evaluateAsInt(&valuation), storm::exceptions::InvalidTypeException); + EXPECT_FALSE(tempExpression.evaluateAsBool(&valuation)); + ASSERT_NO_THROW(valuation.setBooleanValue("a", true)); + EXPECT_FALSE(tempExpression.evaluateAsBool(&valuation)); + ASSERT_NO_THROW(valuation.setIntegerValue("y", 3)); + EXPECT_FALSE(tempExpression.evaluateAsBool(&valuation)); + + ASSERT_NO_THROW(tempExpression = ((intVarExpression < threeExpression).ite(trueExpression, falseExpression))); + ASSERT_THROW(tempExpression.evaluateAsDouble(&valuation), storm::exceptions::InvalidTypeException); + ASSERT_THROW(tempExpression.evaluateAsInt(&valuation), storm::exceptions::InvalidTypeException); + EXPECT_FALSE(tempExpression.evaluateAsBool(&valuation)); +} + +TEST(Expression, VisitorTest) { + storm::expressions::Expression threeExpression; + storm::expressions::Expression piExpression; + storm::expressions::Expression intVarExpression; + storm::expressions::Expression doubleVarExpression; + + ASSERT_NO_THROW(threeExpression = storm::expressions::Expression::createIntegerLiteral(3)); + ASSERT_NO_THROW(piExpression = storm::expressions::Expression::createDoubleLiteral(3.14)); + ASSERT_NO_THROW(intVarExpression = storm::expressions::Expression::createIntegerVariable("y")); + ASSERT_NO_THROW(doubleVarExpression = storm::expressions::Expression::createDoubleVariable("z")); + + storm::expressions::Expression tempExpression = intVarExpression + doubleVarExpression * threeExpression; + storm::expressions::LinearityCheckVisitor visitor; + EXPECT_TRUE(visitor.check(tempExpression)); +} \ No newline at end of file diff --git a/test/functional/storage/MaximalEndComponentDecompositionTest.cpp b/test/functional/storage/MaximalEndComponentDecompositionTest.cpp index a85e9ef3f..1a6112148 100644 --- a/test/functional/storage/MaximalEndComponentDecompositionTest.cpp +++ b/test/functional/storage/MaximalEndComponentDecompositionTest.cpp @@ -2,12 +2,13 @@ #include "storm-config.h" #include "src/parser/AutoParser.h" #include "src/storage/MaximalEndComponentDecomposition.h" +#include "src/models/MarkovAutomaton.h" TEST(MaximalEndComponentDecomposition, FullSystem1) { - storm::parser::AutoParser<double> parser(STORM_CPP_BASE_PATH "/examples/ma/tiny/tiny1.tra", STORM_CPP_BASE_PATH "/examples/ma/tiny/tiny1.lab", "", ""); + std::shared_ptr<storm::models::AbstractModel<double>> abstractModel = storm::parser::AutoParser::parseModel(STORM_CPP_BASE_PATH "/examples/ma/tiny/tiny1.tra", STORM_CPP_BASE_PATH "/examples/ma/tiny/tiny1.lab", "", ""); + + std::shared_ptr<storm::models::MarkovAutomaton<double>> markovAutomaton = abstractModel->as<storm::models::MarkovAutomaton<double>>(); - std::shared_ptr<storm::models::MarkovAutomaton<double>> markovAutomaton = parser.getModel<storm::models::MarkovAutomaton<double>>(); - storm::storage::MaximalEndComponentDecomposition<double> mecDecomposition; ASSERT_NO_THROW(mecDecomposition = storm::storage::MaximalEndComponentDecomposition<double>(*markovAutomaton)); @@ -72,10 +73,10 @@ TEST(MaximalEndComponentDecomposition, FullSystem1) { } TEST(MaximalEndComponentDecomposition, FullSystem2) { - storm::parser::AutoParser<double> parser(STORM_CPP_BASE_PATH "/examples/ma/tiny/tiny2.tra", STORM_CPP_BASE_PATH "/examples/ma/tiny/tiny2.lab", "", ""); + std::shared_ptr<storm::models::AbstractModel<double>> abstractModel = storm::parser::AutoParser::parseModel(STORM_CPP_BASE_PATH "/examples/ma/tiny/tiny2.tra", STORM_CPP_BASE_PATH "/examples/ma/tiny/tiny2.lab", "", ""); + + std::shared_ptr<storm::models::MarkovAutomaton<double>> markovAutomaton = abstractModel->as<storm::models::MarkovAutomaton<double>>(); - std::shared_ptr<storm::models::MarkovAutomaton<double>> markovAutomaton = parser.getModel<storm::models::MarkovAutomaton<double>>(); - storm::storage::MaximalEndComponentDecomposition<double> mecDecomposition; ASSERT_NO_THROW(mecDecomposition = storm::storage::MaximalEndComponentDecomposition<double>(*markovAutomaton)); @@ -101,9 +102,9 @@ TEST(MaximalEndComponentDecomposition, FullSystem2) { } TEST(MaximalEndComponentDecomposition, Subsystem) { - storm::parser::AutoParser<double> parser(STORM_CPP_BASE_PATH "/examples/ma/tiny/tiny1.tra", STORM_CPP_BASE_PATH "/examples/ma/tiny/tiny1.lab", "", ""); + std::shared_ptr<storm::models::AbstractModel<double>> abstractModel = storm::parser::AutoParser::parseModel(STORM_CPP_BASE_PATH "/examples/ma/tiny/tiny1.tra", STORM_CPP_BASE_PATH "/examples/ma/tiny/tiny1.lab", "", ""); - std::shared_ptr<storm::models::MarkovAutomaton<double>> markovAutomaton = parser.getModel<storm::models::MarkovAutomaton<double>>(); + std::shared_ptr<storm::models::MarkovAutomaton<double>> markovAutomaton = abstractModel->as<storm::models::MarkovAutomaton<double>>(); storm::storage::BitVector subsystem(markovAutomaton->getNumberOfStates(), true); subsystem.set(7, false); @@ -130,4 +131,4 @@ TEST(MaximalEndComponentDecomposition, Subsystem) { // This case must never happen as the only two existing MEC contains 3. ASSERT_TRUE(false); } -} \ No newline at end of file +} diff --git a/test/functional/storage/SparseMatrixTest.cpp b/test/functional/storage/SparseMatrixTest.cpp index 69517a6d4..de89f4950 100644 --- a/test/functional/storage/SparseMatrixTest.cpp +++ b/test/functional/storage/SparseMatrixTest.cpp @@ -147,7 +147,7 @@ TEST(SparseMatrix, Build) { } TEST(SparseMatrix, CreationWithMovingContents) { - std::vector<std::pair<uint_fast64_t, double>> columnsAndValues; + std::vector<storm::storage::MatrixEntry<double>> columnsAndValues; columnsAndValues.emplace_back(1, 1.0); columnsAndValues.emplace_back(2, 1.2); columnsAndValues.emplace_back(0, 0.5); @@ -540,24 +540,24 @@ TEST(SparseMatrix, Iteration) { ASSERT_NO_THROW(matrix = matrixBuilder.build()); for (auto const& entry : matrix.getRow(4)) { - if (entry.first == 0) { - ASSERT_EQ(0.1, entry.second); - } else if (entry.first == 1) { - ASSERT_EQ(0.2, entry.second); - } else if (entry.first == 3) { - ASSERT_EQ(0.3, entry.second); + if (entry.getColumn() == 0) { + ASSERT_EQ(0.1, entry.getValue()); + } else if (entry.getColumn() == 1) { + ASSERT_EQ(0.2, entry.getValue()); + } else if (entry.getColumn() == 3) { + ASSERT_EQ(0.3, entry.getValue()); } else { ASSERT_TRUE(false); } } for (storm::storage::SparseMatrix<double>::iterator it = matrix.begin(4), ite = matrix.end(4); it != ite; ++it) { - if (it->first == 0) { - ASSERT_EQ(0.1, it->second); - } else if (it->first == 1) { - ASSERT_EQ(0.2, it->second); - } else if (it->first == 3) { - ASSERT_EQ(0.3, it->second); + if (it->getColumn() == 0) { + ASSERT_EQ(0.1, it->getValue()); + } else if (it->getColumn() == 1) { + ASSERT_EQ(0.2, it->getValue()); + } else if (it->getColumn() == 3) { + ASSERT_EQ(0.3, it->getValue()); } else { ASSERT_TRUE(false); } diff --git a/test/functional/storage/StronglyConnectedComponentDecompositionTest.cpp b/test/functional/storage/StronglyConnectedComponentDecompositionTest.cpp index 0279b72fb..bc3585fc2 100644 --- a/test/functional/storage/StronglyConnectedComponentDecompositionTest.cpp +++ b/test/functional/storage/StronglyConnectedComponentDecompositionTest.cpp @@ -2,10 +2,12 @@ #include "storm-config.h" #include "src/parser/AutoParser.h" #include "src/storage/StronglyConnectedComponentDecomposition.h" +#include "src/models/MarkovAutomaton.h" TEST(StronglyConnectedComponentDecomposition, FullSystem1) { - storm::parser::AutoParser<double> parser(STORM_CPP_BASE_PATH "/examples/ma/tiny/tiny1.tra", STORM_CPP_BASE_PATH "/examples/ma/tiny/tiny1.lab", "", ""); - std::shared_ptr<storm::models::MarkovAutomaton<double>> markovAutomaton = parser.getModel<storm::models::MarkovAutomaton<double>>(); + std::shared_ptr<storm::models::AbstractModel<double>> abstractModel = storm::parser::AutoParser::parseModel(STORM_CPP_BASE_PATH "/examples/ma/tiny/tiny1.tra", STORM_CPP_BASE_PATH "/examples/ma/tiny/tiny1.lab", "", ""); + + std::shared_ptr<storm::models::MarkovAutomaton<double>> markovAutomaton = abstractModel->as<storm::models::MarkovAutomaton<double>>(); storm::storage::StronglyConnectedComponentDecomposition<double> sccDecomposition; @@ -22,8 +24,9 @@ TEST(StronglyConnectedComponentDecomposition, FullSystem1) { } TEST(StronglyConnectedComponentDecomposition, FullSystem2) { - storm::parser::AutoParser<double> parser(STORM_CPP_BASE_PATH "/examples/ma/tiny/tiny2.tra", STORM_CPP_BASE_PATH "/examples/ma/tiny/tiny2.lab", "", ""); - std::shared_ptr<storm::models::MarkovAutomaton<double>> markovAutomaton = parser.getModel<storm::models::MarkovAutomaton<double>>(); + std::shared_ptr<storm::models::AbstractModel<double>> abstractModel = storm::parser::AutoParser::parseModel(STORM_CPP_BASE_PATH "/examples/ma/tiny/tiny2.tra", STORM_CPP_BASE_PATH "/examples/ma/tiny/tiny2.lab", "", ""); + + std::shared_ptr<storm::models::MarkovAutomaton<double>> markovAutomaton = abstractModel->as<storm::models::MarkovAutomaton<double>>(); storm::storage::StronglyConnectedComponentDecomposition<double> sccDecomposition; ASSERT_NO_THROW(sccDecomposition = storm::storage::StronglyConnectedComponentDecomposition<double>(*markovAutomaton, true, false)); @@ -71,4 +74,4 @@ TEST(StronglyConnectedComponentDecomposition, MatrixBasedSystem) { ASSERT_EQ(2, sccDecomposition.size()); dtmc = nullptr; -} \ No newline at end of file +} diff --git a/test/performance/graph/GraphTest.cpp b/test/performance/graph/GraphTest.cpp index b3355c398..e0fe1b9b4 100644 --- a/test/performance/graph/GraphTest.cpp +++ b/test/performance/graph/GraphTest.cpp @@ -4,11 +4,13 @@ #include "src/parser/AutoParser.h" #include "src/utility/graph.h" #include "src/storage/StronglyConnectedComponentDecomposition.h" +#include "src/models/Mdp.h" +#include "src/models/Dtmc.h" TEST(GraphTest, PerformProb01) { - storm::parser::AutoParser<double> parser(STORM_CPP_BASE_PATH "/examples/dtmc/crowds/crowds20_5.tra", STORM_CPP_BASE_PATH "/examples/dtmc/crowds/crowds20_5.lab", "", ""); + std::shared_ptr<storm::models::AbstractModel<double>> abstractModel = storm::parser::AutoParser::parseModel(STORM_CPP_BASE_PATH "/examples/dtmc/crowds/crowds20_5.tra", STORM_CPP_BASE_PATH "/examples/dtmc/crowds/crowds20_5.lab", "", ""); - std::shared_ptr<storm::models::Dtmc<double>> dtmc = parser.getModel<storm::models::Dtmc<double>>(); + std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>(); storm::storage::BitVector trueStates(dtmc->getNumberOfStates(), true); LOG4CPLUS_WARN(logger, "Computing prob01 (3 times) for crowds/crowds20_5..."); @@ -31,9 +33,9 @@ TEST(GraphTest, PerformProb01) { dtmc = nullptr; - storm::parser::AutoParser<double> parser2(STORM_CPP_BASE_PATH "/examples/dtmc/synchronous_leader/leader6_8.tra", STORM_CPP_BASE_PATH "/examples/dtmc/synchronous_leader/leader6_8.lab", "", ""); + abstractModel = storm::parser::AutoParser::parseModel(STORM_CPP_BASE_PATH "/examples/dtmc/synchronous_leader/leader6_8.tra", STORM_CPP_BASE_PATH "/examples/dtmc/synchronous_leader/leader6_8.lab", "", ""); - std::shared_ptr<storm::models::Dtmc<double>> dtmc2 = parser2.getModel<storm::models::Dtmc<double>>(); + std::shared_ptr<storm::models::Dtmc<double>> dtmc2 = abstractModel->as<storm::models::Dtmc<double>>(); trueStates = storm::storage::BitVector(dtmc2->getNumberOfStates(), true); LOG4CPLUS_WARN(logger, "Computing prob01 for synchronous_leader/leader6_8..."); @@ -47,8 +49,8 @@ TEST(GraphTest, PerformProb01) { } TEST(GraphTest, PerformProb01MinMax) { - storm::parser::AutoParser<double> parser(STORM_CPP_BASE_PATH "/examples/mdp/asynchronous_leader/leader7.tra", STORM_CPP_BASE_PATH "/examples/mdp/asynchronous_leader/leader7.lab", "", ""); - std::shared_ptr<storm::models::Mdp<double>> mdp = parser.getModel<storm::models::Mdp<double>>(); + std::shared_ptr<storm::models::AbstractModel<double>> abstractModel = storm::parser::AutoParser::parseModel(STORM_CPP_BASE_PATH "/examples/mdp/asynchronous_leader/leader7.tra", STORM_CPP_BASE_PATH "/examples/mdp/asynchronous_leader/leader7.lab", "", ""); + std::shared_ptr<storm::models::Mdp<double>> mdp = abstractModel->as<storm::models::Mdp<double>>(); storm::storage::BitVector trueStates(mdp->getNumberOfStates(), true); LOG4CPLUS_WARN(logger, "Computing prob01min for asynchronous_leader/leader7..."); @@ -67,8 +69,8 @@ TEST(GraphTest, PerformProb01MinMax) { mdp = nullptr; - storm::parser::AutoParser<double> parser2(STORM_CPP_BASE_PATH "/examples/mdp/consensus/coin4_6.tra", STORM_CPP_BASE_PATH "/examples/mdp/consensus/coin4_6.lab", "", ""); - std::shared_ptr<storm::models::Mdp<double>> mdp2 = parser2.getModel<storm::models::Mdp<double>>(); + abstractModel = storm::parser::AutoParser::parseModel(STORM_CPP_BASE_PATH "/examples/mdp/consensus/coin4_6.tra", STORM_CPP_BASE_PATH "/examples/mdp/consensus/coin4_6.lab", "", ""); + std::shared_ptr<storm::models::Mdp<double>> mdp2 = abstractModel->as<storm::models::Mdp<double>>(); trueStates = storm::storage::BitVector(mdp2->getNumberOfStates(), true); LOG4CPLUS_WARN(logger, "Computing prob01min for consensus/coin4_6..."); @@ -86,4 +88,4 @@ TEST(GraphTest, PerformProb01MinMax) { ASSERT_EQ(prob01.second.getNumberOfSetBits(), 63616ull); mdp2 = nullptr; -} \ No newline at end of file +} diff --git a/test/performance/modelchecker/GmmxxDtmcPrctModelCheckerTest.cpp b/test/performance/modelchecker/GmmxxDtmcPrctModelCheckerTest.cpp index c9c309d60..2fddf8702 100644 --- a/test/performance/modelchecker/GmmxxDtmcPrctModelCheckerTest.cpp +++ b/test/performance/modelchecker/GmmxxDtmcPrctModelCheckerTest.cpp @@ -10,14 +10,14 @@ TEST(GmmxxDtmcPrctlModelCheckerTest, Crowds) { storm::settings::Settings* s = storm::settings::Settings::getInstance(); storm::settings::InternalOptionMemento deadlockOption("fixDeadlocks", true); ASSERT_TRUE(s->isSet("fixDeadlocks")); - storm::parser::AutoParser<double> parser(STORM_CPP_BASE_PATH "/examples/dtmc/crowds/crowds20_5.tra", STORM_CPP_BASE_PATH "/examples/dtmc/crowds/crowds20_5.lab", "", ""); + std::shared_ptr<storm::models::AbstractModel<double>> abstractModel = storm::parser::AutoParser::parseModel(STORM_CPP_BASE_PATH "/examples/dtmc/crowds/crowds20_5.tra", STORM_CPP_BASE_PATH "/examples/dtmc/crowds/crowds20_5.lab", "", ""); - ASSERT_EQ(parser.getType(), storm::models::DTMC); + ASSERT_EQ(abstractModel->getType(), storm::models::DTMC); - std::shared_ptr<storm::models::Dtmc<double>> dtmc = parser.getModel<storm::models::Dtmc<double>>(); + std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>(); ASSERT_EQ(dtmc->getNumberOfStates(), 2036647ull); - ASSERT_EQ(dtmc->getNumberOfTransitions(), 8973900ull); + ASSERT_EQ(dtmc->getNumberOfTransitions(), 7362293ull); storm::modelchecker::prctl::SparseDtmcPrctlModelChecker<double> mc(*dtmc, new storm::solver::GmmxxLinearEquationSolver<double>()); @@ -63,15 +63,15 @@ TEST(GmmxxDtmcPrctlModelCheckerTest, SynchronousLeader) { storm::settings::Settings* s = storm::settings::Settings::getInstance(); storm::settings::InternalOptionMemento deadlockOption("fixDeadlocks", true); ASSERT_TRUE(s->isSet("fixDeadlocks")); - storm::parser::AutoParser<double> parser(STORM_CPP_BASE_PATH "/examples/dtmc/synchronous_leader/leader6_8.tra", STORM_CPP_BASE_PATH "/examples/dtmc/synchronous_leader/leader6_8.lab", "", STORM_CPP_BASE_PATH "/examples/dtmc/synchronous_leader/leader6_8.pick.trans.rew"); + std::shared_ptr<storm::models::AbstractModel<double>> abstractModel = storm::parser::AutoParser::parseModel(STORM_CPP_BASE_PATH "/examples/dtmc/synchronous_leader/leader6_8.tra", STORM_CPP_BASE_PATH "/examples/dtmc/synchronous_leader/leader6_8.lab", "", STORM_CPP_BASE_PATH "/examples/dtmc/synchronous_leader/leader6_8.pick.trans.rew"); - ASSERT_EQ(parser.getType(), storm::models::DTMC); + ASSERT_EQ(abstractModel->getType(), storm::models::DTMC); - std::shared_ptr<storm::models::Dtmc<double>> dtmc = parser.getModel<storm::models::Dtmc<double>>(); + std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>(); ASSERT_EQ(dtmc->getNumberOfStates(), 1312334ull); - ASSERT_EQ(dtmc->getNumberOfTransitions(), 2886810ull); + ASSERT_EQ(dtmc->getNumberOfTransitions(), 1574477ull); storm::modelchecker::prctl::SparseDtmcPrctlModelChecker<double> mc(*dtmc, new storm::solver::GmmxxLinearEquationSolver<double>()); @@ -110,4 +110,4 @@ TEST(GmmxxDtmcPrctlModelCheckerTest, SynchronousLeader) { ASSERT_LT(std::abs(result[0] - 1.025106273), s->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); delete rewardFormula; -} \ No newline at end of file +} diff --git a/test/performance/modelchecker/SparseMdpPrctlModelCheckerTest.cpp b/test/performance/modelchecker/SparseMdpPrctlModelCheckerTest.cpp index 4a8193d26..8f083f89f 100644 --- a/test/performance/modelchecker/SparseMdpPrctlModelCheckerTest.cpp +++ b/test/performance/modelchecker/SparseMdpPrctlModelCheckerTest.cpp @@ -8,11 +8,11 @@ TEST(SparseMdpPrctlModelCheckerTest, AsynchronousLeader) { storm::settings::Settings* s = storm::settings::Settings::getInstance(); - storm::parser::AutoParser<double> parser(STORM_CPP_BASE_PATH "/examples/mdp/asynchronous_leader/leader7.tra", STORM_CPP_BASE_PATH "/examples/mdp/asynchronous_leader/leader7.lab", "", STORM_CPP_BASE_PATH "/examples/mdp/asynchronous_leader/leader7.trans.rew"); + std::shared_ptr<storm::models::AbstractModel<double>> abstractModel = storm::parser::AutoParser::parseModel(STORM_CPP_BASE_PATH "/examples/mdp/asynchronous_leader/leader7.tra", STORM_CPP_BASE_PATH "/examples/mdp/asynchronous_leader/leader7.lab", "", STORM_CPP_BASE_PATH "/examples/mdp/asynchronous_leader/leader7.trans.rew"); - ASSERT_EQ(parser.getType(), storm::models::MDP); + ASSERT_EQ(abstractModel->getType(), storm::models::MDP); - std::shared_ptr<storm::models::Mdp<double>> mdp = parser.getModel<storm::models::Mdp<double>>(); + std::shared_ptr<storm::models::Mdp<double>> mdp = abstractModel->as<storm::models::Mdp<double>>(); ASSERT_EQ(mdp->getNumberOfStates(), 2095783ull); ASSERT_EQ(mdp->getNumberOfTransitions(), 7714385ull); @@ -79,11 +79,11 @@ TEST(SparseMdpPrctlModelCheckerTest, Consensus) { // Increase the maximal number of iterations, because the solver does not converge otherwise. // This is done in the main cpp unit - storm::parser::AutoParser<double> parser(STORM_CPP_BASE_PATH "/examples/mdp/consensus/coin4_6.tra", STORM_CPP_BASE_PATH "/examples/mdp/consensus/coin4_6.lab", STORM_CPP_BASE_PATH "/examples/mdp/consensus/coin4_6.steps.state.rew", ""); + std::shared_ptr<storm::models::AbstractModel<double>> abstractModel = storm::parser::AutoParser::parseModel(STORM_CPP_BASE_PATH "/examples/mdp/consensus/coin4_6.tra", STORM_CPP_BASE_PATH "/examples/mdp/consensus/coin4_6.lab", STORM_CPP_BASE_PATH "/examples/mdp/consensus/coin4_6.steps.state.rew", ""); - ASSERT_EQ(parser.getType(), storm::models::MDP); + ASSERT_EQ(abstractModel->getType(), storm::models::MDP); - std::shared_ptr<storm::models::Mdp<double>> mdp = parser.getModel<storm::models::Mdp<double>>(); + std::shared_ptr<storm::models::Mdp<double>> mdp = abstractModel->as<storm::models::Mdp<double>>(); ASSERT_EQ(mdp->getNumberOfStates(), 63616ull); ASSERT_EQ(mdp->getNumberOfTransitions(), 213472ull); @@ -168,4 +168,4 @@ TEST(SparseMdpPrctlModelCheckerTest, Consensus) { ASSERT_LT(std::abs(result[31168] - 2183.142422), s->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); delete rewardFormula; -} \ No newline at end of file +} diff --git a/test/performance/storage/MaximalEndComponentDecompositionTest.cpp b/test/performance/storage/MaximalEndComponentDecompositionTest.cpp index a34a35091..ee5460fed 100644 --- a/test/performance/storage/MaximalEndComponentDecompositionTest.cpp +++ b/test/performance/storage/MaximalEndComponentDecompositionTest.cpp @@ -2,10 +2,11 @@ #include "storm-config.h" #include "src/parser/AutoParser.h" #include "src/storage/MaximalEndComponentDecomposition.h" +#include "src/models/Mdp.h" TEST(MaximalEndComponentDecomposition, AsynchronousLeader) { - storm::parser::AutoParser<double> parser(STORM_CPP_BASE_PATH "/examples/mdp/asynchronous_leader/leader7.tra", STORM_CPP_BASE_PATH "/examples/mdp/asynchronous_leader/leader7.lab", "", ""); - std::shared_ptr<storm::models::Mdp<double>> mdp = parser.getModel<storm::models::Mdp<double>>(); + std::shared_ptr<storm::models::AbstractModel<double>> abstractModel = storm::parser::AutoParser::parseModel(STORM_CPP_BASE_PATH "/examples/mdp/asynchronous_leader/leader7.tra", STORM_CPP_BASE_PATH "/examples/mdp/asynchronous_leader/leader7.lab", "", ""); + std::shared_ptr<storm::models::Mdp<double>> mdp = abstractModel->as<storm::models::Mdp<double>>(); storm::storage::MaximalEndComponentDecomposition<double> mecDecomposition; ASSERT_NO_THROW(mecDecomposition = storm::storage::MaximalEndComponentDecomposition<double>(*mdp)); @@ -15,12 +16,12 @@ TEST(MaximalEndComponentDecomposition, AsynchronousLeader) { } TEST(MaximalEndComponentDecomposition, Consensus) { - storm::parser::AutoParser<double> parser(STORM_CPP_BASE_PATH "/examples/mdp/consensus/coin6_4.tra", STORM_CPP_BASE_PATH "/examples/mdp/consensus/coin6_4.lab", "", ""); - std::shared_ptr<storm::models::Mdp<double>> mdp = parser.getModel<storm::models::Mdp<double>>(); + std::shared_ptr<storm::models::AbstractModel<double>> abstractModel = storm::parser::AutoParser::parseModel(STORM_CPP_BASE_PATH "/examples/mdp/consensus/coin6_4.tra", STORM_CPP_BASE_PATH "/examples/mdp/consensus/coin6_4.lab", "", ""); + std::shared_ptr<storm::models::Mdp<double>> mdp = abstractModel->as<storm::models::Mdp<double>>(); storm::storage::MaximalEndComponentDecomposition<double> mecDecomposition; ASSERT_NO_THROW(mecDecomposition = storm::storage::MaximalEndComponentDecomposition<double>(*mdp)); ASSERT_EQ(384, mecDecomposition.size()); mdp = nullptr; -} \ No newline at end of file +} diff --git a/test/performance/storage/SparseMatrixTest.cpp b/test/performance/storage/SparseMatrixTest.cpp index 367bd3401..b59d283af 100644 --- a/test/performance/storage/SparseMatrixTest.cpp +++ b/test/performance/storage/SparseMatrixTest.cpp @@ -15,7 +15,7 @@ TEST(SparseMatrix, Iteration) { for (uint_fast64_t row = 0; row < matrix.getRowCount(); ++row) { for (auto const& entry : matrix.getRow(row)) { // The following can never be true, but prevents the compiler from optimizing away the loop. - if (entry.first > matrix.getColumnCount()) { + if (entry.getColumn() > matrix.getColumnCount()) { ASSERT_TRUE(false); } } diff --git a/test/performance/storage/StronglyConnectedComponentDecompositionTest.cpp b/test/performance/storage/StronglyConnectedComponentDecompositionTest.cpp index b519c27be..83bc77de6 100644 --- a/test/performance/storage/StronglyConnectedComponentDecompositionTest.cpp +++ b/test/performance/storage/StronglyConnectedComponentDecompositionTest.cpp @@ -2,10 +2,12 @@ #include "storm-config.h" #include "src/parser/AutoParser.h" #include "src/storage/StronglyConnectedComponentDecomposition.h" +#include "src/models/Mdp.h" +#include "src/models/Dtmc.h" TEST(StronglyConnectedComponentDecomposition, Crowds) { - storm::parser::AutoParser<double> parser(STORM_CPP_BASE_PATH "/examples/dtmc/crowds/crowds20_5.tra", STORM_CPP_BASE_PATH "/examples/dtmc/crowds/crowds20_5.lab", "", ""); - std::shared_ptr<storm::models::Dtmc<double>> dtmc = parser.getModel<storm::models::Dtmc<double>>(); + std::shared_ptr<storm::models::AbstractModel<double>> abstractModel = storm::parser::AutoParser::parseModel(STORM_CPP_BASE_PATH "/examples/dtmc/crowds/crowds20_5.tra", STORM_CPP_BASE_PATH "/examples/dtmc/crowds/crowds20_5.lab", "", ""); + std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>(); storm::storage::StronglyConnectedComponentDecomposition<double> sccDecomposition; @@ -22,8 +24,8 @@ TEST(StronglyConnectedComponentDecomposition, Crowds) { } TEST(StronglyConnectedComponentDecomposition, SynchronousLeader) { - storm::parser::AutoParser<double> parser(STORM_CPP_BASE_PATH "/examples/dtmc/synchronous_leader/leader6_9.tra", STORM_CPP_BASE_PATH "/examples/dtmc/synchronous_leader/leader6_9.lab", "", ""); - std::shared_ptr<storm::models::Dtmc<double>> dtmc = parser.getModel<storm::models::Dtmc<double>>(); + std::shared_ptr<storm::models::AbstractModel<double>> abstractModel = storm::parser::AutoParser::parseModel(STORM_CPP_BASE_PATH "/examples/dtmc/synchronous_leader/leader6_9.tra", STORM_CPP_BASE_PATH "/examples/dtmc/synchronous_leader/leader6_9.lab", "", ""); + std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>(); storm::storage::StronglyConnectedComponentDecomposition<double> sccDecomposition; @@ -40,8 +42,8 @@ TEST(StronglyConnectedComponentDecomposition, SynchronousLeader) { } TEST(StronglyConnectedComponentDecomposition, AsynchronousLeader) { - storm::parser::AutoParser<double> parser(STORM_CPP_BASE_PATH "/examples/mdp/asynchronous_leader/leader7.tra", STORM_CPP_BASE_PATH "/examples/mdp/asynchronous_leader/leader7.lab", "", ""); - std::shared_ptr<storm::models::Mdp<double>> mdp = parser.getModel<storm::models::Mdp<double>>(); + std::shared_ptr<storm::models::AbstractModel<double>> abstractModel = storm::parser::AutoParser::parseModel(STORM_CPP_BASE_PATH "/examples/mdp/asynchronous_leader/leader7.tra", STORM_CPP_BASE_PATH "/examples/mdp/asynchronous_leader/leader7.lab", "", ""); + std::shared_ptr<storm::models::Mdp<double>> mdp = abstractModel->as<storm::models::Mdp<double>>(); storm::storage::StronglyConnectedComponentDecomposition<double> sccDecomposition; @@ -58,8 +60,8 @@ TEST(StronglyConnectedComponentDecomposition, AsynchronousLeader) { } TEST(StronglyConnectedComponentDecomposition, Consensus) { - storm::parser::AutoParser<double> parser(STORM_CPP_BASE_PATH "/examples/mdp/consensus/coin6_4.tra", STORM_CPP_BASE_PATH "/examples/mdp/consensus/coin6_4.lab", "", ""); - std::shared_ptr<storm::models::Mdp<double>> mdp = parser.getModel<storm::models::Mdp<double>>(); + std::shared_ptr<storm::models::AbstractModel<double>> abstractModel = storm::parser::AutoParser::parseModel(STORM_CPP_BASE_PATH "/examples/mdp/consensus/coin6_4.tra", STORM_CPP_BASE_PATH "/examples/mdp/consensus/coin6_4.lab", "", ""); + std::shared_ptr<storm::models::Mdp<double>> mdp = abstractModel->as<storm::models::Mdp<double>>(); storm::storage::StronglyConnectedComponentDecomposition<double> sccDecomposition; @@ -73,4 +75,4 @@ TEST(StronglyConnectedComponentDecomposition, Consensus) { ASSERT_EQ(384, sccDecomposition.size()); mdp = nullptr; -} \ No newline at end of file +}