2574 lines
164 KiB
2574 lines
164 KiB
#include "storm/builder/jit/ExplicitJitJaniModelBuilder.h"
|
|
|
|
#include <iostream>
|
|
#include <cstdio>
|
|
#include <chrono>
|
|
#include <errno.h>
|
|
|
|
#include "storm/solver/SmtSolver.h"
|
|
|
|
#include "storm/storage/jani/Edge.h"
|
|
#include "storm/storage/jani/EdgeDestination.h"
|
|
#include "storm/storage/jani/Model.h"
|
|
#include "storm/storage/jani/Automaton.h"
|
|
#include "storm/storage/jani/Location.h"
|
|
#include "storm/storage/jani/AutomatonComposition.h"
|
|
#include "storm/storage/jani/ParallelComposition.h"
|
|
#include "storm/storage/jani/CompositionInformationVisitor.h"
|
|
|
|
|
|
#include "storm/builder/RewardModelInformation.h"
|
|
|
|
#include "storm/models/sparse/Dtmc.h"
|
|
#include "storm/models/sparse/StandardRewardModel.h"
|
|
|
|
#include "storm/utility/macros.h"
|
|
#include "storm/utility/solver.h"
|
|
#include "storm/exceptions/WrongFormatException.h"
|
|
#include "storm/exceptions/InvalidStateException.h"
|
|
#include "storm/exceptions/InvalidArgumentException.h"
|
|
#include "storm/exceptions/NotSupportedException.h"
|
|
#include "storm/exceptions/UnexpectedException.h"
|
|
|
|
#include "storm/settings/SettingsManager.h"
|
|
#include "storm/settings/modules/JitBuilderSettings.h"
|
|
|
|
#include "storm/settings/modules/BuildSettings.h"
|
|
|
|
#include "storm/utility/OsDetection.h"
|
|
#include "storm-config.h"
|
|
|
|
namespace storm {
|
|
namespace builder {
|
|
namespace jit {
|
|
|
|
static const std::string JIT_VARIABLE_EXTENSION = "_jit_";
|
|
|
|
#ifdef LINUX
|
|
static const std::string DYLIB_EXTENSION = ".so";
|
|
#endif
|
|
#ifdef MACOSX
|
|
static const std::string DYLIB_EXTENSION = ".dylib";
|
|
#endif
|
|
#ifdef WINDOWS
|
|
static const std::string DYLIB_EXTENSION = ".dll";
|
|
#endif
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::ExplicitJitJaniModelBuilder(storm::jani::Model const& model, storm::builder::BuilderOptions const& options) : options(options), model(model.substituteConstantsFunctions()), modelComponentsBuilder(model.getModelType()) {
|
|
|
|
// Load all options from the settings module.
|
|
storm::settings::modules::JitBuilderSettings const& settings = storm::settings::getModule<storm::settings::modules::JitBuilderSettings>();
|
|
if (settings.isCompilerSet()) {
|
|
compiler = settings.getCompiler();
|
|
} else {
|
|
const char* cxxEnv = std::getenv("CXX");
|
|
if (cxxEnv != nullptr) {
|
|
compiler = std::string(cxxEnv);
|
|
}
|
|
if (compiler.empty()) {
|
|
compiler = "c++";
|
|
}
|
|
}
|
|
if (settings.isCompilerFlagsSet()) {
|
|
compilerFlags = settings.getCompilerFlags();
|
|
} else {
|
|
std::stringstream flagStream;
|
|
#ifdef LINUX
|
|
flagStream << "-std=c++14 -fPIC -march=native -shared ";
|
|
#endif
|
|
#ifdef MACOSX
|
|
flagStream << "-std=c++14 -stdlib=libc++ -fPIC -march=native -shared -undefined dynamic_lookup ";
|
|
#endif
|
|
|
|
flagStream << "-O" << settings.getOptimizationLevel();
|
|
compilerFlags = flagStream.str();
|
|
}
|
|
if (settings.isBoostIncludeDirectorySet()) {
|
|
boostIncludeDirectory = settings.getBoostIncludeDirectory();
|
|
} else {
|
|
boostIncludeDirectory = STORM_BOOST_INCLUDE_DIR;
|
|
}
|
|
if (settings.isStormIncludeDirectorySet()) {
|
|
stormIncludeDirectory = settings.getStormIncludeDirectory();
|
|
} else {
|
|
stormIncludeDirectory = STORM_BUILD_DIR "/include";
|
|
}
|
|
if (settings.isCarlIncludeDirectorySet()) {
|
|
carlIncludeDirectory = settings.getCarlIncludeDirectory();
|
|
} else {
|
|
carlIncludeDirectory = STORM_CARL_INCLUDE_DIR;
|
|
}
|
|
#ifdef STORM_HAVE_CLN
|
|
clnIncludeDirectory = CLN_INCLUDE_DIR;
|
|
#else
|
|
clnIncludeDirectory = "";
|
|
#endif
|
|
#ifdef STORM_HAVE_GMP
|
|
gmpIncludeDirectory = GMP_INCLUDE_DIR;
|
|
#else
|
|
gmpIncludeDirectory = "";
|
|
#endif
|
|
sparseppIncludeDirectory = STORM_BUILD_DIR "/include/resources/3rdparty/sparsepp/";
|
|
|
|
// Register all transient variables as transient.
|
|
for (auto const& variable : this->model.getGlobalVariables().getTransientVariables()) {
|
|
transientVariables.insert(variable.getExpressionVariable());
|
|
}
|
|
|
|
// Construct vector of the automata to be put in parallel.
|
|
storm::jani::Composition const& topLevelComposition = this->model.getSystemComposition();
|
|
if (topLevelComposition.isAutomatonComposition()) {
|
|
parallelAutomata.push_back(this->model.getAutomaton(topLevelComposition.asAutomatonComposition().getAutomatonName()));
|
|
} else {
|
|
STORM_LOG_THROW(topLevelComposition.isParallelComposition(), storm::exceptions::WrongFormatException, "Expected parallel composition.");
|
|
storm::jani::ParallelComposition const& parallelComposition = topLevelComposition.asParallelComposition();
|
|
|
|
for (auto const& composition : parallelComposition.getSubcompositions()) {
|
|
STORM_LOG_THROW(composition->isAutomatonComposition(), storm::exceptions::WrongFormatException, "Expected flat parallel composition.");
|
|
parallelAutomata.push_back(this->model.getAutomaton(composition->asAutomatonComposition().getAutomatonName()));
|
|
}
|
|
}
|
|
|
|
// If the program still contains undefined constants and we are not in a parametric setting, assemble an appropriate error message.
|
|
#ifdef STORM_HAVE_CARL
|
|
if (!std::is_same<ValueType, storm::RationalFunction>::value && this->model.hasUndefinedConstants()) {
|
|
#else
|
|
if (this->model.hasUndefinedConstants()) {
|
|
#endif
|
|
std::vector<std::reference_wrapper<storm::jani::Constant const>> undefinedConstants = this->model.getUndefinedConstants();
|
|
std::stringstream stream;
|
|
bool printComma = false;
|
|
for (auto const& constant : undefinedConstants) {
|
|
if (printComma) {
|
|
stream << ", ";
|
|
} else {
|
|
printComma = true;
|
|
}
|
|
stream << constant.get().getName() << " (" << constant.get().getType() << ")";
|
|
}
|
|
stream << ".";
|
|
STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Model still contains these undefined constants: " + stream.str());
|
|
}
|
|
|
|
#ifdef STORM_HAVE_CARL
|
|
else if (std::is_same<ValueType, storm::RationalFunction>::value && !this->model.undefinedConstantsAreGraphPreserving()) {
|
|
STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "The input model contains undefined constants that influence the graph structure of the underlying model, which is not allowed.");
|
|
}
|
|
#endif
|
|
auto features = model.getModelFeatures();
|
|
features.remove(storm::jani::ModelFeature::DerivedOperators);
|
|
features.remove(storm::jani::ModelFeature::StateExitRewards);
|
|
STORM_LOG_THROW(features.empty(), storm::exceptions::InvalidArgumentException, "The jit model builder does not support the following model feature(s): " << features.toString() << ".");
|
|
|
|
//STORM_LOG_THROW(!model.reusesActionsInComposition(), storm::exceptions::InvalidArgumentException, "The jit JANI model builder currently does not support reusing actions in parallel composition");
|
|
|
|
// Comment this in to print the JANI model for debugging purposes.
|
|
// this->model.makeStandardJaniCompliant();
|
|
// storm::jani::JsonExporter::toStream(this->model, std::vector<std::shared_ptr<storm::logic::Formula const>>(), std::cout, false);
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
boost::optional<std::string> ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::execute(std::string command) {
|
|
auto start = std::chrono::high_resolution_clock::now();
|
|
char buffer[128];
|
|
std::stringstream output;
|
|
command += " 2>&1";
|
|
|
|
STORM_LOG_TRACE("Executing command: " << command);
|
|
|
|
std::unique_ptr<FILE> pipe(popen(command.c_str(), "r"));
|
|
STORM_LOG_THROW(pipe, storm::exceptions::InvalidStateException, "Call to popen failed: " << strerror(errno));
|
|
|
|
while (!feof(pipe.get())) {
|
|
if (fgets(buffer, 128, pipe.get()) != nullptr)
|
|
output << buffer;
|
|
}
|
|
int result = pclose(pipe.get());
|
|
pipe.release();
|
|
|
|
auto end = std::chrono::high_resolution_clock::now();
|
|
STORM_LOG_TRACE("Executing command took " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms.");
|
|
|
|
if (WEXITSTATUS(result) == 0) {
|
|
return boost::none;
|
|
} else {
|
|
return "Executing command failed. Got response: " + output.str();
|
|
}
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
boost::filesystem::path ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::writeToTemporaryFile(std::string const& content, std::string const& suffix) {
|
|
boost::filesystem::path temporaryFile = boost::filesystem::unique_path("%%%%-%%%%-%%%%-%%%%" + suffix);
|
|
std::ofstream out(temporaryFile.native());
|
|
out << content << std::endl;
|
|
out.close();
|
|
return temporaryFile;
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
bool ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::checkTemporaryFileWritable() const {
|
|
bool result = true;
|
|
std::string problem = "Unable to write to a temporary file. Are the appropriate permissions set?";
|
|
try {
|
|
boost::filesystem::path temporaryFile = writeToTemporaryFile("Hello world.");
|
|
} catch (std::exception const& e) {
|
|
result = false;
|
|
STORM_LOG_ERROR(problem);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
bool ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::checkCompilerWorks() const {
|
|
bool result = true;
|
|
std::string problem = "Unable to compile empty program with C++ compiler. Is the C++ compiler '" + compiler + "' installed and working?";
|
|
try {
|
|
std::string emptyProgram = R"(
|
|
int main() {
|
|
return 0;
|
|
}
|
|
)";
|
|
boost::filesystem::path temporaryFile = writeToTemporaryFile(emptyProgram);
|
|
std::string temporaryFilename = boost::filesystem::absolute(temporaryFile).string();
|
|
boost::filesystem::path outputFile = temporaryFile;
|
|
outputFile += ".out";
|
|
std::string outputFilename = boost::filesystem::absolute(outputFile).string();
|
|
boost::optional<std::string> error = execute(compiler + " " + temporaryFilename + " -o " + outputFilename);
|
|
|
|
if (error) {
|
|
result = false;
|
|
STORM_LOG_ERROR(problem);
|
|
STORM_LOG_TRACE(error.get());
|
|
} else {
|
|
boost::filesystem::remove(outputFile);
|
|
}
|
|
} catch(std::exception const& e) {
|
|
result = false;
|
|
STORM_LOG_ERROR(problem);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
bool ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::checkCompilerFlagsWork() const {
|
|
bool result = true;
|
|
std::string problem = "Unable to compile program with the flags '" + compilerFlags + "'. Does the C++ compiler accept these flags?";
|
|
try {
|
|
std::string emptyProgram = R"(
|
|
#include <cstdint>
|
|
|
|
int main() {
|
|
return 0;
|
|
}
|
|
)";
|
|
boost::filesystem::path temporaryFile = writeToTemporaryFile(emptyProgram);
|
|
std::string temporaryFilename = boost::filesystem::absolute(temporaryFile).string();
|
|
boost::filesystem::path outputFile = temporaryFile;
|
|
outputFile += ".out";
|
|
std::string outputFilename = boost::filesystem::absolute(outputFile).string();
|
|
boost::optional<std::string> error = execute(compiler + " " + compilerFlags + " " + temporaryFilename + " -o " + outputFilename);
|
|
|
|
if (error) {
|
|
result = false;
|
|
STORM_LOG_ERROR(problem);
|
|
STORM_LOG_TRACE(error.get());
|
|
} else {
|
|
boost::filesystem::remove(outputFile);
|
|
}
|
|
} catch(std::exception const& e) {
|
|
result = false;
|
|
STORM_LOG_ERROR(problem);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
bool ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::checkBoostAvailable() const {
|
|
bool result = true;
|
|
std::string problem = "Unable to compile program using boost. Is boosts's include directory '" + boostIncludeDirectory + "' set correctly?";
|
|
try {
|
|
std::string program = R"(
|
|
#include <boost/optional.hpp>
|
|
|
|
int main() {
|
|
return 0;
|
|
}
|
|
)";
|
|
boost::filesystem::path temporaryFile = writeToTemporaryFile(program);
|
|
std::string temporaryFilename = boost::filesystem::absolute(temporaryFile).string();
|
|
boost::filesystem::path outputFile = temporaryFile;
|
|
outputFile += ".out";
|
|
std::string outputFilename = boost::filesystem::absolute(outputFile).string();
|
|
boost::optional<std::string> error = execute(compiler + " " + compilerFlags + " " + temporaryFilename + " -I" + boostIncludeDirectory + " -o " + outputFilename);
|
|
|
|
if (error) {
|
|
result = false;
|
|
STORM_LOG_ERROR(problem);
|
|
STORM_LOG_TRACE(error.get());
|
|
} else {
|
|
boost::filesystem::remove(outputFile);
|
|
}
|
|
} catch(std::exception const& e) {
|
|
result = false;
|
|
STORM_LOG_ERROR(problem);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
bool ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::checkBoostDllAvailable() const {
|
|
bool result = true;
|
|
std::string problem = "Unable to compile program using boost's dll library. Is boosts's include directory '" + boostIncludeDirectory + "' pointing to a boost installation with version at least 1.61?";
|
|
try {
|
|
std::string program = R"(
|
|
#include <boost/dll/alias.hpp>
|
|
|
|
int main() {
|
|
return 0;
|
|
}
|
|
)";
|
|
boost::filesystem::path temporaryFile = writeToTemporaryFile(program);
|
|
std::string temporaryFilename = boost::filesystem::absolute(temporaryFile).string();
|
|
boost::filesystem::path outputFile = temporaryFile;
|
|
outputFile += ".out";
|
|
std::string outputFilename = boost::filesystem::absolute(outputFile).string();
|
|
boost::optional<std::string> error = execute(compiler + " " + compilerFlags + " " + temporaryFilename + " -I" + boostIncludeDirectory + " -o " + outputFilename);
|
|
|
|
if (error) {
|
|
result = false;
|
|
STORM_LOG_ERROR(problem);
|
|
STORM_LOG_TRACE(error.get());
|
|
} else {
|
|
boost::filesystem::remove(outputFile);
|
|
}
|
|
} catch(std::exception const& e) {
|
|
result = false;
|
|
STORM_LOG_ERROR(problem);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
bool ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::checkStormHeadersAvailable() const {
|
|
bool result = true;
|
|
std::string problem = "Unable to compile program using storm headers. Is storm's include directory '" + stormIncludeDirectory + "' set correctly? Does the directory contain all the headers (in particular 'storm-config.h')?";
|
|
try {
|
|
std::string program = R"(
|
|
#include "storm/builder/RewardModelInformation.h"
|
|
#include "storm-config.h"
|
|
|
|
int main() {
|
|
return 0;
|
|
}
|
|
)";
|
|
boost::filesystem::path temporaryFile = writeToTemporaryFile(program);
|
|
std::string temporaryFilename = boost::filesystem::absolute(temporaryFile).string();
|
|
boost::filesystem::path outputFile = temporaryFile;
|
|
outputFile += ".out";
|
|
std::string outputFilename = boost::filesystem::absolute(outputFile).string();
|
|
boost::optional<std::string> error = execute(compiler + " " + compilerFlags + " " + temporaryFilename + " -I" + stormIncludeDirectory + " -o " + outputFilename);
|
|
|
|
if (error) {
|
|
result = false;
|
|
STORM_LOG_ERROR(problem);
|
|
STORM_LOG_TRACE(error.get());
|
|
} else {
|
|
boost::filesystem::remove(outputFile);
|
|
}
|
|
} catch(std::exception const& e) {
|
|
result = false;
|
|
STORM_LOG_ERROR(problem);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
bool ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::checkCarlAvailable() const {
|
|
bool result = true;
|
|
std::string problem = "Unable to compile program using Carl data structures. Is Carls's include directory '" + carlIncludeDirectory + "' set correctly?";
|
|
try {
|
|
std::string program = R"(
|
|
#include "storm/adapters/RationalFunctionAdapter.h"
|
|
|
|
int main() {
|
|
return 0;
|
|
}
|
|
)";
|
|
boost::filesystem::path temporaryFile = writeToTemporaryFile(program);
|
|
std::string temporaryFilename = boost::filesystem::absolute(temporaryFile).string();
|
|
boost::filesystem::path outputFile = temporaryFile;
|
|
outputFile += ".out";
|
|
std::string outputFilename = boost::filesystem::absolute(outputFile).string();
|
|
std::string includes = "";
|
|
for (std::string const& dir : {stormIncludeDirectory, carlIncludeDirectory, clnIncludeDirectory, gmpIncludeDirectory}) {
|
|
if (dir != "") {
|
|
includes += " -I" + dir;
|
|
}
|
|
}
|
|
boost::optional<std::string> error = execute(compiler + " " + compilerFlags + " " + temporaryFilename + includes + " -o " + outputFilename);
|
|
|
|
if (error) {
|
|
result = false;
|
|
STORM_LOG_ERROR(problem);
|
|
STORM_LOG_TRACE(error.get());
|
|
} else {
|
|
boost::filesystem::remove(outputFile);
|
|
}
|
|
} catch(std::exception const& e) {
|
|
result = false;
|
|
STORM_LOG_ERROR(problem);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
bool ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::doctor() const {
|
|
bool result = true;
|
|
|
|
STORM_LOG_DEBUG("Checking whether temporary file is writable.");
|
|
result = checkTemporaryFileWritable();
|
|
if (!result) {
|
|
return result;
|
|
}
|
|
STORM_LOG_DEBUG("Success.");
|
|
|
|
STORM_LOG_DEBUG("Checking whether compiler invocation works.");
|
|
result = checkCompilerWorks();
|
|
if (!result) {
|
|
return result;
|
|
}
|
|
STORM_LOG_DEBUG("Success.");
|
|
|
|
STORM_LOG_DEBUG("Checking whether compiler flags work.");
|
|
result = checkCompilerFlagsWork();
|
|
if (!result) {
|
|
return result;
|
|
}
|
|
STORM_LOG_DEBUG("Success.");
|
|
|
|
STORM_LOG_DEBUG("Checking whether boost is available.");
|
|
result = checkBoostAvailable();
|
|
if (!result) {
|
|
return result;
|
|
}
|
|
STORM_LOG_DEBUG("Success.");
|
|
|
|
STORM_LOG_DEBUG("Checking whether boost dll library is available.");
|
|
result = checkBoostDllAvailable();
|
|
if (!result) {
|
|
return result;
|
|
}
|
|
STORM_LOG_DEBUG("Success.");
|
|
|
|
STORM_LOG_DEBUG("Checking whether Storm's headers are available.");
|
|
result = checkStormHeadersAvailable();
|
|
if (!result) {
|
|
return result;
|
|
}
|
|
STORM_LOG_DEBUG("Success.");
|
|
|
|
if (std::is_same<storm::RationalNumber, ValueType>::value) {
|
|
STORM_LOG_DEBUG("Checking whether Carl's headers are available.");
|
|
result = checkCarlAvailable();
|
|
if (!result) {
|
|
return result;
|
|
}
|
|
STORM_LOG_DEBUG("Success.");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
std::shared_ptr<storm::models::sparse::Model<ValueType, RewardModelType>> ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::build() {
|
|
// (0) Assemble information about the model.
|
|
cpptempl::data_map modelData = generateModelData();
|
|
|
|
// (1) generate the source code of the shared library
|
|
std::string source;
|
|
try {
|
|
source = createSourceCodeFromSkeleton(modelData);
|
|
} catch (std::exception const& e) {
|
|
STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Could not create the source code for model generation (error: " << e.what() << ").");
|
|
}
|
|
STORM_LOG_TRACE("Successfully created source code for model generation: " << source);
|
|
|
|
// (2) Write the source code to a temporary file.
|
|
boost::filesystem::path temporarySourceFile = writeToTemporaryFile(source);
|
|
|
|
// (3) Compile the source code to a shared library.
|
|
boost::filesystem::path dynamicLibraryPath = compileToSharedLibrary(temporarySourceFile);
|
|
STORM_LOG_TRACE("Successfully compiled shared library.");
|
|
|
|
// (4) Remove the source code of the shared library we just compiled.
|
|
boost::filesystem::remove(temporarySourceFile);
|
|
|
|
// (5) Create the builder from the shared library.
|
|
createBuilder(dynamicLibraryPath);
|
|
|
|
// (6) Execute the build function of the builder in the shared library and build the actual model.
|
|
auto start = std::chrono::high_resolution_clock::now();
|
|
|
|
std::shared_ptr<storm::models::sparse::Model<ValueType, storm::models::sparse::StandardRewardModel<ValueType>>> sparseModel(nullptr);
|
|
boost::optional<std::string> error;
|
|
try {
|
|
sparseModel = std::shared_ptr<storm::models::sparse::Model<ValueType, storm::models::sparse::StandardRewardModel<ValueType>>>(builder->build());
|
|
STORM_LOG_THROW(sparseModel, storm::exceptions::UnexpectedException, "An unexpected error occurred.");
|
|
STORM_LOG_TRACE("Successfully got model from jit-builder.");
|
|
} catch (std::exception const& e) {
|
|
error = e.what();
|
|
}
|
|
|
|
auto end = std::chrono::high_resolution_clock::now();
|
|
STORM_LOG_TRACE("Building model took " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms.");
|
|
|
|
// (7) Delete the shared library.
|
|
boost::filesystem::remove(dynamicLibraryPath);
|
|
|
|
STORM_LOG_THROW(!error, storm::exceptions::WrongFormatException, "Model building failed. Reason: " << error.get());
|
|
|
|
// Return the constructed model.
|
|
return sparseModel;
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
cpptempl::data_map ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::generateModelData() {
|
|
cpptempl::data_map modelData;
|
|
// Generate trivial model-information.
|
|
modelData["deterministic_model"] = asString(model.isDeterministicModel());
|
|
modelData["discrete_time_model"] = asString(model.isDiscreteTimeModel());
|
|
|
|
// Use lists here to enable if query in skeleton program even if the property is just boolean.
|
|
cpptempl::data_list list;
|
|
if (options.isExplorationChecksSet()) {
|
|
list.push_back(cpptempl::data_map());
|
|
}
|
|
modelData["exploration_checks"] = cpptempl::make_data(list);
|
|
list = cpptempl::data_list();
|
|
if (options.isShowProgressSet()) {
|
|
list.push_back(cpptempl::data_map());
|
|
}
|
|
modelData["expl_progress"] = cpptempl::make_data(list);
|
|
|
|
std::stringstream progressDelayStream;
|
|
progressDelayStream << options.getShowProgressDelay();
|
|
modelData["expl_progress_interval"] = cpptempl::make_data(progressDelayStream.str());
|
|
list = cpptempl::data_list();
|
|
if (std::is_same<storm::RationalNumber, ValueType>::value) {
|
|
list.push_back(cpptempl::data_map());
|
|
}
|
|
modelData["exact"] = cpptempl::make_data(list);
|
|
list = cpptempl::data_list();
|
|
if (std::is_same<storm::RationalFunction, ValueType>::value) {
|
|
list.push_back(cpptempl::data_map());
|
|
}
|
|
modelData["parametric"] = cpptempl::make_data(list);
|
|
list = cpptempl::data_list();
|
|
if (std::is_same<double, ValueType>::value) {
|
|
list.push_back(cpptempl::data_map());
|
|
}
|
|
modelData["double"] = cpptempl::make_data(list);
|
|
|
|
list = cpptempl::data_list();
|
|
if (storm::settings::getModule<storm::settings::modules::BuildSettings>().isDontFixDeadlocksSet()) {
|
|
list.push_back(cpptempl::data_map());
|
|
}
|
|
modelData["dontFixDeadlocks"] = cpptempl::make_data(list);
|
|
|
|
// If we are building a possibly parametric model, we need to create the parameters.
|
|
if (std::is_same<storm::RationalFunction, ValueType>::value) {
|
|
generateParameters(modelData);
|
|
}
|
|
|
|
// Generate non-trivial model-information.
|
|
generateVariables(modelData);
|
|
generateInitialStates(modelData);
|
|
generateRewards(modelData); // We need to generate the reward information before the edges, because we already use it there.
|
|
generateEdges(modelData);
|
|
generateLocations(modelData);
|
|
generateLabels(modelData);
|
|
generateTerminalExpressions(modelData);
|
|
|
|
return modelData;
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
cpptempl::data_map ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::generateBooleanVariable(storm::jani::BooleanVariable const& variable) {
|
|
cpptempl::data_map result;
|
|
result["name"] = registerVariable(variable.getExpressionVariable(), variable.isTransient());
|
|
if (variable.hasInitExpression()) {
|
|
result["init"] = asString(variable.getInitExpression().evaluateAsBool());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
cpptempl::data_map ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::generateBoundedIntegerVariable(storm::jani::BoundedIntegerVariable const& variable) {
|
|
cpptempl::data_map result;
|
|
|
|
int_fast64_t lowerBound = variable.getLowerBound().evaluateAsInt();
|
|
int_fast64_t upperBound = variable.getUpperBound().evaluateAsInt();
|
|
|
|
lowerBounds[variable.getExpressionVariable()] = lowerBound;
|
|
if (lowerBound != 0) {
|
|
lowerBoundShiftSubstitution[variable.getExpressionVariable()] = variable.getExpressionVariable() - model.getManager().integer(-lowerBound);
|
|
}
|
|
uint64_t range = static_cast<uint64_t>(upperBound - lowerBound + 1);
|
|
uint64_t numberOfBits = static_cast<uint64_t>(std::ceil(std::log2(range)));
|
|
|
|
result["name"] = registerVariable(variable.getExpressionVariable(), variable.isTransient());
|
|
result["original_name"] = variable.getExpressionVariable().getName();
|
|
result["numberOfBits"] = std::to_string(numberOfBits);
|
|
if (variable.hasInitExpression()) {
|
|
result["init"] = asString(variable.getInitExpression().evaluateAsInt() - lowerBound);
|
|
}
|
|
|
|
result["lower"] = asString(lowerBound);
|
|
result["upper"] = asString(upperBound);
|
|
|
|
return result;
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
cpptempl::data_map ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::generateUnboundedIntegerVariable(storm::jani::UnboundedIntegerVariable const& variable) {
|
|
cpptempl::data_map result;
|
|
|
|
result["name"] = registerVariable(variable.getExpressionVariable(), variable.isTransient());
|
|
if (variable.hasInitExpression()) {
|
|
result["init"] = asString(variable.getInitExpression().evaluateAsInt());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
cpptempl::data_map ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::generateRealVariable(storm::jani::RealVariable const& variable) {
|
|
cpptempl::data_map result;
|
|
|
|
result["name"] = registerVariable(variable.getExpressionVariable(), variable.isTransient());
|
|
realVariables.insert(variable.getExpressionVariable());
|
|
if (variable.hasInitExpression()) {
|
|
if (std::is_same<double, ValueType>::value) {
|
|
result["init"] = expressionTranslator.translate(variable.getInitExpression(), storm::expressions::ToCppTranslationOptions(variablePrefixes, variableToName, storm::expressions::ToCppTranslationMode::CastDouble));
|
|
} else if (std::is_same<storm::RationalNumber, ValueType>::value) {
|
|
result["init"] = expressionTranslator.translate(variable.getInitExpression(), storm::expressions::ToCppTranslationOptions(variablePrefixes, variableToName, storm::expressions::ToCppTranslationMode::CastRationalNumber));
|
|
} else if (std::is_same<storm::RationalFunction, ValueType>::value) {
|
|
result["init"] = expressionTranslator.translate(variable.getInitExpression(), storm::expressions::ToCppTranslationOptions(variablePrefixes, variableToName, storm::expressions::ToCppTranslationMode::CastRationalFunction));
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
cpptempl::data_map ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::generateLocationVariable(storm::jani::Automaton const& automaton) {
|
|
cpptempl::data_map result;
|
|
|
|
result["name"] = registerVariable(getLocationVariable(automaton), false);
|
|
result["numberOfBits"] = static_cast<uint64_t>(std::ceil(std::log2(automaton.getNumberOfLocations())));
|
|
|
|
return result;
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
void ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::generateVariables(cpptempl::data_map& modelData) {
|
|
cpptempl::data_list nonTransientBooleanVariables;
|
|
cpptempl::data_list transientBooleanVariables;
|
|
cpptempl::data_list nonTransientBoundedIntegerVariables;
|
|
cpptempl::data_list transientBoundedIntegerVariables;
|
|
cpptempl::data_list nonTransientUnboundedIntegerVariables;
|
|
cpptempl::data_list transientUnboundedIntegerVariables;
|
|
cpptempl::data_list nonTransientRealVariables;
|
|
cpptempl::data_list transientRealVariables;
|
|
cpptempl::data_list locationVariables;
|
|
|
|
for (auto const& variable : model.getGlobalVariables().getBooleanVariables()) {
|
|
cpptempl::data_map newBooleanVariable = generateBooleanVariable(variable.asBooleanVariable());
|
|
if (variable.isTransient()) {
|
|
transientBooleanVariables.push_back(newBooleanVariable);
|
|
} else {
|
|
nonTransientBooleanVariables.push_back(newBooleanVariable);
|
|
}
|
|
}
|
|
for (auto const& variable : model.getGlobalVariables().getBoundedIntegerVariables()) {
|
|
cpptempl::data_map newBoundedIntegerVariable = generateBoundedIntegerVariable(variable.asBoundedIntegerVariable());
|
|
if (variable.isTransient()) {
|
|
transientBoundedIntegerVariables.push_back(newBoundedIntegerVariable);
|
|
} else {
|
|
nonTransientBoundedIntegerVariables.push_back(newBoundedIntegerVariable);
|
|
}
|
|
}
|
|
for (auto const& variable : model.getGlobalVariables().getUnboundedIntegerVariables()) {
|
|
cpptempl::data_map newUnboundedIntegerVariable = generateUnboundedIntegerVariable(variable.asUnboundedIntegerVariable());
|
|
if (variable.isTransient()) {
|
|
transientUnboundedIntegerVariables.push_back(newUnboundedIntegerVariable);
|
|
} else {
|
|
nonTransientUnboundedIntegerVariables.push_back(newUnboundedIntegerVariable);
|
|
}
|
|
}
|
|
for (auto const& variable : model.getGlobalVariables().getRealVariables()) {
|
|
cpptempl::data_map newRealVariable = generateRealVariable(variable.asRealVariable());
|
|
if (variable.isTransient()) {
|
|
transientRealVariables.push_back(newRealVariable);
|
|
} else {
|
|
nonTransientRealVariables.push_back(newRealVariable);
|
|
}
|
|
}
|
|
for (auto const& automatonRef : parallelAutomata) {
|
|
storm::jani::Automaton const& automaton = automatonRef.get();
|
|
for (auto const& variable : automaton.getVariables().getBooleanVariables()) {
|
|
cpptempl::data_map newBooleanVariable = generateBooleanVariable(variable.asBooleanVariable());
|
|
if (variable.isTransient()) {
|
|
transientBooleanVariables.push_back(newBooleanVariable);
|
|
} else {
|
|
nonTransientBooleanVariables.push_back(newBooleanVariable);
|
|
}
|
|
}
|
|
for (auto const& variable : automaton.getVariables().getBoundedIntegerVariables()) {
|
|
cpptempl::data_map newBoundedIntegerVariable = generateBoundedIntegerVariable(variable.asBoundedIntegerVariable());
|
|
if (variable.isTransient()) {
|
|
transientBoundedIntegerVariables.push_back(newBoundedIntegerVariable);
|
|
} else {
|
|
nonTransientBoundedIntegerVariables.push_back(newBoundedIntegerVariable);
|
|
}
|
|
}
|
|
for (auto const& variable : automaton.getVariables().getUnboundedIntegerVariables()) {
|
|
cpptempl::data_map newUnboundedIntegerVariable = generateUnboundedIntegerVariable(variable.asUnboundedIntegerVariable());
|
|
if (variable.isTransient()) {
|
|
transientUnboundedIntegerVariables.push_back(newUnboundedIntegerVariable);
|
|
} else {
|
|
nonTransientUnboundedIntegerVariables.push_back(newUnboundedIntegerVariable);
|
|
}
|
|
}
|
|
for (auto const& variable : automaton.getVariables().getRealVariables()) {
|
|
cpptempl::data_map newRealVariable = generateRealVariable(variable.asRealVariable());
|
|
if (variable.isTransient()) {
|
|
transientRealVariables.push_back(newRealVariable);
|
|
} else {
|
|
nonTransientRealVariables.push_back(newRealVariable);
|
|
}
|
|
}
|
|
|
|
// Only generate a location variable if there is more than one location for the automaton.
|
|
if (automaton.getNumberOfLocations() > 1) {
|
|
locationVariables.push_back(generateLocationVariable(automaton));
|
|
}
|
|
}
|
|
|
|
cpptempl::data_map nonTransientVariables;
|
|
nonTransientVariables["boolean"] = cpptempl::make_data(nonTransientBooleanVariables);
|
|
nonTransientVariables["boundedInteger"] = cpptempl::make_data(nonTransientBoundedIntegerVariables);
|
|
nonTransientVariables["unboundedInteger"] = cpptempl::make_data(nonTransientUnboundedIntegerVariables);
|
|
nonTransientVariables["real"] = cpptempl::make_data(nonTransientRealVariables);
|
|
nonTransientVariables["locations"] = cpptempl::make_data(locationVariables);
|
|
modelData["nontransient_variables"] = nonTransientVariables;
|
|
|
|
cpptempl::data_map transientVariables;
|
|
transientVariables["boolean"] = cpptempl::make_data(transientBooleanVariables);
|
|
transientVariables["boundedInteger"] = cpptempl::make_data(transientBoundedIntegerVariables);
|
|
transientVariables["unboundedInteger"] = cpptempl::make_data(transientUnboundedIntegerVariables);
|
|
transientVariables["real"] = cpptempl::make_data(transientRealVariables);
|
|
modelData["transient_variables"] = transientVariables;
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
void ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::generateInitialStates(cpptempl::data_map& modelData) {
|
|
cpptempl::data_list initialStates;
|
|
|
|
// Prepare an SMT solver to enumerate all initial states.
|
|
storm::utility::solver::SmtSolverFactory factory;
|
|
std::unique_ptr<storm::solver::SmtSolver> solver = factory.create(model.getExpressionManager());
|
|
|
|
std::vector<storm::expressions::Expression> rangeExpressions = model.getAllRangeExpressions();
|
|
for (auto const& expression : rangeExpressions) {
|
|
solver->add(expression);
|
|
}
|
|
solver->add(model.getInitialStatesExpression(parallelAutomata));
|
|
|
|
// Proceed as long as the solver can still enumerate initial states.
|
|
while (solver->check() == storm::solver::SmtSolver::CheckResult::Sat) {
|
|
// Create fresh state.
|
|
cpptempl::data_list initialStateAssignment;
|
|
|
|
// Read variable assignment from the solution of the solver. Also, create an expression we can use to
|
|
// prevent the variable assignment from being enumerated again.
|
|
storm::expressions::Expression blockingExpression;
|
|
std::shared_ptr<storm::solver::SmtSolver::ModelReference> model = solver->getModel();
|
|
for (auto const& variable : this->model.getGlobalVariables().getBooleanVariables()) {
|
|
if (variable.isTransient()) {
|
|
continue;
|
|
}
|
|
|
|
storm::expressions::Variable const& expressionVariable = variable.getExpressionVariable();
|
|
bool variableValue = model->getBooleanValue(expressionVariable);
|
|
initialStateAssignment.push_back(generateAssignment(variable, variableValue));
|
|
|
|
storm::expressions::Expression localBlockingExpression = variableValue ? !expressionVariable : expressionVariable;
|
|
blockingExpression = blockingExpression.isInitialized() ? blockingExpression || localBlockingExpression : localBlockingExpression;
|
|
}
|
|
for (auto const& variable : this->model.getGlobalVariables().getBoundedIntegerVariables()) {
|
|
if (variable.isTransient()) {
|
|
continue;
|
|
}
|
|
|
|
storm::expressions::Variable const& expressionVariable = variable.getExpressionVariable();
|
|
int_fast64_t variableValue = model->getIntegerValue(expressionVariable);
|
|
initialStateAssignment.push_back(generateAssignment(variable, variableValue));
|
|
|
|
storm::expressions::Expression localBlockingExpression = expressionVariable != model->getManager().integer(variableValue);
|
|
blockingExpression = blockingExpression.isInitialized() ? blockingExpression || localBlockingExpression : localBlockingExpression;
|
|
}
|
|
for (auto const& automatonRef : parallelAutomata) {
|
|
storm::jani::Automaton const& automaton = automatonRef.get();
|
|
for (auto const& variable : automaton.getVariables().getBooleanVariables()) {
|
|
if (variable.isTransient()) {
|
|
continue;
|
|
}
|
|
|
|
storm::expressions::Variable const& expressionVariable = variable.getExpressionVariable();
|
|
bool variableValue = model->getBooleanValue(expressionVariable);
|
|
initialStateAssignment.push_back(generateAssignment(variable, variableValue));
|
|
|
|
storm::expressions::Expression localBlockingExpression = variableValue ? !expressionVariable : expressionVariable;
|
|
blockingExpression = blockingExpression.isInitialized() ? blockingExpression || localBlockingExpression : localBlockingExpression;
|
|
}
|
|
for (auto const& variable : automaton.getVariables().getBoundedIntegerVariables()) {
|
|
if (variable.isTransient()) {
|
|
continue;
|
|
}
|
|
|
|
storm::expressions::Variable const& expressionVariable = variable.getExpressionVariable();
|
|
int_fast64_t variableValue = model->getIntegerValue(expressionVariable);
|
|
initialStateAssignment.push_back(generateAssignment(variable, variableValue));
|
|
|
|
storm::expressions::Expression localBlockingExpression = expressionVariable != model->getManager().integer(variableValue);
|
|
blockingExpression = blockingExpression.isInitialized() ? blockingExpression || localBlockingExpression : localBlockingExpression;
|
|
}
|
|
}
|
|
|
|
// Gather iterators to the initial locations of all the automata.
|
|
std::vector<std::set<uint64_t>::const_iterator> initialLocationsIterators;
|
|
for (auto const& automaton : parallelAutomata) {
|
|
initialLocationsIterators.push_back(automaton.get().getInitialLocationIndices().cbegin());
|
|
}
|
|
|
|
// Now iterate through all combinations of initial locations.
|
|
while (true) {
|
|
cpptempl::data_list completeAssignment(initialStateAssignment);
|
|
|
|
for (uint64_t index = 0; index < initialLocationsIterators.size(); ++index) {
|
|
storm::jani::Automaton const& automaton = parallelAutomata[index].get();
|
|
if (automaton.getNumberOfLocations() > 1) {
|
|
completeAssignment.push_back(generateLocationAssignment(automaton, *initialLocationsIterators[index]));
|
|
}
|
|
}
|
|
initialStates.push_back(cpptempl::make_data(completeAssignment));
|
|
|
|
uint64_t index = 0;
|
|
for (; index < initialLocationsIterators.size(); ++index) {
|
|
++initialLocationsIterators[index];
|
|
if (initialLocationsIterators[index] == parallelAutomata[index].get().getInitialLocationIndices().cend()) {
|
|
initialLocationsIterators[index] = parallelAutomata[index].get().getInitialLocationIndices().cbegin();
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we are at the end, leave the loop.
|
|
if (index == initialLocationsIterators.size()) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Block the current initial state to search for the next one.
|
|
if (!blockingExpression.isInitialized()) {
|
|
break;
|
|
}
|
|
solver->add(blockingExpression);
|
|
}
|
|
|
|
modelData["initialStates"] = cpptempl::make_data(initialStates);
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
void ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::generateRewards(cpptempl::data_map& modelData) {
|
|
// Extract the reward models from the program based on the names we were given.
|
|
std::vector<storm::expressions::Variable> rewardVariables;
|
|
if (this->options.isBuildAllRewardModelsSet()) {
|
|
for (auto const& rewExpr : model.getAllRewardModelExpressions()) {
|
|
STORM_LOG_ERROR_COND(rewExpr.second.isVariable(), "The jit builder can not build the non-trivial reward expression '" << rewExpr.second << "'.");
|
|
rewardVariables.push_back(rewExpr.second.getBaseExpression().asVariableExpression().getVariable());
|
|
}
|
|
} else {
|
|
for (auto const& rewardModelName : this->options.getRewardModelNames()) {
|
|
auto const& rewExpr = model.getRewardModelExpression(rewardModelName);
|
|
STORM_LOG_ERROR_COND(rewExpr.isVariable(), "The jit builder can not build the non-trivial reward expression '" << rewExpr << "'.");
|
|
rewardVariables.push_back(rewExpr.getBaseExpression().asVariableExpression().getVariable());
|
|
}
|
|
}
|
|
// Sort the reward variables to match the order in the ordered assignments
|
|
std::sort(rewardVariables.begin(), rewardVariables.end());
|
|
|
|
std::vector<storm::builder::RewardModelInformation> rewardModels;
|
|
cpptempl::data_list rewards;
|
|
cpptempl::data_list locationRewards;
|
|
cpptempl::data_list edgeRewards;
|
|
cpptempl::data_list destinationRewards;
|
|
uint64_t rewardModelIndex = 0;
|
|
uint64_t stateActionRewardCount = 0;
|
|
for (auto const& variable : rewardVariables) {
|
|
bool hasLocationRewards = false;
|
|
bool hasEdgeRewards = false;
|
|
bool hasDestinationRewards = false;
|
|
|
|
for (auto const& automatonRef : parallelAutomata) {
|
|
storm::jani::Automaton const& automaton = automatonRef.get();
|
|
|
|
for (auto const& location : automaton.getLocations()) {
|
|
for (auto const& assignment : location.getAssignments()) {
|
|
if (assignment.getExpressionVariable() == variable) {
|
|
hasLocationRewards = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto const& edge : automaton.getEdges()) {
|
|
for (auto const& assignment : edge.getAssignments()) {
|
|
if (assignment.getExpressionVariable() == variable) {
|
|
hasEdgeRewards = true;
|
|
}
|
|
}
|
|
|
|
for (auto const& destination : edge.getDestinations()) {
|
|
for (auto const& assignment : destination.getOrderedAssignments()) {
|
|
if (assignment.getExpressionVariable() == variable) {
|
|
hasDestinationRewards = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
rewardModels.emplace_back(variable.getName(), hasLocationRewards, hasEdgeRewards || hasDestinationRewards, false);
|
|
|
|
if (hasEdgeRewards || hasDestinationRewards) {
|
|
++stateActionRewardCount;
|
|
}
|
|
|
|
cpptempl::data_map reward;
|
|
reward["name"] = variable.getName();
|
|
reward["location_rewards"] = asString(hasLocationRewards);
|
|
reward["edge_rewards"] = asString(hasEdgeRewards);
|
|
reward["destination_rewards"] = asString(hasDestinationRewards);
|
|
rewards.push_back(reward);
|
|
|
|
if (hasLocationRewards) {
|
|
cpptempl::data_map locationReward;
|
|
locationReward["variable"] = variable.getName() + JIT_VARIABLE_EXTENSION;
|
|
locationRewards.push_back(locationReward);
|
|
}
|
|
if (hasEdgeRewards) {
|
|
cpptempl::data_map edgeReward;
|
|
edgeReward["variable"] = variable.getName() + JIT_VARIABLE_EXTENSION;
|
|
edgeReward["index"] = asString(rewardModelIndex);
|
|
edgeRewards.push_back(edgeReward);
|
|
}
|
|
if (hasDestinationRewards) {
|
|
cpptempl::data_map destinationReward;
|
|
destinationReward["index"] = asString(rewardModelIndex);
|
|
destinationReward["variable"] = variable.getName() + JIT_VARIABLE_EXTENSION;
|
|
destinationRewards.push_back(destinationReward);
|
|
}
|
|
++rewardModelIndex;
|
|
}
|
|
|
|
modelData["location_rewards"] = cpptempl::make_data(locationRewards);
|
|
modelData["edge_rewards"] = cpptempl::make_data(edgeRewards);
|
|
modelData["destination_rewards"] = cpptempl::make_data(destinationRewards);
|
|
modelData["rewards"] = cpptempl::make_data(rewards);
|
|
modelData["edge_destination_rewards_count"] = asString(stateActionRewardCount);
|
|
}
|
|
|
|
static std::ostream& indent(std::ostream& out, uint64_t indentLevel) {
|
|
for (uint64_t i = 0; i < indentLevel; ++i) {
|
|
out << "\t";
|
|
}
|
|
return out;
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
cpptempl::data_map ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::generateSynchronizationVector(cpptempl::data_map& modelData, storm::jani::ParallelComposition const& parallelComposition, storm::jani::SynchronizationVector const& synchronizationVector, uint64_t synchronizationVectorIndex) {
|
|
std::stringstream vectorSource;
|
|
uint64_t numberOfActionInputs = synchronizationVector.getNumberOfActionInputs();
|
|
|
|
// First, we check whether we need to generate code for a) different assignment levels and b) transient variables.
|
|
uint64_t position = 0;
|
|
int_fast64_t lowestLevel;
|
|
int_fast64_t highestLevel;
|
|
bool firstDestination = true;
|
|
bool generateTransient = false;
|
|
for (auto const& inputActionName : synchronizationVector.getInput()) {
|
|
if (!storm::jani::SynchronizationVector::isNoActionInput(inputActionName)) {
|
|
storm::jani::Automaton const& automaton = model.getAutomaton(parallelComposition.getSubcomposition(position).asAutomatonComposition().getAutomatonName());
|
|
uint64_t actionIndex = model.getActionIndex(inputActionName);
|
|
for (auto const& edge : automaton.getEdges()) {
|
|
if (edge.getActionIndex() == actionIndex) {
|
|
for (auto const& destination : edge.getDestinations()) {
|
|
if (!destination.getOrderedAssignments().empty()) {
|
|
if (firstDestination) {
|
|
lowestLevel = destination.getOrderedAssignments().getLowestLevel();
|
|
highestLevel = destination.getOrderedAssignments().getHighestLevel();
|
|
firstDestination = false;
|
|
} else {
|
|
lowestLevel = std::min(lowestLevel, destination.getOrderedAssignments().getLowestLevel());
|
|
highestLevel = std::max(highestLevel, destination.getOrderedAssignments().getHighestLevel());
|
|
}
|
|
}
|
|
if (!generateTransient) {
|
|
for (auto const& assignment : destination.getOrderedAssignments()) {
|
|
if (assignment.isTransient()) {
|
|
generateTransient = true;
|
|
}
|
|
std::set<storm::expressions::Variable> usedVariables = assignment.getAssignedExpression().getVariables();
|
|
for (auto const& variable : usedVariables) {
|
|
if (transientVariables.find(variable) != transientVariables.end()) {
|
|
generateTransient = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
++position;
|
|
}
|
|
bool generateLevelCode = lowestLevel != highestLevel;
|
|
|
|
|
|
uint64_t indentLevel = 4;
|
|
indent(vectorSource, indentLevel - 4) << "void performSynchronizedDestinations_" << synchronizationVectorIndex << "(StateType const& in, StateBehaviour<IndexType, ValueType>& behaviour, StateSet<StateType>& statesToExplore, ";
|
|
for (uint64_t index = 0; index < numberOfActionInputs; ++index) {
|
|
vectorSource << "Destination const& destination" << index << ", ";
|
|
}
|
|
if (generateLevelCode) {
|
|
vectorSource << "int64_t lowestLevel, int64_t highestLevel, ";
|
|
}
|
|
vectorSource << "ValueType const& rate, Choice<IndexType, ValueType>& choice) {" << std::endl;
|
|
if (options.isExplorationChecksSet()) {
|
|
indent(vectorSource, indentLevel + 1) << "VariableWrites variableWrites;" << std::endl;
|
|
}
|
|
indent(vectorSource, indentLevel + 1) << "StateType out(in);" << std::endl;
|
|
indent(vectorSource, indentLevel + 1) << "TransientVariables transientIn;" << std::endl;
|
|
indent(vectorSource, indentLevel + 1) << "TransientVariables transientOut;" << std::endl;
|
|
|
|
if (generateLevelCode) {
|
|
indent(vectorSource, indentLevel + 1) << "for (int64_t level = lowestLevel; level <= highestLevel; ++level) {" << std::endl;
|
|
++indentLevel;
|
|
}
|
|
for (uint64_t index = 0; index < numberOfActionInputs; ++index) {
|
|
indent(vectorSource, indentLevel + 1) << "destination" << index << ".perform(";
|
|
if (generateLevelCode) {
|
|
vectorSource << "level, ";
|
|
}
|
|
vectorSource << "in, out, transientIn, transientOut";
|
|
if (options.isExplorationChecksSet()) {
|
|
vectorSource << ", variableWrites";
|
|
}
|
|
vectorSource << ");" << std::endl;
|
|
}
|
|
if (generateLevelCode) {
|
|
--indentLevel;
|
|
indent(vectorSource, indentLevel + 1) << "}" << std::endl;
|
|
}
|
|
indent(vectorSource, indentLevel + 1) << "IndexType outStateIndex = getOrAddIndex(out, statesToExplore);" << std::endl;
|
|
indent(vectorSource, indentLevel + 1) << "ValueType probability = ";
|
|
for (uint64_t index = 0; index < numberOfActionInputs; ++index) {
|
|
vectorSource << "destination" << index << ".probability(in)";
|
|
if (index + 1 < numberOfActionInputs) {
|
|
vectorSource << " * ";
|
|
}
|
|
}
|
|
vectorSource << ";" << std::endl;
|
|
indent(vectorSource, indentLevel + 1) << "ValueType value = rate * probability;" << std::endl;
|
|
indent(vectorSource, indentLevel + 1) << "choice.add(outStateIndex, value);" << std::endl;
|
|
|
|
std::stringstream tmp;
|
|
indent(tmp, indentLevel + 1) << "{% for reward in destination_rewards %}choice.addReward({$reward.index}, probability * transientOut.{$reward.variable});" << std::endl;
|
|
indent(tmp, indentLevel + 1) << "{% endfor %}" << std::endl;
|
|
vectorSource << cpptempl::parse(tmp.str(), modelData);
|
|
indent(vectorSource, indentLevel) << "}" << std::endl << std::endl;
|
|
|
|
indent(vectorSource, indentLevel) << "void performSynchronizedDestinations_" << synchronizationVectorIndex << "(StateType const& in, StateBehaviour<IndexType, ValueType>& behaviour, StateSet<StateType>& statesToExplore, ";
|
|
for (uint64_t index = 0; index < numberOfActionInputs; ++index) {
|
|
vectorSource << "Edge const& edge" << index << ", ";
|
|
}
|
|
vectorSource << "ValueType const& rate, Choice<IndexType, ValueType>& choice) {" << std::endl;
|
|
if (generateLevelCode) {
|
|
indent(vectorSource, indentLevel + 1) << "int64_t lowestLevel; int64_t highestLevel;";
|
|
}
|
|
for (uint64_t index = 0; index < numberOfActionInputs; ++index) {
|
|
indent(vectorSource, indentLevel + 1 + index) << "for (auto const& destination" << index << " : edge" << index << ") {" << std::endl;
|
|
if (generateLevelCode) {
|
|
if (index == 0) {
|
|
indent(vectorSource, indentLevel + 2 + index) << "lowestLevel = destination" << index << ".lowestLevel();" << std::endl;
|
|
indent(vectorSource, indentLevel + 2 + index) << "highestLevel = destination" << index << ".highestLevel();" << std::endl;
|
|
} else {
|
|
indent(vectorSource, indentLevel + 2 + index) << "lowestLevel = std::min(lowestLevel, destination" << index << ".lowestLevel());" << std::endl;
|
|
indent(vectorSource, indentLevel + 2 + index) << "highestLevel = std::max(highestLevel, destination" << index << ".highestLevel());" << std::endl;
|
|
}
|
|
}
|
|
}
|
|
indent(vectorSource, indentLevel + 1 + numberOfActionInputs) << "performSynchronizedDestinations_" << synchronizationVectorIndex << "(in, behaviour, statesToExplore, ";
|
|
for (uint64_t index = 0; index < numberOfActionInputs; ++index) {
|
|
vectorSource << "destination" << index << ", ";
|
|
}
|
|
if (generateLevelCode) {
|
|
vectorSource << "lowestLevel, highestLevel, ";
|
|
}
|
|
vectorSource << "rate, choice);" << std::endl;
|
|
for (uint64_t index = 0; index < numberOfActionInputs; ++index) {
|
|
indent(vectorSource, indentLevel + numberOfActionInputs - index) << "}" << std::endl;
|
|
}
|
|
indent(vectorSource, indentLevel) << "}" << std::endl << std::endl;
|
|
|
|
for (uint64_t index = 0; index < numberOfActionInputs; ++index) {
|
|
indent(vectorSource, indentLevel) << "void performSynchronizedEdges_" << synchronizationVectorIndex << "_" << index << "(StateType const& in, std::vector<std::vector<std::reference_wrapper<Edge const>>> const& edges, StateBehaviour<IndexType, ValueType>& behaviour, StateSet<StateType>& statesToExplore";
|
|
if (index > 0) {
|
|
vectorSource << ", ";
|
|
}
|
|
for (uint64_t innerIndex = 0; innerIndex < index; ++innerIndex) {
|
|
vectorSource << "Edge const& edge" << innerIndex;
|
|
if (innerIndex + 1 < index) {
|
|
vectorSource << ", ";
|
|
}
|
|
}
|
|
if (index > 0) {
|
|
vectorSource << ", TransientVariables const& transientIn, TransientVariables& transientOut";
|
|
if (options.isExplorationChecksSet()) {
|
|
vectorSource << ", VariableWrites& variableWrites";
|
|
}
|
|
}
|
|
vectorSource << ", ValueType const& rate) {" << std::endl;
|
|
if (index == 0) {
|
|
if (options.isExplorationChecksSet()) {
|
|
indent(vectorSource, indentLevel + 1) << "VariableWrites variableWrites;" << std::endl;
|
|
}
|
|
indent(vectorSource, indentLevel + 1) << "TransientVariables transientIn;" << std::endl;
|
|
indent(vectorSource, indentLevel + 1) << "TransientVariables transientOut;" << std::endl;
|
|
}
|
|
indent(vectorSource, indentLevel + 1) << "for (auto const& edge" << index << " : edges[" << index << "]) {" << std::endl;
|
|
indent(vectorSource, indentLevel + 2) << "edge" << index << ".get().perform(in, transientIn, transientOut";
|
|
if (options.isExplorationChecksSet()) {
|
|
vectorSource << ", variableWrites";
|
|
}
|
|
vectorSource << ");" << std::endl;
|
|
if (index + 1 < numberOfActionInputs) {
|
|
indent(vectorSource, indentLevel + 2) << "performSynchronizedEdges_" << synchronizationVectorIndex << "_" << (index + 1) << "(in, edges, behaviour, statesToExplore, ";
|
|
for (uint64_t innerIndex = 0; innerIndex <= index; ++innerIndex) {
|
|
vectorSource << "edge" << innerIndex << ", ";
|
|
}
|
|
vectorSource << "transientIn, transientOut, ";
|
|
if (options.isExplorationChecksSet()) {
|
|
vectorSource << "variableWrites, ";
|
|
}
|
|
vectorSource << "rate * edge" << index << ".get().rate(in));" << std::endl;
|
|
} else {
|
|
indent(vectorSource, indentLevel + 2) << "Choice<IndexType, ValueType>& choice = behaviour.addChoice();" << std::endl;
|
|
|
|
std::stringstream tmp;
|
|
indent(tmp, indentLevel + 2) << "choice.resizeRewards({$edge_destination_rewards_count});" << std::endl;
|
|
indent(tmp, indentLevel + 2) << "{% for reward in edge_rewards %}choice.addReward({$reward.index}, transientOut.{$reward.variable});" << std::endl;
|
|
indent(tmp, indentLevel + 2) << "{% endfor %}" << std::endl;
|
|
|
|
vectorSource << cpptempl::parse(tmp.str(), modelData);
|
|
|
|
indent(vectorSource, indentLevel + 2) << "performSynchronizedDestinations_" << synchronizationVectorIndex << "(in, behaviour, statesToExplore, ";
|
|
for (uint64_t innerIndex = 0; innerIndex <= index; ++innerIndex) {
|
|
vectorSource << "edge" << innerIndex << ", ";
|
|
}
|
|
vectorSource << "rate * edge" << index << ".get().rate(in), choice);" << std::endl;
|
|
|
|
}
|
|
indent(vectorSource, indentLevel + 1) << "}" << std::endl;
|
|
indent(vectorSource, indentLevel) << "}" << std::endl << std::endl;
|
|
}
|
|
|
|
indent(vectorSource, indentLevel) << "void get_edges_" << synchronizationVectorIndex << "(StateType const& state, TransientVariables const& transientIn, std::vector<std::reference_wrapper<Edge const>>& edges, uint64_t position) {" << std::endl;
|
|
position = 0;
|
|
uint64_t participatingPosition = 0;
|
|
for (auto const& inputActionName : synchronizationVector.getInput()) {
|
|
if (!storm::jani::SynchronizationVector::isNoActionInput(inputActionName)) {
|
|
indent(vectorSource, indentLevel + 1) << "if (position == " << participatingPosition << ") {" << std::endl;
|
|
|
|
storm::jani::Automaton const& automaton = model.getAutomaton(parallelComposition.getSubcomposition(position).asAutomatonComposition().getAutomatonName());
|
|
uint64_t actionIndex = model.getActionIndex(inputActionName);
|
|
uint64_t edgeIndex = 0;
|
|
for (auto const& edge : automaton.getEdges()) {
|
|
if (edge.getActionIndex() == actionIndex) {
|
|
std::string edgeName = automaton.getName() + "_" + std::to_string(edgeIndex);
|
|
indent(vectorSource, indentLevel + 2) << "if (edge_enabled_" << edgeName << "(state, transientIn)) {" << std::endl;
|
|
indent(vectorSource, indentLevel + 3) << "edges.emplace_back(edge_" << edgeName << ");" << std::endl;
|
|
indent(vectorSource, indentLevel + 2) << "}" << std::endl;
|
|
}
|
|
++edgeIndex;
|
|
}
|
|
|
|
indent(vectorSource, indentLevel + 1) << "}" << std::endl;
|
|
++participatingPosition;
|
|
}
|
|
++position;
|
|
}
|
|
indent(vectorSource, indentLevel) << "}" << std::endl << std::endl;
|
|
|
|
indent(vectorSource, indentLevel) << "void exploreSynchronizationVector_" << synchronizationVectorIndex << "(StateType const& state, TransientVariables const& transientIn, StateBehaviour<IndexType, ValueType>& behaviour, StateSet<StateType>& statesToExplore) {" << std::endl;
|
|
indent(vectorSource, indentLevel + 1) << "#ifndef NDEBUG" << std::endl;
|
|
indent(vectorSource, indentLevel + 1) << "std::cout << \"Exploring synchronization vector " << synchronizationVectorIndex << ".\" << std::endl;" << std::endl;
|
|
indent(vectorSource, indentLevel + 1) << "#endif" << std::endl;
|
|
indent(vectorSource, indentLevel + 1) << "std::vector<std::vector<std::reference_wrapper<Edge const>>> edges(" << synchronizationVector.getNumberOfActionInputs() << ");" << std::endl;
|
|
|
|
participatingPosition = 0;
|
|
for (auto const& input : synchronizationVector.getInput()) {
|
|
if (!storm::jani::SynchronizationVector::isNoActionInput(input)) {
|
|
indent(vectorSource, indentLevel + 1) << "get_edges_" << synchronizationVectorIndex << "(state, transientIn, edges[" << participatingPosition << "], " << participatingPosition << ");" << std::endl;
|
|
indent(vectorSource, indentLevel + 1) << "if (edges[" << participatingPosition << "].empty()) {" << std::endl;
|
|
indent(vectorSource, indentLevel + 2) << "return;" << std::endl;
|
|
indent(vectorSource, indentLevel + 1) << "}" << std::endl;
|
|
++participatingPosition;
|
|
}
|
|
}
|
|
indent(vectorSource, indentLevel + 1) << "performSynchronizedEdges_" << synchronizationVectorIndex << "_0(state, edges, behaviour, statesToExplore, storm::utility::one<ValueType>());" << std::endl;
|
|
indent(vectorSource, indentLevel) << "}" << std::endl << std::endl;
|
|
|
|
cpptempl::data_map vector;
|
|
vector["functions"] = vectorSource.str();
|
|
vector["index"] = asString(synchronizationVectorIndex);
|
|
return vector;
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
void ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::generateEdges(cpptempl::data_map& modelData) {
|
|
STORM_LOG_THROW(model.hasStandardCompliantComposition(), storm::exceptions::WrongFormatException, "Model builder only supports non-nested parallel compositions.");
|
|
|
|
cpptempl::data_list nonSynchronizingEdges;
|
|
cpptempl::data_list synchronizingEdges;
|
|
cpptempl::data_list vectors;
|
|
|
|
storm::jani::Composition const& topLevelComposition = model.getSystemComposition();
|
|
if (topLevelComposition.isAutomatonComposition()) {
|
|
storm::jani::Automaton const& automaton = model.getAutomaton(topLevelComposition.asAutomatonComposition().getAutomatonName());
|
|
uint64_t edgeIndex = 0;
|
|
for (auto const& edge : automaton.getEdges()) {
|
|
nonSynchronizingEdges.push_back(generateEdge(automaton, edgeIndex, edge));
|
|
++edgeIndex;
|
|
}
|
|
} else {
|
|
storm::jani::ParallelComposition const& parallelComposition = topLevelComposition.asParallelComposition();
|
|
|
|
std::vector<std::set<uint64_t>> synchronizingActions(parallelComposition.getNumberOfSubcompositions());
|
|
uint64_t synchronizationVectorIndex = 0;
|
|
for (auto const& synchronizationVector : parallelComposition.getSynchronizationVectors()) {
|
|
// If the synchronization vector has at most one action input, there is no synchronization going on.
|
|
if (synchronizationVector.getNumberOfActionInputs() <= 1) {
|
|
continue;
|
|
}
|
|
|
|
bool createVector = true;
|
|
uint64_t position = 0;
|
|
for (auto const& inputActionName : synchronizationVector.getInput()) {
|
|
if (!storm::jani::SynchronizationVector::isNoActionInput(inputActionName)) {
|
|
uint64_t actionIndex = model.getActionIndex(inputActionName);
|
|
synchronizingActions[position].insert(actionIndex);
|
|
|
|
storm::jani::Automaton const& automaton = model.getAutomaton(parallelComposition.getSubcomposition(position).asAutomatonComposition().getAutomatonName());
|
|
bool hasParticipatingEdge = false;
|
|
for (auto const& edge : automaton.getEdges()) {
|
|
if (edge.getActionIndex() == actionIndex) {
|
|
hasParticipatingEdge = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!hasParticipatingEdge) {
|
|
createVector = false;
|
|
}
|
|
}
|
|
++position;
|
|
}
|
|
|
|
if (createVector) {
|
|
cpptempl::data_map vector = generateSynchronizationVector(modelData, parallelComposition, synchronizationVector, synchronizationVectorIndex);
|
|
vectors.push_back(vector);
|
|
}
|
|
++synchronizationVectorIndex;
|
|
}
|
|
|
|
uint64_t position = 0;
|
|
for (auto const& composition : parallelComposition.getSubcompositions()) {
|
|
storm::jani::Automaton const& automaton = model.getAutomaton(composition->asAutomatonComposition().getAutomatonName());
|
|
|
|
// Add all edges with an action index that is not mentioned in any synchronization vector as
|
|
// non-synchronizing edges.
|
|
uint64_t edgeIndex = 0;
|
|
for (auto const& edge : automaton.getEdges()) {
|
|
if (synchronizingActions[position].find(edge.getActionIndex()) != synchronizingActions[position].end()) {
|
|
synchronizingEdges.push_back(generateEdge(automaton, edgeIndex, edge));
|
|
} else {
|
|
nonSynchronizingEdges.push_back(generateEdge(automaton, edgeIndex, edge));
|
|
}
|
|
++edgeIndex;
|
|
}
|
|
|
|
++position;
|
|
}
|
|
}
|
|
|
|
modelData["nonsynch_edges"] = cpptempl::make_data(nonSynchronizingEdges);
|
|
modelData["synch_edges"] = cpptempl::make_data(synchronizingEdges);
|
|
modelData["synch_vectors"] = cpptempl::make_data(vectors);
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
cpptempl::data_map ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::generateEdge(storm::jani::Automaton const& automaton, uint64_t edgeIndex, storm::jani::Edge const& edge) {
|
|
cpptempl::data_map edgeData;
|
|
|
|
std::set<storm::expressions::Variable> transientVariablesInEdge;
|
|
cpptempl::data_list edgeAssignments;
|
|
for (auto const& assignment : edge.getAssignments()) {
|
|
transientVariablesInEdge.insert(assignment.getExpressionVariable());
|
|
std::set<storm::expressions::Variable> usedVariables = assignment.getAssignedExpression().getVariables();
|
|
for (auto const& variable : usedVariables) {
|
|
if (transientVariables.find(variable) != transientVariables.end()) {
|
|
transientVariablesInEdge.insert(variable);
|
|
}
|
|
}
|
|
edgeAssignments.push_back(generateAssignment(assignment));
|
|
}
|
|
|
|
cpptempl::data_list destinations;
|
|
uint64_t destinationIndex = 0;
|
|
std::set<storm::expressions::Variable> transientVariablesInDestinations;
|
|
for (auto const& destination : edge.getDestinations()) {
|
|
destinations.push_back(generateDestination(automaton, destinationIndex, destination));
|
|
|
|
for (auto const& assignment : destination.getOrderedAssignments().getAllAssignments()) {
|
|
if (assignment.isTransient()) {
|
|
transientVariablesInDestinations.insert(assignment.getExpressionVariable());
|
|
}
|
|
std::set<storm::expressions::Variable> usedVariables = assignment.getAssignedExpression().getVariables();
|
|
for (auto const& variable : usedVariables) {
|
|
if (transientVariables.find(variable) != transientVariables.end()) {
|
|
transientVariablesInDestinations.insert(variable);
|
|
}
|
|
}
|
|
}
|
|
|
|
++destinationIndex;
|
|
}
|
|
|
|
if (automaton.getNumberOfLocations() > 1) {
|
|
edgeData["guard"] = expressionTranslator.translate(shiftVariablesWrtLowerBound(edge.getGuard()) && getLocationVariable(automaton) == model.getManager().integer(edge.getSourceLocationIndex()), storm::expressions::ToCppTranslationOptions(variablePrefixes, variableToName));
|
|
} else {
|
|
edgeData["guard"] = expressionTranslator.translate(shiftVariablesWrtLowerBound(edge.getGuard()), storm::expressions::ToCppTranslationOptions(variablePrefixes, variableToName));
|
|
}
|
|
edgeData["destinations"] = cpptempl::make_data(destinations);
|
|
edgeData["name"] = automaton.getName() + "_" + std::to_string(edgeIndex);
|
|
edgeData["transient_assignments"] = cpptempl::make_data(edgeAssignments);
|
|
edgeData["markovian"] = asString(edge.hasRate());
|
|
if (edge.hasRate()) {
|
|
if (std::is_same<double, ValueType>::value) {
|
|
edgeData["rate"] = expressionTranslator.translate(shiftVariablesWrtLowerBound(edge.getRate()), storm::expressions::ToCppTranslationOptions(variablePrefixes, variableToName, storm::expressions::ToCppTranslationMode::CastDouble));
|
|
} else if (std::is_same<storm::RationalNumber, ValueType>::value) {
|
|
edgeData["rate"] = expressionTranslator.translate(shiftVariablesWrtLowerBound(edge.getRate()), storm::expressions::ToCppTranslationOptions(variablePrefixes, variableToName, storm::expressions::ToCppTranslationMode::CastRationalNumber));
|
|
} else if (std::is_same<storm::RationalFunction, ValueType>::value) {
|
|
edgeData["rate"] = expressionTranslator.translate(shiftVariablesWrtLowerBound(edge.getRate()), storm::expressions::ToCppTranslationOptions(variablePrefixes, variableToName, storm::expressions::ToCppTranslationMode::CastRationalFunction));
|
|
}
|
|
} else {
|
|
edgeData["rate"] = std::string("storm::utility::one<ValueType>()");
|
|
}
|
|
|
|
cpptempl::data_list transientVariablesInDestinationsData;
|
|
for (auto const& variable : transientVariablesInDestinations) {
|
|
transientVariablesInDestinationsData.push_back(getVariableName(variable));
|
|
}
|
|
edgeData["transient_variables_in_destinations"] = cpptempl::make_data(transientVariablesInDestinationsData);
|
|
cpptempl::data_list transientVariablesInEdgeData;
|
|
for (auto const& variable : transientVariablesInEdge) {
|
|
transientVariablesInEdgeData.push_back(getVariableName(variable));
|
|
}
|
|
edgeData["transient_variables_in_edge"] = cpptempl::make_data(transientVariablesInEdgeData);
|
|
return edgeData;
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
cpptempl::data_map ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::generateDestination(storm::jani::Automaton const& automaton, uint64_t destinationIndex, storm::jani::EdgeDestination const& destination) {
|
|
cpptempl::data_map destinationData;
|
|
|
|
cpptempl::data_list levels = generateLevels(automaton, destination.getLocationIndex(), destination.getOrderedAssignments());
|
|
destinationData["name"] = asString(destinationIndex);
|
|
destinationData["levels"] = cpptempl::make_data(levels);
|
|
storm::expressions::Expression shiftedProbabilityExpression = shiftVariablesWrtLowerBound(destination.getProbability());
|
|
if (std::is_same<double, ValueType>::value) {
|
|
destinationData["probability"] = expressionTranslator.translate(shiftedProbabilityExpression, storm::expressions::ToCppTranslationOptions(variablePrefixes, variableToName, storm::expressions::ToCppTranslationMode::CastDouble));
|
|
} else if (std::is_same<storm::RationalNumber, ValueType>::value) {
|
|
destinationData["probability"] = expressionTranslator.translate(shiftedProbabilityExpression, storm::expressions::ToCppTranslationOptions(variablePrefixes, variableToName, storm::expressions::ToCppTranslationMode::CastRationalNumber));
|
|
} else if (std::is_same<storm::RationalFunction, ValueType>::value) {
|
|
destinationData["probability"] = expressionTranslator.translate(shiftedProbabilityExpression, storm::expressions::ToCppTranslationOptions(variablePrefixes, variableToName, storm::expressions::ToCppTranslationMode::CastRationalFunction));
|
|
}
|
|
if (destination.getOrderedAssignments().empty()) {
|
|
destinationData["lowestLevel"] = "0";
|
|
destinationData["highestLevel"] = "0";
|
|
} else {
|
|
destinationData["lowestLevel"] = asString(destination.getOrderedAssignments().getLowestLevel());
|
|
destinationData["highestLevel"] = asString(destination.getOrderedAssignments().getHighestLevel());
|
|
}
|
|
|
|
return destinationData;
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
cpptempl::data_list ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::generateLevels(storm::jani::Automaton const& automaton, uint64_t destinationLocationIndex, storm::jani::OrderedAssignments const& orderedAssignments) {
|
|
cpptempl::data_list levels;
|
|
|
|
auto const& assignments = orderedAssignments.getAllAssignments();
|
|
if (!assignments.empty()) {
|
|
int64_t currentLevel = assignments.front().getLevel();
|
|
|
|
cpptempl::data_list nonTransientAssignmentData;
|
|
cpptempl::data_list transientAssignmentData;
|
|
for (auto const& assignment : assignments) {
|
|
if (assignment.getLevel() != currentLevel) {
|
|
cpptempl::data_map level;
|
|
level["non_transient_assignments"] = cpptempl::make_data(nonTransientAssignmentData);
|
|
level["transient_assignments"] = cpptempl::make_data(transientAssignmentData);
|
|
level["index"] = asString(currentLevel);
|
|
levels.push_back(level);
|
|
|
|
nonTransientAssignmentData = cpptempl::data_list();
|
|
transientAssignmentData = cpptempl::data_list();
|
|
currentLevel = assignment.getLevel();
|
|
}
|
|
|
|
if (assignment.isTransient()) {
|
|
transientAssignmentData.push_back(generateAssignment(assignment));
|
|
} else {
|
|
nonTransientAssignmentData.push_back(generateAssignment(assignment));
|
|
}
|
|
}
|
|
|
|
// Close the last (open) level.
|
|
cpptempl::data_map level;
|
|
if (automaton.getNumberOfLocations() > 1) {
|
|
nonTransientAssignmentData.push_back(generateLocationAssignment(automaton, destinationLocationIndex));
|
|
}
|
|
level["non_transient_assignments"] = cpptempl::make_data(nonTransientAssignmentData);
|
|
level["transient_assignments"] = cpptempl::make_data(transientAssignmentData);
|
|
level["index"] = asString(currentLevel);
|
|
levels.push_back(level);
|
|
} else if (automaton.getNumberOfLocations() > 1) {
|
|
// Create (an otherwise) empty level to perform the location assignment.
|
|
cpptempl::data_map level;
|
|
cpptempl::data_list nonTransientAssignmentData;
|
|
cpptempl::data_list transientAssignmentData;
|
|
nonTransientAssignmentData.push_back(generateLocationAssignment(automaton, destinationLocationIndex));
|
|
level["non_transient_assignments"] = cpptempl::make_data(nonTransientAssignmentData);
|
|
level["transient_assignments"] = cpptempl::make_data(transientAssignmentData);
|
|
level["index"] = asString(0);
|
|
levels.push_back(level);
|
|
}
|
|
|
|
return levels;
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
template <typename ValueTypePrime>
|
|
cpptempl::data_map ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::generateAssignment(storm::jani::Variable const& variable, ValueTypePrime value) const {
|
|
cpptempl::data_map result;
|
|
result["variable"] = getVariableName(variable.getExpressionVariable());
|
|
|
|
// Check if the variable has a non-zero lower bound and, if so, shift it appropriately.
|
|
auto it = lowerBounds.find(variable.getExpressionVariable());
|
|
if (it != lowerBounds.end()) {
|
|
result["value"] = asString(value) + " + " + asString(-it->second);
|
|
} else {
|
|
result["value"] = asString(value);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
cpptempl::data_map ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::generateLocationAssignment(storm::jani::Automaton const& automaton, uint64_t value) const {
|
|
cpptempl::data_map result;
|
|
result["variable"] = getVariableName(getLocationVariable(automaton));
|
|
result["value"] = asString(value);
|
|
return result;
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
cpptempl::data_map ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::generateAssignment(storm::jani::Assignment const& assignment) {
|
|
cpptempl::data_map result;
|
|
result["variable"] = getVariableName(assignment.getExpressionVariable());
|
|
auto lowerBoundIt = lowerBounds.find(assignment.getExpressionVariable());
|
|
|
|
storm::expressions::ToCppTranslationOptions options(variablePrefixes, variableToName);
|
|
if (realVariables.find(assignment.getExpressionVariable()) != realVariables.end()) {
|
|
if (std::is_same<double, ValueType>::value) {
|
|
options = storm::expressions::ToCppTranslationOptions(variablePrefixes, variableToName, storm::expressions::ToCppTranslationMode::CastDouble);
|
|
} else if (std::is_same<storm::RationalNumber, ValueType>::value) {
|
|
options = storm::expressions::ToCppTranslationOptions(variablePrefixes, variableToName, storm::expressions::ToCppTranslationMode::CastRationalNumber);
|
|
} else if (std::is_same<storm::RationalFunction, ValueType>::value) {
|
|
options = storm::expressions::ToCppTranslationOptions(variablePrefixes, variableToName, storm::expressions::ToCppTranslationMode::CastRationalFunction);
|
|
}
|
|
}
|
|
|
|
if (lowerBoundIt != lowerBounds.end()) {
|
|
result["value"] = expressionTranslator.translate(shiftVariablesWrtLowerBound(assignment.getAssignedExpression()) + model.getManager().integer(-lowerBoundIt->second), options);
|
|
} else {
|
|
result["value"] = expressionTranslator.translate(shiftVariablesWrtLowerBound(assignment.getAssignedExpression()), options);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
void ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::generateLocations(cpptempl::data_map& modelData) {
|
|
cpptempl::data_list locations;
|
|
|
|
for (auto const& automatonRef : parallelAutomata) {
|
|
storm::jani::Automaton const& automaton = automatonRef.get();
|
|
cpptempl::data_map locationData;
|
|
uint64_t locationIndex = 0;
|
|
for (auto const& location : automaton.getLocations()) {
|
|
cpptempl::data_list assignments;
|
|
for (auto const& assignment : location.getAssignments()) {
|
|
assignments.push_back(generateAssignment(assignment));
|
|
}
|
|
locationData["assignments"] = cpptempl::make_data(assignments);
|
|
if (automaton.getNumberOfLocations() > 1) {
|
|
locationData["guard"] = expressionTranslator.translate(shiftVariablesWrtLowerBound(getLocationVariable(automaton) == this->model.getManager().integer(locationIndex)), storm::expressions::ToCppTranslationOptions(variablePrefixes, variableToName));
|
|
} else {
|
|
locationData["guard"] = asString(true);
|
|
}
|
|
++locationIndex;
|
|
}
|
|
if (!locationData["assignments"]->empty()) {
|
|
locations.push_back(locationData);
|
|
}
|
|
}
|
|
|
|
modelData["locations"] = cpptempl::make_data(locations);
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
void ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::generateLabels(cpptempl::data_map& modelData) {
|
|
cpptempl::data_list labels;
|
|
|
|
// As in JANI we can use transient boolean variable assignments in locations to identify states, we need to
|
|
// create a list of boolean transient variables and the expressions that define them.
|
|
for (auto const& variable : model.getGlobalVariables().getTransientVariables()) {
|
|
if (variable.isBooleanVariable()) {
|
|
if (this->options.isBuildAllLabelsSet() || this->options.getLabelNames().find(variable.getName()) != this->options.getLabelNames().end()) {
|
|
cpptempl::data_map label;
|
|
label["name"] = variable.getName();
|
|
label["predicate"] = expressionTranslator.translate(shiftVariablesWrtLowerBound(model.getLabelExpression(variable.asBooleanVariable(), parallelAutomata)), storm::expressions::ToCppTranslationOptions(variablePrefixes, variableToName, storm::expressions::ToCppTranslationMode::CastDouble));
|
|
labels.push_back(label);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::set<std::string> expressionLabelStrings;
|
|
for (auto const& expressionLabel : this->options.getExpressionLabels()) {
|
|
cpptempl::data_map label;
|
|
std::string const& expressionLabelString = expressionLabel.first;
|
|
auto const& expression = expressionLabel.second;
|
|
if(expressionLabelStrings.count(expressionLabelString) == 0) {
|
|
label["name"] = expressionLabelString;
|
|
label["predicate"] = expressionTranslator.translate(shiftVariablesWrtLowerBound(expression), storm::expressions::ToCppTranslationOptions(variablePrefixes, variableToName, storm::expressions::ToCppTranslationMode::CastDouble));
|
|
labels.push_back(label);
|
|
expressionLabelStrings.insert(expressionLabelString);
|
|
}
|
|
}
|
|
|
|
modelData["labels"] = cpptempl::make_data(labels);
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
void ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::generateTerminalExpressions(cpptempl::data_map& modelData) {
|
|
cpptempl::data_list terminalExpressions;
|
|
|
|
for (auto const& terminalEntry : options.getTerminalStates()) {
|
|
LabelOrExpression const& labelOrExpression = terminalEntry.first;
|
|
if (labelOrExpression.isLabel()) {
|
|
auto const& variables = model.getGlobalVariables();
|
|
STORM_LOG_THROW(variables.hasVariable(labelOrExpression.getLabel()), storm::exceptions::WrongFormatException, "Terminal label refers to unknown identifier '" << labelOrExpression.getLabel() << "'.");
|
|
auto const& variable = variables.getVariable(labelOrExpression.getLabel());
|
|
STORM_LOG_THROW(variable.isBooleanVariable(), storm::exceptions::WrongFormatException, "Terminal label refers to non-boolean variable '" << variable.getName() << ".");
|
|
STORM_LOG_THROW(variable.isTransient(), storm::exceptions::WrongFormatException, "Terminal label refers to non-transient variable '" << variable.getName() << ".");
|
|
auto labelExpression = model.getLabelExpression(variable.asBooleanVariable(), parallelAutomata);
|
|
if (!terminalEntry.second) {
|
|
labelExpression = !labelExpression;
|
|
}
|
|
terminalExpressions.push_back(expressionTranslator.translate(shiftVariablesWrtLowerBound(labelExpression), storm::expressions::ToCppTranslationOptions(variablePrefixes, variableToName)));
|
|
} else {
|
|
auto expression = terminalEntry.second ? labelOrExpression.getExpression() : !labelOrExpression.getExpression();
|
|
terminalExpressions.push_back(expressionTranslator.translate(shiftVariablesWrtLowerBound(expression), storm::expressions::ToCppTranslationOptions(variablePrefixes, variableToName)));
|
|
}
|
|
}
|
|
|
|
modelData["terminalExpressions"] = cpptempl::make_data(terminalExpressions);
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
void ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::generateParameters(cpptempl::data_map& modelData) {
|
|
cpptempl::data_list parameters;
|
|
for (auto const& constant : model.getConstants()) {
|
|
if (!constant.isDefined() && constant.isRealConstant()) {
|
|
cpptempl::data_map parameter;
|
|
parameter["name"] = constant.getName();
|
|
parameters.push_back(parameter);
|
|
}
|
|
}
|
|
modelData["parameters"] = cpptempl::make_data(parameters);
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
std::string const& ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::getVariableName(storm::expressions::Variable const& variable) const {
|
|
return variableToName.at(variable);
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
std::string const& ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::registerVariable(storm::expressions::Variable const& variable, bool transient) {
|
|
// Since the variable name might be illegal as a C++ identifier, we need to prepare it a bit.
|
|
variableToName[variable] = variable.getName() + JIT_VARIABLE_EXTENSION;
|
|
if (transient) {
|
|
transientVariables.insert(variable);
|
|
variablePrefixes[variable] = "transientIn.";
|
|
} else {
|
|
nontransientVariables.insert(variable);
|
|
variablePrefixes[variable] = "in.";
|
|
}
|
|
return variableToName[variable];
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
storm::expressions::Variable const& ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::getLocationVariable(storm::jani::Automaton const& automaton) const {
|
|
return automaton.getLocationExpressionVariable();
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
std::string ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::asString(bool value) const {
|
|
std::stringstream out;
|
|
out << std::boolalpha << value;
|
|
return out.str();
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
storm::expressions::Expression ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::shiftVariablesWrtLowerBound(storm::expressions::Expression const& expression) {
|
|
return expression.substitute(lowerBoundShiftSubstitution);
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
template <typename ValueTypePrime>
|
|
std::string ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::asString(ValueTypePrime value) const {
|
|
return std::to_string(value);
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
std::string ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::createSourceCodeFromSkeleton(cpptempl::data_map& modelData) {
|
|
std::string sourceTemplate = R"(
|
|
#define NDEBUG
|
|
|
|
{% if expl_progress %}
|
|
#define EXPL_PROGRESS
|
|
{% endif %}
|
|
|
|
#include <cstdint>
|
|
#include <iostream>
|
|
#include <vector>
|
|
#include <queue>
|
|
#include <cmath>
|
|
#include <unordered_map>
|
|
#include <chrono>
|
|
#include <boost/dll/alias.hpp>
|
|
|
|
{% if exact %}
|
|
#include "storm/adapters/RationalNumberAdapter.h"
|
|
{% endif %}
|
|
{% if parametric %}
|
|
#include "storm/adapters/RationalFunctionAdapter.h"
|
|
{% endif %}
|
|
|
|
#include <sparsepp/spp.h>
|
|
|
|
#include "storm/builder/jit/StateSet.h"
|
|
#include "storm/builder/jit/JitModelBuilderInterface.h"
|
|
#include "storm/builder/jit/StateBehaviour.h"
|
|
#include "storm/builder/jit/ModelComponentsBuilder.h"
|
|
#include "storm/builder/RewardModelInformation.h"
|
|
|
|
#include "storm/utility/constants.h"
|
|
#include "storm/exceptions/WrongFormatException.h"
|
|
|
|
namespace storm {
|
|
namespace builder {
|
|
namespace jit {
|
|
|
|
typedef uint32_t IndexType;
|
|
|
|
{% if double %}
|
|
typedef double ValueType;
|
|
{% endif %}
|
|
{% if exact %}
|
|
typedef storm::RationalNumber ValueType;
|
|
{% endif %}
|
|
{% if parametric %}
|
|
typedef storm::RationalFunction ValueType;
|
|
{% endif %}
|
|
|
|
struct StateType {
|
|
// Boolean variables.
|
|
{% for variable in nontransient_variables.boolean %}bool {$variable.name} : 1;
|
|
{% endfor %}
|
|
// Bounded integer variables.
|
|
{% for variable in nontransient_variables.boundedInteger %}// {$variable.original_name}: [{$variable.lower} ... {$variable.upper}]
|
|
uint64_t {$variable.name} : {$variable.numberOfBits};
|
|
{% endfor %}
|
|
// Unbounded integer variables.
|
|
{% for variable in nontransient_variables.unboundedInteger %}int64_t {$variable.name};
|
|
{% endfor %}
|
|
// Real variables.
|
|
{% for variable in nontransient_variables.real %}double {$variable.name};
|
|
{% endfor %}
|
|
// Location variables.
|
|
{% for variable in nontransient_variables.locations %}uint64_t {$variable.name} : {$variable.numberOfBits};
|
|
{% endfor %}
|
|
};
|
|
|
|
bool operator==(StateType const& first, StateType const& second) {
|
|
bool result = true;
|
|
{% for variable in nontransient_variables.boolean %}result &= !(first.{$variable.name} ^ second.{$variable.name});
|
|
{% endfor %}
|
|
{% for variable in nontransient_variables.boundedInteger %}result &= first.{$variable.name} == second.{$variable.name};
|
|
{% endfor %}
|
|
{% for variable in nontransient_variables.unboundedInteger %}result &= first.{$variable.name} == second.{$variable.name};
|
|
{% endfor %}
|
|
{% for variable in nontransient_variables.real %}result &= first.{$variable.name} == second.{$variable.name};
|
|
{% endfor %}
|
|
{% for variable in nontransient_variables.locations %}result &= first.{$variable.name} == second.{$variable.name};
|
|
{% endfor %}
|
|
return result;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& out, StateType const& in) {
|
|
out << "<";
|
|
{% for variable in nontransient_variables.boolean %}out << "{$variable.name}=" << std::boolalpha << in.{$variable.name} << ", " << std::noboolalpha;
|
|
{% endfor %}
|
|
{% for variable in nontransient_variables.boundedInteger %}out << "{$variable.name}=" << in.{$variable.name} << ", ";
|
|
{% endfor %}
|
|
{% for variable in nontransient_variables.unboundedInteger %}out << "{$variable.name}=" << in.{$variable.name} << ", ";
|
|
{% endfor %}
|
|
{% for variable in nontransient_variables.real %}out << "{$variable.name}=" << in.{$variable.name} << ", ";
|
|
{% endfor %}
|
|
{% for variable in nontransient_variables.locations %}out << "{$variable.name}=" << in.{$variable.name} << ", ";
|
|
{% endfor %}
|
|
out << ">";
|
|
return out;
|
|
}
|
|
|
|
struct TransientVariables {
|
|
TransientVariables() {
|
|
reset();
|
|
}
|
|
|
|
void reset() {
|
|
{% for variable in transient_variables.boolean %}{$variable.name} = {$variable.init};
|
|
{% endfor %}
|
|
{% for variable in transient_variables.boundedInteger %}{$variable.name} = {$variable.init};
|
|
{% endfor %}
|
|
{% for variable in transient_variables.unboundedInteger %}{$variable.name} = {$variable.init};
|
|
{% endfor %}
|
|
{% for variable in transient_variables.real %}{$variable.name} = {$variable.init};
|
|
{% endfor %}
|
|
}
|
|
|
|
// Boolean variables.
|
|
{% for variable in transient_variables.boolean %}bool {$variable.name} : 1;
|
|
{% endfor %}
|
|
// Bounded integer variables.
|
|
{% for variable in transient_variables.boundedInteger %}uint64_t {$variable.name} : {$variable.numberOfBits};
|
|
{% endfor %}
|
|
// Unbounded integer variables.
|
|
{% for variable in transient_variables.unboundedInteger %}int64_t {$variable.name};
|
|
{% endfor %}
|
|
// Real variables.
|
|
{% if double %}
|
|
{% for variable in transient_variables.real %}double {$variable.name};
|
|
{% endfor %}
|
|
{% endif %}
|
|
{% if exact %}
|
|
{% for variable in transient_variables.real %}storm::RationalNumber {$variable.name};
|
|
{% endfor %}
|
|
{% endif %}
|
|
{% if parametric %}
|
|
{% for variable in transient_variables.real %}storm::RationalFunction {$variable.name};
|
|
{% endfor %}
|
|
{% endif %}
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream& out, TransientVariables const& in) {
|
|
out << "<";
|
|
{% for variable in transient_variables.boolean %}out << "{$variable.name}=" << std::boolalpha << in.{$variable.name} << ", " << std::noboolalpha;
|
|
{% endfor %}
|
|
{% for variable in transient_variables.boundedInteger %}out << "{$variable.name}=" << in.{$variable.name} << ", ";
|
|
{% endfor %}
|
|
{% for variable in transient_variables.unboundedInteger %}out << "{$variable.name}=" << in.{$variable.name} << ", ";
|
|
{% endfor %}
|
|
{% for variable in transient_variables.real %}out << "{$variable.name}=" << in.{$variable.name} << ", ";
|
|
{% endfor %}
|
|
out << ">";
|
|
return out;
|
|
}
|
|
|
|
{% if exploration_checks %}
|
|
struct VariableWrites {
|
|
VariableWrites() {
|
|
reset();
|
|
}
|
|
|
|
void reset() {
|
|
{% for variable in nontransient_variables.boolean %}{$variable.name} = false;
|
|
{% endfor %}
|
|
{% for variable in nontransient_variables.boundedInteger %}{$variable.name} = false;
|
|
{% endfor %}
|
|
{% for variable in nontransient_variables.unboundedInteger %}{$variable.name} = false;
|
|
{% endfor %}
|
|
{% for variable in nontransient_variables.real %}{$variable.name} = false;
|
|
{% endfor %}
|
|
{% for variable in nontransient_variables.locations %}{$variable.name} = false;
|
|
{% endfor %}
|
|
{% for variable in transient_variables.boolean %}{$variable.name} = false;
|
|
{% endfor %}
|
|
{% for variable in transient_variables.boundedInteger %}{$variable.name} = false;
|
|
{% endfor %}
|
|
{% for variable in transient_variables.unboundedInteger %}{$variable.name} = false;
|
|
{% endfor %}
|
|
{% for variable in transient_variables.real %}{$variable.name} = false;
|
|
{% endfor %}
|
|
}
|
|
|
|
// Boolean variables.
|
|
{% for variable in nontransient_variables.boolean %}bool {$variable.name} : 1;
|
|
{% endfor %}
|
|
{% for variable in transient_variables.boolean %}bool {$variable.name} : 1;
|
|
{% endfor %}
|
|
// Bounded integer variables.
|
|
{% for variable in nontransient_variables.boundedInteger %}bool {$variable.name} : 1;
|
|
{% endfor %}
|
|
{% for variable in transient_variables.boundedInteger %}bool {$variable.name} : 1;
|
|
{% endfor %}
|
|
// Unbounded integer variables.
|
|
{% for variable in nontransient_variables.unboundedInteger %}bool {$variable.name} : 1;
|
|
{% endfor %}
|
|
{% for variable in transient_variables.unboundedInteger %}bool {$variable.name} : 1;
|
|
{% endfor %}
|
|
// Real variables.
|
|
{% for variable in nontransient_variables.real %}bool {$variable.name} : 1;
|
|
{% endfor %}
|
|
{% for variable in transient_variables.real %}bool {$variable.name} : 1;
|
|
{% endfor %}
|
|
|
|
// Location variables.
|
|
{% for variable in nontransient_variables.locations %}bool {$variable.name} : 1;
|
|
{% endfor %}
|
|
};
|
|
{% endif %}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace std {
|
|
template <>
|
|
struct hash<storm::builder::jit::StateType> {
|
|
std::size_t operator()(storm::builder::jit::StateType const& in) const {
|
|
// Note: this is faster than viewing the struct as a bit field and taking hash_combine of the bytes.
|
|
std::size_t seed = 0;
|
|
{% for variable in nontransient_variables.boolean %}spp::hash_combine(seed, in.{$variable.name});
|
|
{% endfor %}
|
|
{% for variable in nontransient_variables.boundedInteger %}spp::hash_combine(seed, in.{$variable.name});
|
|
{% endfor %}
|
|
{% for variable in nontransient_variables.unboundedInteger %}spp::hash_combine(seed, in.{$variable.name});
|
|
{% endfor %}
|
|
{% for variable in nontransient_variables.real %}spp::hash_combine(seed, in.{$variable.name});
|
|
{% endfor %}
|
|
{% for variable in nontransient_variables.locations %}spp::hash_combine(seed, in.{$variable.name});
|
|
{% endfor %}
|
|
return seed;
|
|
}
|
|
};
|
|
}
|
|
|
|
namespace storm {
|
|
namespace builder {
|
|
namespace jit {
|
|
|
|
static bool model_is_deterministic() {
|
|
return {$deterministic_model};
|
|
}
|
|
|
|
static bool model_is_discrete_time() {
|
|
return {$discrete_time_model};
|
|
}
|
|
|
|
static bool perform_exploration_checks() {
|
|
{% if exploration_checks %}
|
|
return true;
|
|
{% endif %}
|
|
{% if not exploration_checks %}
|
|
return false;
|
|
{% endif %}
|
|
}
|
|
|
|
{% if parametric %}
|
|
{% for parameter in parameters %}static storm::RationalFunction {$parameter.name};
|
|
{% endfor %}
|
|
|
|
void initialize_parameters(std::vector<storm::RationalFunction> const& parameters) {
|
|
#ifndef NDEBUG
|
|
std::cout << "initializing parameters" << std::endl;
|
|
#endif
|
|
{% for parameter in parameters %}{$parameter.name} = parameters[{$loop.index} - 1];
|
|
{% endfor %}
|
|
}
|
|
{% endif %}
|
|
|
|
// Non-synchronizing edges.
|
|
{% for edge in nonsynch_edges %}static bool edge_enabled_{$edge.name}(StateType const& in, TransientVariables const& transientIn) {
|
|
if ({$edge.guard}) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static ValueType edge_rate_{$edge.name}(StateType const& in) {
|
|
return {$edge.rate};
|
|
}
|
|
|
|
static void edge_perform_{$edge.name}(StateType const& in, TransientVariables const& transientIn, TransientVariables& transientOut {% if exploration_checks %}, VariableWrites& variableWrites {% endif %}) {
|
|
{% for assignment in edge.transient_assignments %}transientOut.{$assignment.variable} = {$assignment.value};
|
|
{% if exploration_checks %}
|
|
if (variableWrites.{$assignment.variable}) {
|
|
throw storm::exceptions::WrongFormatException("Illegal write to {$assignment.variable}, variable has been written before.");
|
|
} else {
|
|
variableWrites.{$assignment.variable} = true;
|
|
}
|
|
{% endif %}
|
|
{% endfor %}
|
|
}
|
|
|
|
{% for destination in edge.destinations %}
|
|
static void destination_perform_level_{$edge.name}_{$destination.name}(int_fast64_t level, StateType const& in, StateType& out {% if exploration_checks %}, VariableWrites& variableWrites {% endif %}) {
|
|
{% for level in destination.levels %}if (level == {$level.index}) {
|
|
{% for assignment in level.non_transient_assignments %}out.{$assignment.variable} = {$assignment.value};
|
|
{% if exploration_checks %}
|
|
if (variableWrites.{$assignment.variable}) {
|
|
throw storm::exceptions::WrongFormatException("Illegal write to {$assignment.variable}, variable has been written before.");
|
|
} else {
|
|
variableWrites.{$assignment.variable} = true;
|
|
}
|
|
{% endif %}
|
|
{% endfor %}
|
|
}
|
|
{% endfor %}
|
|
}
|
|
|
|
static void destination_perform_{$edge.name}_{$destination.name}(StateType const& in, StateType& out {% if exploration_checks %}, VariableWrites& variableWrites {% endif %}) {
|
|
{% for level in destination.levels %}
|
|
destination_perform_level_{$edge.name}_{$destination.name}({$level.index}, in, out {% if exploration_checks %}, variableWrites {% endif %});
|
|
{% endfor %}
|
|
}
|
|
|
|
static void destination_perform_level_{$edge.name}_{$destination.name}(int_fast64_t level, StateType const& in, StateType& out, TransientVariables const& transientIn, TransientVariables& transientOut {% if exploration_checks %}, VariableWrites& variableWrites {% endif %}) {
|
|
{% for level in destination.levels %}if (level == {$level.index}) {
|
|
{% for assignment in level.non_transient_assignments %}out.{$assignment.variable} = {$assignment.value};
|
|
{% if exploration_checks %}
|
|
if (variableWrites.{$assignment.variable}) {
|
|
throw storm::exceptions::WrongFormatException("Illegal write to {$assignment.variable}, variable has been written before.");
|
|
} else {
|
|
variableWrites.{$assignment.variable} = true;
|
|
}
|
|
{% endif %}
|
|
{% endfor %}
|
|
{% for assignment in level.transient_assignments %}transientOut.{$assignment.variable} = {$assignment.value};
|
|
{% if exploration_checks %}
|
|
if (variableWrites.{$assignment.variable}) {
|
|
throw storm::exceptions::WrongFormatException("Illegal write to {$assignment.variable}, variable has been written before.");
|
|
} else {
|
|
variableWrites.{$assignment.variable} = true;
|
|
}
|
|
{% endif %}
|
|
{% endfor %}
|
|
}
|
|
{% endfor %}
|
|
}
|
|
|
|
static void destination_perform_{$edge.name}_{$destination.name}(StateType const& in, StateType& out, TransientVariables const& transientIn, TransientVariables& transientOut {% if exploration_checks %}, VariableWrites& variableWrites {% endif %}) {
|
|
{% for level in destination.levels %}
|
|
destination_perform_level_{$edge.name}_{$destination.name}({$level.index}, in, out, transientIn, transientOut {% if exploration_checks %}, variableWrites {% endif %});
|
|
{% endfor %}
|
|
}
|
|
|
|
static ValueType destination_probability_{$edge.name}_{$destination.name}(StateType const& in) {
|
|
return {$destination.probability};
|
|
}
|
|
{% endfor %}{% endfor %}
|
|
|
|
// Synchronizing edges.
|
|
{% for edge in synch_edges %}static bool edge_enabled_{$edge.name}(StateType const& in, TransientVariables const& transientIn) {
|
|
if ({$edge.guard}) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static ValueType edge_rate_{$edge.name}(StateType const& in) {
|
|
return {$edge.rate};
|
|
}
|
|
|
|
static void edge_perform_{$edge.name}(StateType const& in, TransientVariables const& transientIn, TransientVariables& transientOut {% if exploration_checks %}, VariableWrites& variableWrites {% endif %}) {
|
|
{% for assignment in edge.transient_assignments %}transientOut.{$assignment.variable} = {$assignment.value};
|
|
{% if exploration_checks %}
|
|
if (variableWrites.{$assignment.variable}) {
|
|
throw storm::exceptions::WrongFormatException("Illegal write to {$assignment.variable}, variable has been written before.");
|
|
} else {
|
|
variableWrites.{$assignment.variable} = true;
|
|
}
|
|
{% endif %}
|
|
{% endfor %}
|
|
}
|
|
|
|
{% for destination in edge.destinations %}
|
|
static void destination_perform_level_{$edge.name}_{$destination.name}(int_fast64_t level, StateType const& in, StateType& out {% if exploration_checks %}, VariableWrites& variableWrites {% endif %}) {
|
|
{% for level in destination.levels %}if (level == {$level.index}) {
|
|
{% for assignment in level.non_transient_assignments %}out.{$assignment.variable} = {$assignment.value};
|
|
{% if exploration_checks %}
|
|
if (variableWrites.{$assignment.variable}) {
|
|
throw storm::exceptions::WrongFormatException("Illegal write to {$assignment.variable}, variable has been written before.");
|
|
} else {
|
|
variableWrites.{$assignment.variable} = true;
|
|
}
|
|
{% endif %}
|
|
{% endfor %}
|
|
}
|
|
{% endfor %}
|
|
}
|
|
|
|
static void destination_perform_{$edge.name}_{$destination.name}(StateType const& in, StateType& out {% if exploration_checks %}, VariableWrites& variableWrites {% endif %}) {
|
|
{% for level in destination.levels %}
|
|
destination_perform_level_{$edge.name}_{$destination.name}({$level.index}, in, out {% if exploration_checks %}, variableWrites {% endif %});
|
|
{% endfor %}
|
|
}
|
|
|
|
static void destination_perform_level_{$edge.name}_{$destination.name}(int_fast64_t level, StateType const& in, StateType& out, TransientVariables const& transientIn, TransientVariables& transientOut {% if exploration_checks %}, VariableWrites& variableWrites {% endif %}) {
|
|
{% for level in destination.levels %}if (level == {$level.index}) {
|
|
{% for assignment in level.non_transient_assignments %}out.{$assignment.variable} = {$assignment.value};
|
|
{% if exploration_checks %}
|
|
if (variableWrites.{$assignment.variable}) {
|
|
throw storm::exceptions::WrongFormatException("Illegal write to {$assignment.variable}, variable has been written before.");
|
|
} else {
|
|
variableWrites.{$assignment.variable} = true;
|
|
}
|
|
{% endif %}
|
|
{% endfor %}
|
|
{% for assignment in level.transient_assignments %}transientOut.{$assignment.variable} = {$assignment.value};
|
|
{% if exploration_checks %}
|
|
if (variableWrites.{$assignment.variable}) {
|
|
throw storm::exceptions::WrongFormatException("Illegal write to {$assignment.variable}, variable has been written before.");
|
|
} else {
|
|
variableWrites.{$assignment.variable} = true;
|
|
}
|
|
{% endif %}
|
|
{% endfor %}
|
|
}
|
|
{% endfor %}
|
|
}
|
|
|
|
static void destination_perform_{$edge.name}_{$destination.name}(StateType const& in, StateType& out, TransientVariables const& transientIn, TransientVariables& transientOut {% if exploration_checks %}, VariableWrites& variableWrites {% endif %}) {
|
|
{% for level in destination.levels %}
|
|
destination_perform_level_{$edge.name}_{$destination.name}({$level.index}, in, out, transientIn, transientOut {% if exploration_checks %}, variableWrites {% endif %});
|
|
{% endfor %}
|
|
}
|
|
|
|
static ValueType destination_probability_{$edge.name}_{$destination.name}(StateType const& in) {
|
|
return {$destination.probability};
|
|
}
|
|
{% endfor %}{% endfor %}
|
|
|
|
typedef ValueType (*DestinationProbabilityFunctionPtr)(StateType const&);
|
|
typedef void (*DestinationLevelFunctionPtr)(int_fast64_t, StateType const&, StateType&, TransientVariables const&, TransientVariables& {% if exploration_checks %}, VariableWrites& {% endif %});
|
|
typedef void (*DestinationFunctionPtr)(StateType const&, StateType&, TransientVariables const&, TransientVariables& {% if exploration_checks %}, VariableWrites& {% endif %});
|
|
typedef void (*DestinationWithoutTransientLevelFunctionPtr)(int_fast64_t, StateType const&, StateType& {% if exploration_checks %}, VariableWrites& {% endif %});
|
|
typedef void (*DestinationWithoutTransientFunctionPtr)(StateType const&, StateType& {% if exploration_checks %}, VariableWrites& {% endif %});
|
|
|
|
class Destination {
|
|
public:
|
|
Destination() : mLowestLevel(0), mHighestLevel(0), destinationProbabilityFunction(nullptr), destinationLevelFunction(nullptr), destinationFunction(nullptr), destinationWithoutTransientLevelFunction(nullptr), destinationWithoutTransientFunction(nullptr) {
|
|
// Intentionally left empty.
|
|
}
|
|
|
|
Destination(int_fast64_t lowestLevel, int_fast64_t highestLevel, DestinationProbabilityFunctionPtr destinationProbabilityFunction, DestinationLevelFunctionPtr destinationLevelFunction, DestinationFunctionPtr destinationFunction, DestinationWithoutTransientLevelFunctionPtr destinationWithoutTransientLevelFunction, DestinationWithoutTransientFunctionPtr destinationWithoutTransientFunction) : mLowestLevel(lowestLevel), mHighestLevel(highestLevel), destinationProbabilityFunction(destinationProbabilityFunction), destinationLevelFunction(destinationLevelFunction), destinationFunction(destinationFunction), destinationWithoutTransientLevelFunction(destinationWithoutTransientLevelFunction), destinationWithoutTransientFunction(destinationWithoutTransientFunction) {
|
|
// Intentionally left empty.
|
|
}
|
|
|
|
int_fast64_t lowestLevel() const {
|
|
return mLowestLevel;
|
|
}
|
|
|
|
int_fast64_t highestLevel() const {
|
|
return mHighestLevel;
|
|
}
|
|
|
|
ValueType probability(StateType const& in) const {
|
|
return destinationProbabilityFunction(in);
|
|
}
|
|
|
|
void perform(int_fast64_t level, StateType const& in, StateType& out, TransientVariables const& transientIn, TransientVariables& transientOut {% if exploration_checks %}, VariableWrites& variableWrites {% endif %}) const {
|
|
destinationLevelFunction(level, in, out, transientIn, transientOut {% if exploration_checks %}, variableWrites {% endif %});
|
|
}
|
|
|
|
void perform(StateType const& in, StateType& out, TransientVariables const& transientIn, TransientVariables& transientOut {% if exploration_checks %}, VariableWrites& variableWrites {% endif %}) const {
|
|
destinationFunction(in, out, transientIn, transientOut {% if exploration_checks %}, variableWrites {% endif %});
|
|
}
|
|
|
|
void perform(int_fast64_t level, StateType const& in, StateType& out {% if exploration_checks %}, VariableWrites& variableWrites {% endif %}) const {
|
|
destinationWithoutTransientLevelFunction(level, in, out {% if exploration_checks %}, variableWrites {% endif %});
|
|
}
|
|
|
|
void perform(StateType const& in, StateType& out {% if exploration_checks %}, VariableWrites& variableWrites {% endif %}) const {
|
|
destinationWithoutTransientFunction(in, out {% if exploration_checks %}, variableWrites {% endif %});
|
|
}
|
|
|
|
private:
|
|
int_fast64_t mLowestLevel;
|
|
int_fast64_t mHighestLevel;
|
|
DestinationProbabilityFunctionPtr destinationProbabilityFunction;
|
|
DestinationLevelFunctionPtr destinationLevelFunction;
|
|
DestinationFunctionPtr destinationFunction;
|
|
DestinationWithoutTransientLevelFunctionPtr destinationWithoutTransientLevelFunction;
|
|
DestinationWithoutTransientFunctionPtr destinationWithoutTransientFunction;
|
|
};
|
|
|
|
typedef bool (*EdgeEnabledFunctionPtr)(StateType const&, TransientVariables const& transientIn);
|
|
typedef ValueType (*EdgeRateFunctionPtr)(StateType const&);
|
|
typedef void (*EdgeTransientFunctionPtr)(StateType const&, TransientVariables const& transientIn, TransientVariables& out {% if exploration_checks %}, VariableWrites& variableWrites {% endif %});
|
|
|
|
class Edge {
|
|
public:
|
|
typedef std::vector<Destination> ContainerType;
|
|
|
|
Edge() : edgeEnabledFunction(nullptr), edgeRateFunction(nullptr), edgeTransientFunction(nullptr) {
|
|
// Intentionally left empty.
|
|
}
|
|
|
|
Edge(EdgeEnabledFunctionPtr edgeEnabledFunction, EdgeRateFunctionPtr edgeRateFunction, EdgeTransientFunctionPtr edgeTransientFunction = nullptr) : edgeEnabledFunction(edgeEnabledFunction), edgeRateFunction(edgeRateFunction), edgeTransientFunction(edgeTransientFunction) {
|
|
// Intentionally left empty.
|
|
}
|
|
|
|
bool isEnabled(StateType const& in, TransientVariables const& transientIn) const {
|
|
return edgeEnabledFunction(in, transientIn);
|
|
}
|
|
|
|
void addDestination(Destination const& destination) {
|
|
destinations.push_back(destination);
|
|
}
|
|
|
|
void addDestination(int_fast64_t lowestLevel, int_fast64_t highestLevel, DestinationProbabilityFunctionPtr destinationProbabilityFunction, DestinationLevelFunctionPtr destinationLevelFunction, DestinationFunctionPtr destinationFunction, DestinationWithoutTransientLevelFunctionPtr destinationWithoutTransientLevelFunction, DestinationWithoutTransientFunctionPtr destinationWithoutTransientFunction) {
|
|
destinations.emplace_back(lowestLevel, highestLevel, destinationProbabilityFunction, destinationLevelFunction, destinationFunction, destinationWithoutTransientLevelFunction, destinationWithoutTransientFunction);
|
|
}
|
|
|
|
std::vector<Destination> const& getDestinations() const {
|
|
return destinations;
|
|
}
|
|
|
|
ContainerType::const_iterator begin() const {
|
|
return destinations.begin();
|
|
}
|
|
|
|
ContainerType::const_iterator end() const {
|
|
return destinations.end();
|
|
}
|
|
|
|
ValueType rate(StateType const& in) const {
|
|
return edgeRateFunction(in);
|
|
}
|
|
|
|
void perform(StateType const& in, TransientVariables const& transientIn, TransientVariables& transientOut {% if exploration_checks %}, VariableWrites& variableWrites {% endif %}) const {
|
|
edgeTransientFunction(in, transientIn, transientOut {% if exploration_checks %}, variableWrites {% endif %});
|
|
}
|
|
|
|
private:
|
|
EdgeEnabledFunctionPtr edgeEnabledFunction;
|
|
EdgeRateFunctionPtr edgeRateFunction;
|
|
EdgeTransientFunctionPtr edgeTransientFunction;
|
|
ContainerType destinations;
|
|
};
|
|
|
|
void locations_perform(StateType const& in, TransientVariables const& transientIn, TransientVariables& transientOut {% if exploration_checks %}, VariableWrites& variableWrites {% endif %}) {
|
|
{% for location in locations %}if ({$location.guard}) {
|
|
{% for assignment in location.assignments %}transientOut.{$assignment.variable} = {$assignment.value};
|
|
{% if exploration_checks %}
|
|
if (variableWrites.{$assignment.variable}) {
|
|
throw storm::exceptions::WrongFormatException("Illegal write to {$assignment.variable}, variable has been written before.");
|
|
} else {
|
|
variableWrites.{$assignment.variable} = true;
|
|
}
|
|
{% endif %}
|
|
{% endfor %}
|
|
}
|
|
{% endfor %}
|
|
}
|
|
|
|
class JitBuilder : public JitModelBuilderInterface<IndexType, ValueType> {
|
|
public:
|
|
JitBuilder(ModelComponentsBuilder<IndexType, ValueType>& modelComponentsBuilder) : JitModelBuilderInterface(modelComponentsBuilder), timeOfStart(std::chrono::high_resolution_clock::now()), timeOfLastMessage(std::chrono::high_resolution_clock::now()), numberOfExploredStates(0), numberOfExploredStatesSinceLastMessage(0) {
|
|
{% for state in initialStates %}{
|
|
StateType state;
|
|
{% for assignment in state %}state.{$assignment.variable} = {$assignment.value};
|
|
{% endfor %}
|
|
initialStates.push_back(state);
|
|
}{% endfor %}
|
|
{% for edge in nonsynch_edges %}{
|
|
edge_{$edge.name} = Edge(&edge_enabled_{$edge.name}, &edge_rate_{$edge.name}, edge_perform_{$edge.name});
|
|
{% for destination in edge.destinations %}edge_{$edge.name}.addDestination({$destination.lowestLevel}, {$destination.highestLevel}, &destination_probability_{$edge.name}_{$destination.name}, &destination_perform_level_{$edge.name}_{$destination.name}, &destination_perform_{$edge.name}_{$destination.name}, &destination_perform_level_{$edge.name}_{$destination.name}, &destination_perform_{$edge.name}_{$destination.name});
|
|
{% endfor %}
|
|
}
|
|
{% endfor %}
|
|
{% for edge in synch_edges %}{
|
|
edge_{$edge.name} = Edge(&edge_enabled_{$edge.name}, &edge_rate_{$edge.name}, edge_perform_{$edge.name});
|
|
{% for destination in edge.destinations %}edge_{$edge.name}.addDestination({$destination.lowestLevel}, {$destination.highestLevel}, &destination_probability_{$edge.name}_{$destination.name}, &destination_perform_level_{$edge.name}_{$destination.name}, &destination_perform_{$edge.name}_{$destination.name}, &destination_perform_level_{$edge.name}_{$destination.name}, &destination_perform_{$edge.name}_{$destination.name});
|
|
{% endfor %}
|
|
}
|
|
{% endfor %}
|
|
{% for reward in rewards %}
|
|
modelComponentsBuilder.registerRewardModel(RewardModelInformation("{$reward.name}", {$reward.location_rewards}, {$reward.edge_rewards} || {$reward.destination_rewards}, false));
|
|
{% endfor %}
|
|
}
|
|
|
|
virtual storm::models::sparse::Model<ValueType, storm::models::sparse::StandardRewardModel<ValueType>>* build() override {
|
|
#ifndef NDEBUG
|
|
std::cout << "starting building process" << std::endl;
|
|
#endif
|
|
explore(initialStates);
|
|
#ifndef NDEBUG
|
|
std::cout << "finished building process with " << stateIds.size() << " states" << std::endl;
|
|
#endif
|
|
|
|
#ifndef NDEBUG
|
|
std::cout << "building labeling" << std::endl;
|
|
#endif
|
|
label();
|
|
#ifndef NDEBUG
|
|
std::cout << "finished building labeling" << std::endl;
|
|
#endif
|
|
|
|
return this->modelComponentsBuilder.build(stateIds.size());
|
|
}
|
|
|
|
void label() {
|
|
uint64_t labelCount = 0;
|
|
{% for label in labels %}this->modelComponentsBuilder.registerLabel("{$label.name}", stateIds.size());
|
|
++labelCount;
|
|
{% endfor %}
|
|
this->modelComponentsBuilder.registerLabel("init", stateIds.size());
|
|
this->modelComponentsBuilder.registerLabel("deadlock", stateIds.size());
|
|
|
|
for (auto const& stateEntry : stateIds) {
|
|
auto const& in = stateEntry.first;
|
|
{% for label in labels %}if ({$label.predicate}) {
|
|
this->modelComponentsBuilder.addLabel(stateEntry.second, {$loop.index} - 1);
|
|
}
|
|
{% endfor %}
|
|
}
|
|
|
|
for (auto const& state : initialStates) {
|
|
auto stateIt = stateIds.find(state);
|
|
if (stateIt != stateIds.end()) {
|
|
this->modelComponentsBuilder.addLabel(stateIt->second, labelCount);
|
|
}
|
|
}
|
|
|
|
for (auto const& stateId : deadlockStates) {
|
|
this->modelComponentsBuilder.addLabel(stateId, labelCount + 1);
|
|
}
|
|
}
|
|
|
|
void explore(std::vector<StateType> const& initialStates) {
|
|
for (auto const& state : initialStates) {
|
|
explore(state);
|
|
}
|
|
}
|
|
|
|
void explore(StateType const& initialState) {
|
|
StateSet<StateType> statesToExplore;
|
|
getOrAddIndex(initialState, statesToExplore);
|
|
|
|
StateBehaviour<IndexType, ValueType> behaviour;
|
|
|
|
while (!statesToExplore.empty()) {
|
|
StateType currentState = statesToExplore.get();
|
|
IndexType currentIndex = getIndex(currentState);
|
|
|
|
if (!isTerminalState(currentState)) {
|
|
#ifndef NDEBUG
|
|
std::cout << "Exploring state " << currentState << ", id " << currentIndex << std::endl;
|
|
#endif
|
|
|
|
behaviour.setExpanded();
|
|
|
|
{% if exploration_checks %}VariableWrites variableWrites;
|
|
{% endif %}
|
|
|
|
// Perform transient location assignments.
|
|
TransientVariables transientIn;
|
|
TransientVariables transientOut;
|
|
locations_perform(currentState, transientIn, transientOut {% if exploration_checks %}, variableWrites {% endif %});
|
|
{% for reward in location_rewards %}
|
|
behaviour.addStateReward(transientOut.{$reward.variable});
|
|
{% endfor %}
|
|
|
|
// Explore all edges that do not take part in synchronization vectors.
|
|
exploreNonSynchronizingEdges(currentState, transientOut, behaviour, statesToExplore);
|
|
|
|
// Explore all edges that participate in synchronization vectors.
|
|
exploreSynchronizingEdges(currentState, transientOut, behaviour, statesToExplore);
|
|
}
|
|
|
|
{% if dontFixDeadlocks %}
|
|
if (behaviour.empty() && behaviour.isExpanded() ) {
|
|
std::cout << "found deadlock state: " << currentState << std::endl;
|
|
throw storm::exceptions::WrongFormatException("Error while creating sparse matrix from JANI model: found deadlock state and fixing deadlocks was explicitly disabled.");
|
|
}
|
|
{% endif %}
|
|
|
|
this->addStateBehaviour(currentIndex, behaviour);
|
|
behaviour.clear();
|
|
|
|
#ifdef EXPL_PROGRESS
|
|
++numberOfExploredStatesSinceLastMessage;
|
|
++numberOfExploredStates;
|
|
|
|
auto now = std::chrono::high_resolution_clock::now();
|
|
auto durationSinceLastMessage = std::chrono::duration_cast<std::chrono::seconds>(now - timeOfLastMessage).count();
|
|
if (static_cast<uint64_t>(durationSinceLastMessage) >= {$expl_progress_interval}) {
|
|
auto statesPerSecond = numberOfExploredStatesSinceLastMessage / durationSinceLastMessage;
|
|
auto durationSinceStart = std::chrono::duration_cast<std::chrono::seconds>(now - timeOfStart).count();
|
|
std::cout << "Explored " << numberOfExploredStates << " states in " << durationSinceStart << " seconds (currently " << statesPerSecond << " states per second)." << std::endl;
|
|
timeOfLastMessage = std::chrono::high_resolution_clock::now();
|
|
numberOfExploredStatesSinceLastMessage = 0;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
bool isTerminalState(StateType const& in) const {
|
|
{% for expression in terminalExpressions %}if ({$expression}) {
|
|
return true;
|
|
}
|
|
{% endfor %}
|
|
return false;
|
|
}
|
|
|
|
void exploreNonSynchronizingEdges(StateType const& in, TransientVariables const& transientIn, StateBehaviour<IndexType, ValueType>& behaviour, StateSet<StateType>& statesToExplore) {
|
|
{% for edge in nonsynch_edges %}{
|
|
#ifndef NDEBUG
|
|
std::cout << "Exploring non-synchronizing edge {$edge.name}." << std::endl;
|
|
#endif
|
|
if ({$edge.guard}) {
|
|
Choice<IndexType, ValueType>& choice = behaviour.addChoice(!model_is_deterministic() && !model_is_discrete_time() && {$edge.markovian});
|
|
choice.resizeRewards({$edge_destination_rewards_count});
|
|
{
|
|
{% if exploration_checks %}VariableWrites variableWrites;
|
|
{% endif %}
|
|
|
|
TransientVariables transient;
|
|
{% if edge.transient_assignments %}
|
|
edge_perform_{$edge.name}(in, transient, transient {% if exploration_checks %}, variableWrites{% endif %});
|
|
{% endif %}
|
|
{% for reward in edge_rewards %}
|
|
choice.addReward({$reward.index}, transient.{$reward.variable});
|
|
{% endfor %}
|
|
}
|
|
auto rate = edge_rate_{$edge.name}(in);
|
|
{% for destination in edge.destinations %}{
|
|
{% if exploration_checks %}VariableWrites variableWrites;
|
|
{% endif %}
|
|
|
|
StateType out(in);
|
|
TransientVariables transientIn;
|
|
TransientVariables transientOut;
|
|
destination_perform_{$edge.name}_{$destination.name}(in, out{% if edge.transient_variables_in_destinations %}, transientIn, transientOut{% endif %}{% if exploration_checks %}, variableWrites{% endif %});
|
|
IndexType outStateIndex = getOrAddIndex(out, statesToExplore);
|
|
auto probability = destination_probability_{$edge.name}_{$destination.name}(in);
|
|
choice.add(outStateIndex, rate * probability);
|
|
{% for reward in destination_rewards %}
|
|
choice.addReward({$reward.index}, probability * transientOut.{$reward.variable});
|
|
{% endfor %}
|
|
}
|
|
{% endfor %}
|
|
}
|
|
}
|
|
{% endfor %}
|
|
}
|
|
|
|
{% for vector in synch_vectors %}{$vector.functions}
|
|
{% endfor %}
|
|
|
|
void exploreSynchronizingEdges(StateType const& state, TransientVariables const& transientIn, StateBehaviour<IndexType, ValueType>& behaviour, StateSet<StateType>& statesToExplore) {
|
|
{% for vector in synch_vectors %}{
|
|
exploreSynchronizationVector_{$vector.index}(state, transientIn, behaviour, statesToExplore);
|
|
}
|
|
{% endfor %}
|
|
}
|
|
|
|
IndexType getOrAddIndex(StateType const& state, StateSet<StateType>& statesToExplore) {
|
|
auto it = stateIds.find(state);
|
|
if (it != stateIds.end()) {
|
|
return it->second;
|
|
} else {
|
|
IndexType newIndex = static_cast<IndexType>(stateIds.size());
|
|
stateIds.insert(std::make_pair(state, newIndex));
|
|
statesToExplore.add(state);
|
|
return newIndex;
|
|
}
|
|
}
|
|
|
|
IndexType getIndex(StateType const& state) const {
|
|
auto it = stateIds.find(state);
|
|
if (it != stateIds.end()) {
|
|
return it->second;
|
|
} else {
|
|
return stateIds.at(state);
|
|
}
|
|
}
|
|
|
|
void addStateBehaviour(IndexType const& stateId, StateBehaviour<IndexType, ValueType>& behaviour) {
|
|
if (behaviour.empty() && behaviour.isExpanded()) {
|
|
deadlockStates.push_back(stateId);
|
|
}
|
|
|
|
JitModelBuilderInterface<IndexType, ValueType>::addStateBehaviour(stateId, behaviour);
|
|
}
|
|
|
|
static JitModelBuilderInterface<IndexType, ValueType>* create(ModelComponentsBuilder<IndexType, ValueType>& modelComponentsBuilder) {
|
|
return new JitBuilder(modelComponentsBuilder);
|
|
}
|
|
|
|
private:
|
|
// State storage.
|
|
spp::sparse_hash_map<StateType, IndexType> stateIds;
|
|
std::vector<StateType> initialStates;
|
|
std::vector<IndexType> deadlockStates;
|
|
|
|
// Edges.
|
|
{% for edge in nonsynch_edges %}Edge edge_{$edge.name};
|
|
{% endfor %}
|
|
{% for edge in synch_edges %}Edge edge_{$edge.name};
|
|
{% endfor %}
|
|
|
|
// Statistics.
|
|
std::chrono::high_resolution_clock::time_point timeOfStart;
|
|
std::chrono::high_resolution_clock::time_point timeOfLastMessage;
|
|
uint64_t numberOfExploredStates;
|
|
uint64_t numberOfExploredStatesSinceLastMessage;
|
|
};
|
|
|
|
BOOST_DLL_ALIAS(storm::builder::jit::JitBuilder::create, create_builder)
|
|
{% if parametric %}
|
|
BOOST_DLL_ALIAS(storm::builder::jit::initialize_parameters, initialize_parameters)
|
|
{% endif %}
|
|
}
|
|
}
|
|
}
|
|
)";
|
|
|
|
return cpptempl::parse(sourceTemplate, modelData);
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
boost::filesystem::path ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::compileToSharedLibrary(boost::filesystem::path const& sourceFile) {
|
|
std::string sourceFilename = boost::filesystem::absolute(sourceFile).string();
|
|
auto dynamicLibraryPath = sourceFile;
|
|
dynamicLibraryPath += DYLIB_EXTENSION;
|
|
std::string dynamicLibraryFilename = boost::filesystem::absolute(dynamicLibraryPath).string();
|
|
std::string includes = "";
|
|
for (std::string const& dir : {stormIncludeDirectory, sparseppIncludeDirectory, boostIncludeDirectory, carlIncludeDirectory, clnIncludeDirectory, gmpIncludeDirectory}) {
|
|
if (dir != "") {
|
|
includes += " -I" + dir;
|
|
}
|
|
}
|
|
std::string command = compiler + " " + sourceFilename + " " + compilerFlags + includes + " -o " + dynamicLibraryFilename;
|
|
boost::optional<std::string> error = execute(command);
|
|
|
|
if (error) {
|
|
boost::filesystem::remove(sourceFile);
|
|
STORM_LOG_THROW(false, storm::exceptions::InvalidStateException, "Compiling shared library failed. Error: " << error.get());
|
|
}
|
|
|
|
return dynamicLibraryPath;
|
|
}
|
|
|
|
template<typename RationalFunctionType, typename TP = typename RationalFunctionType::PolyType, carl::EnableIf<carl::needs_cache<TP>> = carl::dummy>
|
|
RationalFunctionType convertVariableToPolynomial(storm::RationalFunctionVariable const& variable, std::shared_ptr<storm::RawPolynomialCache> cache) {
|
|
return RationalFunctionType(typename RationalFunctionType::PolyType(typename RationalFunctionType::PolyType::PolyType(variable), cache));
|
|
}
|
|
|
|
template<typename RationalFunctionType, typename TP = typename RationalFunctionType::PolyType, carl::DisableIf<carl::needs_cache<TP>> = carl::dummy>
|
|
RationalFunctionType convertVariableToPolynomial(storm::RationalFunctionVariable const& variable, std::shared_ptr<storm::RawPolynomialCache>) {
|
|
return RationalFunctionType(variable);
|
|
}
|
|
|
|
template<typename ValueType>
|
|
std::vector<storm::RationalFunction> getParameters(storm::jani::Model const&, std::shared_ptr<storm::RawPolynomialCache>) {
|
|
STORM_LOG_THROW(false, storm::exceptions::InvalidStateException, "This function must not be called for this type.");
|
|
}
|
|
|
|
template<>
|
|
std::vector<storm::RationalFunction> getParameters<storm::RationalFunction>(storm::jani::Model const& model, std::shared_ptr<storm::RawPolynomialCache> cache) {
|
|
std::vector<storm::RationalFunction> parameters;
|
|
for (auto const& constant : model.getConstants()) {
|
|
if (!constant.isDefined() && constant.isRealConstant()) {
|
|
parameters.push_back(convertVariableToPolynomial<storm::RationalFunction>(carl::freshRealVariable(constant.getName()), cache));
|
|
}
|
|
}
|
|
return parameters;
|
|
}
|
|
|
|
template <typename ValueType, typename RewardModelType>
|
|
void ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::createBuilder(boost::filesystem::path const& dynamicLibraryPath) {
|
|
jitBuilderCreateFunction = boost::dll::import_alias<typename ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::CreateFunctionType>(dynamicLibraryPath, "create_builder");
|
|
builder = std::unique_ptr<JitModelBuilderInterface<IndexType, ValueType>>(jitBuilderCreateFunction(modelComponentsBuilder));
|
|
|
|
if (std::is_same<storm::RationalFunction, ValueType>::value) {
|
|
typedef void (InitializeParametersFunctionType)(std::vector<storm::RationalFunction> const&);
|
|
typedef boost::function<InitializeParametersFunctionType> ImportInitializeParametersFunctionType;
|
|
|
|
// Create the carl cache if we are building a parametric model.
|
|
cache = std::make_shared<storm::RawPolynomialCache>();
|
|
|
|
ImportInitializeParametersFunctionType initializeParametersFunction = boost::dll::import_alias<InitializeParametersFunctionType>(dynamicLibraryPath, "initialize_parameters");
|
|
std::vector<storm::RationalFunction> parameters = getParameters<ValueType>(this->model, cache);
|
|
initializeParametersFunction(parameters);
|
|
}
|
|
}
|
|
|
|
template class ExplicitJitJaniModelBuilder<double, storm::models::sparse::StandardRewardModel<double>>;
|
|
template class ExplicitJitJaniModelBuilder<storm::RationalNumber, storm::models::sparse::StandardRewardModel<storm::RationalNumber>>;
|
|
template class ExplicitJitJaniModelBuilder<storm::RationalFunction, storm::models::sparse::StandardRewardModel<storm::RationalFunction>>;
|
|
|
|
}
|
|
}
|
|
}
|