314 lines
15 KiB
314 lines
15 KiB
#include "cli.h"
|
|
#include "entrypoints.h"
|
|
|
|
#include "../utility/storm.h"
|
|
|
|
#include "storm/storage/SymbolicModelDescription.h"
|
|
|
|
#include "storm/settings/modules/DebugSettings.h"
|
|
#include "storm/settings/modules/IOSettings.h"
|
|
#include "storm/settings/modules/CoreSettings.h"
|
|
#include "storm/exceptions/OptionParserException.h"
|
|
#include "storm/settings/modules/ResourceSettings.h"
|
|
#include "storm/settings/modules/JaniExportSettings.h"
|
|
|
|
#include "storm/utility/resources.h"
|
|
#include "storm/utility/storm-version.h"
|
|
|
|
|
|
// Includes for the linked libraries and versions header.
|
|
#ifdef STORM_HAVE_INTELTBB
|
|
# include "tbb/tbb_stddef.h"
|
|
#endif
|
|
#ifdef STORM_HAVE_GLPK
|
|
# include "glpk.h"
|
|
#endif
|
|
#ifdef STORM_HAVE_GUROBI
|
|
# include "gurobi_c.h"
|
|
#endif
|
|
#ifdef STORM_HAVE_Z3
|
|
# include "z3.h"
|
|
#endif
|
|
#ifdef STORM_HAVE_MSAT
|
|
# include "mathsat.h"
|
|
#endif
|
|
#ifdef STORM_HAVE_CUDA
|
|
#include <cuda.h>
|
|
#include <cuda_runtime.h>
|
|
#endif
|
|
|
|
#ifdef STORM_HAVE_SMTRAT
|
|
#include "lib/smtrat.h"
|
|
#endif
|
|
|
|
namespace storm {
|
|
namespace cli {
|
|
std::string getCurrentWorkingDirectory() {
|
|
char temp[512];
|
|
return (GetCurrentDir(temp, 512 - 1) ? std::string(temp) : std::string(""));
|
|
}
|
|
|
|
void printHeader(std::string const& name, const int argc, const char* argv[]) {
|
|
STORM_PRINT(name << " " << storm::utility::StormVersion::shortVersionString() << std::endl << std::endl);
|
|
|
|
// "Compute" the command line argument string with which storm was invoked.
|
|
std::stringstream commandStream;
|
|
for (int i = 1; i < argc; ++i) {
|
|
commandStream << argv[i] << " ";
|
|
}
|
|
|
|
std::string command = commandStream.str();
|
|
|
|
if (!command.empty()) {
|
|
STORM_PRINT("Command line arguments: " << commandStream.str() << std::endl);
|
|
STORM_PRINT("Current working directory: " << getCurrentWorkingDirectory() << std::endl << std::endl);
|
|
}
|
|
}
|
|
|
|
void printVersion(std::string const& name) {
|
|
STORM_PRINT(storm::utility::StormVersion::longVersionString() << std::endl);
|
|
STORM_PRINT(storm::utility::StormVersion::buildInfo() << std::endl);
|
|
|
|
#ifdef STORM_HAVE_INTELTBB
|
|
STORM_PRINT("Linked with Intel Threading Building Blocks v" << TBB_VERSION_MAJOR << "." << TBB_VERSION_MINOR << " (Interface version " << TBB_INTERFACE_VERSION << ")." << std::endl);
|
|
#endif
|
|
#ifdef STORM_HAVE_GLPK
|
|
STORM_PRINT("Linked with GNU Linear Programming Kit v" << GLP_MAJOR_VERSION << "." << GLP_MINOR_VERSION << "." << std::endl);
|
|
#endif
|
|
#ifdef STORM_HAVE_GUROBI
|
|
STORM_PRINT("Linked with Gurobi Optimizer v" << GRB_VERSION_MAJOR << "." << GRB_VERSION_MINOR << "." << GRB_VERSION_TECHNICAL << "." << std::endl);
|
|
#endif
|
|
#ifdef STORM_HAVE_Z3
|
|
unsigned int z3Major, z3Minor, z3BuildNumber, z3RevisionNumber;
|
|
Z3_get_version(&z3Major, &z3Minor, &z3BuildNumber, &z3RevisionNumber);
|
|
STORM_PRINT("Linked with Microsoft Z3 Optimizer v" << z3Major << "." << z3Minor << " Build " << z3BuildNumber << " Rev " << z3RevisionNumber << "." << std::endl);
|
|
#endif
|
|
#ifdef STORM_HAVE_MSAT
|
|
char* msatVersion = msat_get_version();
|
|
STORM_PRINT("Linked with " << msatVersion << "." << std::endl);
|
|
msat_free(msatVersion);
|
|
#endif
|
|
#ifdef STORM_HAVE_SMTRAT
|
|
STORM_PRINT("Linked with SMT-RAT " << SMTRAT_VERSION << "." << std::endl);
|
|
#endif
|
|
#ifdef STORM_HAVE_CARL
|
|
// TODO get version string
|
|
STORM_PRINT("Linked with CARL." << std::endl);
|
|
#endif
|
|
|
|
#ifdef STORM_HAVE_CUDA
|
|
int deviceCount = 0;
|
|
cudaError_t error_id = cudaGetDeviceCount(&deviceCount);
|
|
|
|
if (error_id == cudaSuccess) {
|
|
STORM_PRINT("Compiled with CUDA support, ");
|
|
// This function call returns 0 if there are no CUDA capable devices.
|
|
if (deviceCount == 0){
|
|
STORM_PRINT("but there are no available device(s) that support CUDA." << std::endl);
|
|
} else {
|
|
STORM_PRINT("detected " << deviceCount << " CUDA capable device(s):" << std::endl);
|
|
}
|
|
|
|
int dev, driverVersion = 0, runtimeVersion = 0;
|
|
|
|
for (dev = 0; dev < deviceCount; ++dev) {
|
|
cudaSetDevice(dev);
|
|
cudaDeviceProp deviceProp;
|
|
cudaGetDeviceProperties(&deviceProp, dev);
|
|
|
|
STORM_PRINT("CUDA device " << dev << ": \"" << deviceProp.name << "\"" << std::endl);
|
|
|
|
// Console log
|
|
cudaDriverGetVersion(&driverVersion);
|
|
cudaRuntimeGetVersion(&runtimeVersion);
|
|
STORM_PRINT(" CUDA Driver Version / Runtime Version " << driverVersion / 1000 << "." << (driverVersion % 100) / 10 << " / " << runtimeVersion / 1000 << "." << (runtimeVersion % 100) / 10 << std::endl);
|
|
STORM_PRINT(" CUDA Capability Major/Minor version number: " << deviceProp.major << "." << deviceProp.minor << std::endl);
|
|
}
|
|
STORM_PRINT(std::endl);
|
|
}
|
|
else {
|
|
STORM_PRINT("Compiled with CUDA support, but an error occured trying to find CUDA devices." << std::endl);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void showTimeAndMemoryStatistics(uint64_t wallclockMilliseconds) {
|
|
struct rusage ru;
|
|
getrusage(RUSAGE_SELF, &ru);
|
|
|
|
std::cout << std::endl << "Performance statistics:" << std::endl;
|
|
#ifdef MACOS
|
|
// For Mac OS, this is returned in bytes.
|
|
uint64_t maximumResidentSizeInMegabytes = ru.ru_maxrss / 1024 / 1024;
|
|
#endif
|
|
#ifdef LINUX
|
|
// For Linux, this is returned in kilobytes.
|
|
uint64_t maximumResidentSizeInMegabytes = ru.ru_maxrss / 1024;
|
|
#endif
|
|
std::cout << " * peak memory usage: " << maximumResidentSizeInMegabytes << "MB" << std::endl;
|
|
std::cout << " * CPU time: " << ru.ru_utime.tv_sec << "." << std::setw(3) << std::setfill('0') << ru.ru_utime.tv_usec/1000 << "s" << std::endl;
|
|
if (wallclockMilliseconds != 0) {
|
|
std::cout << " * wallclock time: " << (wallclockMilliseconds/1000) << "." << std::setw(3) << std::setfill('0') << (wallclockMilliseconds % 1000) << "s" << std::endl;
|
|
}
|
|
}
|
|
|
|
bool parseOptions(const int argc, const char* argv[]) {
|
|
try {
|
|
storm::settings::mutableManager().setFromCommandLine(argc, argv);
|
|
} catch (storm::exceptions::OptionParserException& e) {
|
|
storm::settings::manager().printHelp();
|
|
throw e;
|
|
return false;
|
|
}
|
|
|
|
storm::settings::modules::GeneralSettings const& general = storm::settings::getModule<storm::settings::modules::GeneralSettings>();
|
|
storm::settings::modules::ResourceSettings const& resources = storm::settings::getModule<storm::settings::modules::ResourceSettings>();
|
|
storm::settings::modules::DebugSettings const& debug = storm::settings::getModule<storm::settings::modules::DebugSettings>();
|
|
|
|
if (general.isHelpSet()) {
|
|
storm::settings::manager().printHelp(storm::settings::getModule<storm::settings::modules::GeneralSettings>().getHelpModuleName());
|
|
return false;
|
|
}
|
|
// If we were given a time limit, we put it in place now.
|
|
if (resources.isTimeoutSet()) {
|
|
storm::utility::resources::setCPULimit(resources.getTimeoutInSeconds());
|
|
}
|
|
|
|
if (general.isVersionSet()) {
|
|
printVersion("storm");
|
|
return false;
|
|
}
|
|
|
|
if (general.isVerboseSet()) {
|
|
storm::utility::setLogLevel(l3pp::LogLevel::INFO);
|
|
}
|
|
if (debug.isDebugSet()) {
|
|
storm::utility::setLogLevel(l3pp::LogLevel::DEBUG);
|
|
}
|
|
if (debug.isTraceSet()) {
|
|
storm::utility::setLogLevel(l3pp::LogLevel::TRACE);
|
|
}
|
|
if (debug.isLogfileSet()) {
|
|
storm::utility::initializeFileLogging();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void processOptions() {
|
|
STORM_LOG_TRACE("Processing options.");
|
|
if (storm::settings::getModule<storm::settings::modules::DebugSettings>().isLogfileSet()) {
|
|
storm::utility::initializeFileLogging();
|
|
}
|
|
|
|
boost::optional<std::set<std::string>> propertyFilter;
|
|
std::string propertyFilterString = storm::settings::getModule<storm::settings::modules::IOSettings>().getPropertyFilter();
|
|
if (propertyFilterString != "all") {
|
|
propertyFilter = storm::parsePropertyFilter(storm::settings::getModule<storm::settings::modules::IOSettings>().getPropertyFilter());
|
|
}
|
|
|
|
auto coreSettings = storm::settings::getModule<storm::settings::modules::CoreSettings>();
|
|
auto generalSettings = storm::settings::getModule<storm::settings::modules::GeneralSettings>();
|
|
auto ioSettings = storm::settings::getModule<storm::settings::modules::IOSettings>();
|
|
if (ioSettings.isPrismOrJaniInputSet()) {
|
|
storm::storage::SymbolicModelDescription model;
|
|
std::vector<storm::jani::Property> properties;
|
|
|
|
STORM_LOG_TRACE("Parsing symbolic input.");
|
|
boost::optional<std::map<std::string, std::string>> labelRenaming;
|
|
if (ioSettings.isPrismInputSet()) {
|
|
model = storm::parseProgram(ioSettings.getPrismInputFilename());
|
|
|
|
bool transformToJani = ioSettings.isPrismToJaniSet();
|
|
bool transformToJaniForJit = coreSettings.getEngine() == storm::settings::modules::CoreSettings::Engine::Sparse && ioSettings.isJitSet();
|
|
STORM_LOG_WARN_COND(transformToJani || !transformToJaniForJit, "The JIT-based model builder is only available for JANI models, automatically converting the PRISM input model.");
|
|
transformToJani |= transformToJaniForJit;
|
|
|
|
if (transformToJani) {
|
|
auto modelAndRenaming = model.toJaniWithLabelRenaming(true);
|
|
if (!modelAndRenaming.second.empty()) {
|
|
labelRenaming = modelAndRenaming.second;
|
|
}
|
|
model = modelAndRenaming.first;
|
|
}
|
|
} else if (ioSettings.isJaniInputSet()) {
|
|
auto input = storm::parseJaniModel(ioSettings.getJaniInputFilename());
|
|
model = input.first;
|
|
if (ioSettings.isJaniPropertiesSet()) {
|
|
for (auto const& propName : ioSettings.getJaniProperties()) {
|
|
STORM_LOG_THROW(input.second.count(propName) == 1, storm::exceptions::InvalidArgumentException, "No property with name " << propName << " known.");
|
|
properties.push_back(input.second.at(propName));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// Get the string that assigns values to the unknown currently undefined constants in the model and formula.
|
|
std::string constantDefinitionString = ioSettings.getConstantDefinitionString();
|
|
std::map<storm::expressions::Variable, storm::expressions::Expression> constantDefinitions;
|
|
|
|
// Then proceed to parsing the properties (if given), since the model we are building may depend on the property.
|
|
STORM_LOG_TRACE("Parsing properties.");
|
|
if (ioSettings.isPropertySet()) {
|
|
if (model.isJaniModel()) {
|
|
properties = storm::parsePropertiesForJaniModel(ioSettings.getProperty(), model.asJaniModel(), propertyFilter);
|
|
|
|
if (labelRenaming) {
|
|
std::vector<storm::jani::Property> amendedProperties;
|
|
for (auto const& property : properties) {
|
|
amendedProperties.emplace_back(property.substituteLabels(labelRenaming.get()));
|
|
}
|
|
properties = std::move(amendedProperties);
|
|
}
|
|
} else {
|
|
properties = storm::parsePropertiesForPrismProgram(ioSettings.getProperty(), model.asPrismProgram(), propertyFilter);
|
|
}
|
|
|
|
constantDefinitions = model.parseConstantDefinitions(constantDefinitionString);
|
|
properties = substituteConstantsInProperties(properties, constantDefinitions);
|
|
} else {
|
|
constantDefinitions = model.parseConstantDefinitions(constantDefinitionString);
|
|
}
|
|
model = model.preprocess(constantDefinitions);
|
|
|
|
if (model.isJaniModel() && storm::settings::getModule<storm::settings::modules::JaniExportSettings>().isJaniFileSet()) {
|
|
exportJaniModel(model.asJaniModel(), properties, storm::settings::getModule<storm::settings::modules::JaniExportSettings>().getJaniFilename());
|
|
}
|
|
|
|
if (ioSettings.isNoBuildModelSet()) {
|
|
return;
|
|
}
|
|
|
|
STORM_LOG_TRACE("Building and checking symbolic model.");
|
|
if (generalSettings.isParametricSet()) {
|
|
#ifdef STORM_HAVE_CARL
|
|
buildAndCheckSymbolicModel<storm::RationalFunction>(model, properties, true);
|
|
#else
|
|
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "No parameters are supported in this build.");
|
|
#endif
|
|
} else if (generalSettings.isExactSet()) {
|
|
#ifdef STORM_HAVE_CARL
|
|
buildAndCheckSymbolicModel<storm::RationalNumber>(model, properties, true);
|
|
#else
|
|
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "No exact numbers are supported in this build.");
|
|
#endif
|
|
} else {
|
|
buildAndCheckSymbolicModel<double>(model, properties, true);
|
|
}
|
|
} else if (ioSettings.isExplicitSet()) {
|
|
STORM_LOG_THROW(coreSettings.getEngine() == storm::settings::modules::CoreSettings::Engine::Sparse, storm::exceptions::InvalidSettingsException, "Only the sparse engine supports explicit model input.");
|
|
|
|
// If the model is given in an explicit format, we parse the properties without allowing expressions
|
|
// in formulas.
|
|
std::vector<storm::jani::Property> properties;
|
|
if (ioSettings.isPropertySet()) {
|
|
properties = storm::parsePropertiesForExplicit(ioSettings.getProperty(), propertyFilter);
|
|
}
|
|
|
|
buildAndCheckExplicitModel<double>(properties, true);
|
|
} else {
|
|
STORM_LOG_THROW(false, storm::exceptions::InvalidSettingsException, "No input model.");
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|