From a7e9c5819ff8100cc2a704fc134840c144088dc2 Mon Sep 17 00:00:00 2001
From: dehnert <dehnert@cs.rwth-aachen.de>
Date: Sat, 14 Jan 2017 18:31:44 +0100
Subject: [PATCH] removed 'size-in-memory' output as it was outdated and
 unreliable. added timing measurements for model construction and model
 checking

---
 .../jit/ExplicitJitJaniModelBuilder.cpp       |   2 +-
 src/storm/cli/cli.cpp                         |  43 +++++--
 src/storm/cli/entrypoints.h                   | 116 +++++++++++-------
 src/storm/logic/Formula.cpp                   |   5 +
 src/storm/logic/Formula.h                     |   1 +
 src/storm/logic/LabelSubstitutionVisitor.cpp  |  25 +++-
 src/storm/logic/LabelSubstitutionVisitor.h    |   4 +-
 .../modelchecker/results/CheckResult.cpp      |   2 +-
 src/storm/modelchecker/results/CheckResult.h  |   2 +-
 src/storm/models/ModelBase.h                  |   9 +-
 src/storm/models/sparse/MarkovAutomaton.cpp   |   5 -
 src/storm/models/sparse/MarkovAutomaton.h     |   4 +-
 src/storm/models/sparse/Model.cpp             |  13 --
 src/storm/models/sparse/Model.h               |   9 +-
 .../models/sparse/StandardRewardModel.cpp     |  16 ---
 src/storm/models/sparse/StandardRewardModel.h |   7 --
 src/storm/models/sparse/StateLabeling.cpp     |   8 --
 src/storm/models/sparse/StateLabeling.h       |   9 +-
 src/storm/models/symbolic/Model.cpp           |   7 --
 src/storm/models/symbolic/Model.h             |   2 -
 .../DeterministicSparseTransitionParser.cpp   |   2 +-
 src/storm/settings/modules/IOSettings.cpp     |   1 -
 .../solver/GmmxxLinearEquationSolver.cpp      |   6 +-
 src/storm/storage/SparseMatrix.cpp            |  13 --
 src/storm/storage/SparseMatrix.h              |   7 --
 .../storage/SymbolicModelDescription.cpp      |  12 ++
 src/storm/storage/SymbolicModelDescription.h  |   1 +
 src/storm/storage/jani/Property.cpp           |   4 +
 src/storm/storage/jani/Property.h             |   6 +
 src/storm/storage/prism/Program.cpp           |  14 ++-
 src/storm/storage/prism/Program.h             |   7 ++
 src/storm/storage/prism/ToJaniConverter.cpp   |  19 ++-
 src/storm/storage/prism/ToJaniConverter.h     |  11 +-
 src/storm/storm.cpp                           |   2 +-
 src/storm/utility/Stopwatch.cpp               |  51 ++++++++
 src/storm/utility/Stopwatch.h                 |  86 ++++---------
 src/storm/utility/storm.h                     |  18 ++-
 37 files changed, 300 insertions(+), 249 deletions(-)
 create mode 100644 src/storm/utility/Stopwatch.cpp

diff --git a/src/storm/builder/jit/ExplicitJitJaniModelBuilder.cpp b/src/storm/builder/jit/ExplicitJitJaniModelBuilder.cpp
index 0f37d1b45..2f3b639bd 100644
--- a/src/storm/builder/jit/ExplicitJitJaniModelBuilder.cpp
+++ b/src/storm/builder/jit/ExplicitJitJaniModelBuilder.cpp
@@ -1546,7 +1546,7 @@ namespace storm {
                         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) {
+                        if (!terminalEntry.second) {
                             labelExpression = !labelExpression;
                         }
                         terminalExpressions.push_back(expressionTranslator.translate(shiftVariablesWrtLowerBound(labelExpression), storm::expressions::ToCppTranslationOptions(variablePrefixes, variableToName)));
diff --git a/src/storm/cli/cli.cpp b/src/storm/cli/cli.cpp
index e8f1e5f8b..7d0d514cb 100644
--- a/src/storm/cli/cli.cpp
+++ b/src/storm/cli/cli.cpp
@@ -227,16 +227,29 @@ namespace storm {
                 propertyFilter = storm::parsePropertyFilter(storm::settings::getModule<storm::settings::modules::GeneralSettings>().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());
-                    if (ioSettings.isPrismToJaniSet()) {
-                        model = model.toJani(true);
+                    
+                    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());
@@ -256,11 +269,19 @@ namespace storm {
 
                 // 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 (storm::settings::getModule<storm::settings::modules::GeneralSettings>().isPropertySet()) {
+                if (generalSettings.isPropertySet()) {
                     if (model.isJaniModel()) {
-                        properties = storm::parsePropertiesForJaniModel(storm::settings::getModule<storm::settings::modules::GeneralSettings>().getProperty(), model.asJaniModel(), propertyFilter);
+                        properties = storm::parsePropertiesForJaniModel(generalSettings.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(storm::settings::getModule<storm::settings::modules::GeneralSettings>().getProperty(), model.asPrismProgram(), propertyFilter);
+                        properties = storm::parsePropertiesForPrismProgram(generalSettings.getProperty(), model.asPrismProgram(), propertyFilter);
                     }
                     
                     constantDefinitions = model.parseConstantDefinitions(constantDefinitionString);
@@ -279,13 +300,13 @@ namespace storm {
                 }
                 
                 STORM_LOG_TRACE("Building and checking symbolic model.");
-                if (storm::settings::getModule<storm::settings::modules::GeneralSettings>().isParametricSet()) {
+                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 (storm::settings::getModule<storm::settings::modules::GeneralSettings>().isExactSet()) {
+                } else if (generalSettings.isExactSet()) {
 #ifdef STORM_HAVE_CARL
                     buildAndCheckSymbolicModel<storm::RationalNumber>(model, properties, true);
 #else
@@ -294,14 +315,14 @@ namespace storm {
                 } else {
                     buildAndCheckSymbolicModel<double>(model, properties, true);
                 }
-            } else if (storm::settings::getModule<storm::settings::modules::IOSettings>().isExplicitSet()) {
-                STORM_LOG_THROW(storm::settings::getModule<storm::settings::modules::CoreSettings>().getEngine() == storm::settings::modules::CoreSettings::Engine::Sparse, storm::exceptions::InvalidSettingsException, "Only the sparse engine supports explicit model input.");
+            } 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 (storm::settings::getModule<storm::settings::modules::GeneralSettings>().isPropertySet()) {
-                    properties = storm::parsePropertiesForExplicit(storm::settings::getModule<storm::settings::modules::GeneralSettings>().getProperty(), propertyFilter);
+                if (generalSettings.isPropertySet()) {
+                    properties = storm::parsePropertiesForExplicit(generalSettings.getProperty(), propertyFilter);
                 }
 
                 buildAndCheckExplicitModel<double>(properties, true);
diff --git a/src/storm/cli/entrypoints.h b/src/storm/cli/entrypoints.h
index 9462f2643..4097d0bc3 100644
--- a/src/storm/cli/entrypoints.h
+++ b/src/storm/cli/entrypoints.h
@@ -7,6 +7,7 @@
 
 #include "storm/storage/SymbolicModelDescription.h"
 #include "storm/utility/ExplicitExporter.h"
+#include "storm/utility/Stopwatch.h"
 
 #include "storm/exceptions/NotImplementedException.h"
 #include "storm/exceptions/InvalidSettingsException.h"
@@ -18,23 +19,22 @@ namespace storm {
 
         template<typename ValueType>
         void applyFilterFunctionAndOutput(std::unique_ptr<storm::modelchecker::CheckResult> const& result, storm::modelchecker::FilterType ft) {
-            
-            if(result->isQuantitative()) {
-                switch(ft) {
+            if (result->isQuantitative()) {
+                switch (ft) {
                     case storm::modelchecker::FilterType::VALUES:
-                        std::cout << *result << std::endl;
+                        STORM_PRINT_AND_LOG(*result << std::endl);
                         return;
                     case storm::modelchecker::FilterType::SUM:
-                        std::cout << result->asQuantitativeCheckResult<ValueType>().sum();
+                        STORM_PRINT_AND_LOG(result->asQuantitativeCheckResult<ValueType>().sum());
                         return;
                     case storm::modelchecker::FilterType::AVG:
-                        std::cout << result->asQuantitativeCheckResult<ValueType>().average();
+                        STORM_PRINT_AND_LOG(result->asQuantitativeCheckResult<ValueType>().average());
                         return;
                     case storm::modelchecker::FilterType::MIN:
-                        std::cout << result->asQuantitativeCheckResult<ValueType>().getMin();
+                        STORM_PRINT_AND_LOG(result->asQuantitativeCheckResult<ValueType>().getMin());
                         return;
                     case storm::modelchecker::FilterType::MAX:
-                        std::cout << result->asQuantitativeCheckResult<ValueType>().getMax();
+                        STORM_PRINT_AND_LOG(result->asQuantitativeCheckResult<ValueType>().getMax());
                         return;
                     case storm::modelchecker::FilterType::ARGMIN:
                     case storm::modelchecker::FilterType::ARGMAX:
@@ -45,18 +45,18 @@ namespace storm {
                         STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "FilterType only defined for qualitative results");
                 }
             } else {
-                switch(ft) {
+                switch (ft) {
                     case storm::modelchecker::FilterType::VALUES:
-                        std::cout << *result << std::endl;
+                        STORM_PRINT_AND_LOG(*result << std::endl);
                         return;
                     case storm::modelchecker::FilterType::EXISTS:
-                        std::cout << result->asQualitativeCheckResult().existsTrue();
+                        STORM_PRINT_AND_LOG(result->asQualitativeCheckResult().existsTrue());
                         return;
                     case storm::modelchecker::FilterType::FORALL:
-                        std::cout << result->asQualitativeCheckResult().forallTrue();
+                        STORM_PRINT_AND_LOG(result->asQualitativeCheckResult().forallTrue());
                         return;
                     case storm::modelchecker::FilterType::COUNT:
-                        std::cout << result->asQualitativeCheckResult().count();
+                        STORM_PRINT_AND_LOG(result->asQualitativeCheckResult().count());
                         return;
                         
                     case storm::modelchecker::FilterType::ARGMIN:
@@ -75,17 +75,19 @@ namespace storm {
         template<typename ValueType>
         void verifySparseModel(std::shared_ptr<storm::models::sparse::Model<ValueType>> model, std::vector<storm::jani::Property> const& properties, bool onlyInitialStatesRelevant = false) {
             for (auto const& property : properties) {
-                std::cout << std::endl << "Model checking property: " << property << " ...";
+                STORM_PRINT_AND_LOG(std::endl << "Model checking property: " << *property.getRawFormula() << " ..." << std::endl);
                 std::cout.flush();
+                storm::utility::Stopwatch modelCheckingWatch(true);
                 std::unique_ptr<storm::modelchecker::CheckResult> result(storm::verifySparseModel(model, property.getFilter().getFormula(), onlyInitialStatesRelevant));
+                modelCheckingWatch.stop();
                 if (result) {
-                    std::cout << " done." << std::endl;
-                    std::cout << "Result (initial states): ";
+                    STORM_PRINT_AND_LOG("Time for model checking: " << modelCheckingWatch << "." << std::endl);
+                    STORM_PRINT_AND_LOG("Result (initial states): ");
                     result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model->getInitialStates()));
                     applyFilterFunctionAndOutput<ValueType>(result, property.getFilter().getFilterType());
-                    std::cout << std::endl;
+                    STORM_PRINT_AND_LOG(std::endl);
                 } else {
-                    std::cout << " skipped, because the modelling formalism is currently unsupported." << std::endl;
+                    STORM_PRINT_AND_LOG(" skipped, because the modelling formalism is currently unsupported." << std::endl);
                 }
             }
         }
@@ -96,17 +98,19 @@ namespace storm {
 
             for (auto const& property : properties) {
                 STORM_LOG_THROW(model->getType() == storm::models::ModelType::Dtmc || model->getType() == storm::models::ModelType::Ctmc, storm::exceptions::InvalidSettingsException, "Currently parametric verification is only available for DTMCs and CTMCs.");
-                std::cout << std::endl << "Model checking property: " << property << " ...";
+                STORM_PRINT_AND_LOG(std::endl << "Model checking property: " << *property.getRawFormula() << " ..." << std::endl);
                 std::cout.flush();
+                storm::utility::Stopwatch modelCheckingWatch(true);
                 std::unique_ptr<storm::modelchecker::CheckResult> result(storm::verifySparseModel(model, property.getFilter().getFormula(), onlyInitialStatesRelevant));
+                modelCheckingWatch.stop();
                 if (result) {
-                    std::cout << " done." << std::endl;
-                    std::cout << "Result (initial states): ";
+                    STORM_PRINT_AND_LOG("Time for model checking: " << modelCheckingWatch << "." << std::endl);
+                    STORM_PRINT_AND_LOG("Result (initial states): ");
                     result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model->getInitialStates()));
                     applyFilterFunctionAndOutput<storm::RationalFunction>(result, property.getFilter().getFilterType());
-                    std::cout << std::endl;
+                    STORM_PRINT_AND_LOG(std::endl);
                 } else {
-                    std::cout << " skipped, because the modelling formalism is currently unsupported." << std::endl;
+                    STORM_PRINT_AND_LOG(" skipped, because the modelling formalism is currently unsupported." << std::endl);
                 }
 
                 if (storm::settings::getModule<storm::settings::modules::ParametricSettings>().exportResultToFile()) {
@@ -120,15 +124,17 @@ namespace storm {
         void verifySymbolicModelWithAbstractionRefinementEngine(storm::storage::SymbolicModelDescription const& model, std::vector<storm::jani::Property> const& properties, bool onlyInitialStatesRelevant = false) {
             typedef double ValueType;
             for (auto const& property : properties) {
-                std::cout << std::endl << "Model checking property: " << property << " ...";
+                STORM_PRINT_AND_LOG(std::endl << "Model checking property: " << *property.getRawFormula() << " ..." << std::endl);
                 std::cout.flush();
+                storm::utility::Stopwatch modelCheckingWatch(true);
                 std::unique_ptr<storm::modelchecker::CheckResult> result(storm::verifySymbolicModelWithAbstractionRefinementEngine<DdType, ValueType>(model, property.getFilter().getFormula(), onlyInitialStatesRelevant));
+                modelCheckingWatch.stop();
                 if (result) {
-                    std::cout << " done." << std::endl;
-                    std::cout << "Result (initial states): ";
-                    std::cout << *result << std::endl;
+                    STORM_PRINT_AND_LOG("Time for model checking: " << modelCheckingWatch << "." << std::endl);
+                    STORM_PRINT_AND_LOG("Result (initial states): ");
+                    STORM_PRINT_AND_LOG(*result << std::endl);
                 } else {
-                    std::cout << " skipped, because the modelling formalism is currently unsupported." << std::endl;
+                    STORM_PRINT_AND_LOG(" skipped, because the modelling formalism is currently unsupported." << std::endl);
                 }
             }
         }
@@ -140,18 +146,22 @@ namespace storm {
             STORM_LOG_THROW(program.getModelType() == storm::prism::Program::ModelType::DTMC || program.getModelType() == storm::prism::Program::ModelType::MDP, storm::exceptions::InvalidSettingsException, "Currently exploration-based verification is only available for DTMCs and MDPs.");
 
             for (auto const& property : formulas) {
-                std::cout << std::endl << "Model checking property: " << property << " ...";
+                STORM_PRINT_AND_LOG(std::endl << "Model checking property: " << *property.getRawFormula() << " ..." << std::endl);
                 std::cout.flush();
                 bool formulaSupported = false;
                 std::unique_ptr<storm::modelchecker::CheckResult> result;
 
+                storm::utility::Stopwatch modelCheckingWatch(false);
+                
                 if (program.getModelType() == storm::prism::Program::ModelType::DTMC) {
                     storm::modelchecker::SparseExplorationModelChecker<storm::models::sparse::Dtmc<ValueType>> checker(program);
                     storm::modelchecker::CheckTask<storm::logic::Formula> task(*property.getFilter().getFormula(), onlyInitialStatesRelevant);
 
                     formulaSupported = checker.canHandle(task);
                     if (formulaSupported) {
+                        modelCheckingWatch.start();
                         result = checker.check(task);
+                        modelCheckingWatch.stop();
                     }
                 } else if (program.getModelType() == storm::prism::Program::ModelType::MDP) {
                     storm::modelchecker::SparseExplorationModelChecker<storm::models::sparse::Mdp<ValueType>> checker(program);
@@ -159,23 +169,25 @@ namespace storm {
 
                     formulaSupported = checker.canHandle(task);
                     if (formulaSupported) {
+                        modelCheckingWatch.start();
                         result = checker.check(task);
+                        modelCheckingWatch.stop();
                     }
                 } else {
                     // Should be catched before.
                     assert(false);
                 }
                 if (!formulaSupported) {
-                    std::cout << " skipped, because the formula cannot be handled by the selected engine/method." << std::endl;
+                    STORM_PRINT_AND_LOG(" skipped, because the formula cannot be handled by the selected engine/method." << std::endl);
                 }
 
                 if (result) {
-                    std::cout << " done." << std::endl;
-                    std::cout << "Result (initial states): ";
+                    STORM_PRINT_AND_LOG("Time for model checking: " << modelCheckingWatch << "." << std::endl);
+                    STORM_PRINT_AND_LOG("Result (initial states): ");
                     applyFilterFunctionAndOutput<ValueType>(result, property.getFilter().getFilterType());
-                    std::cout << std::endl;
+                    STORM_PRINT_AND_LOG(std::endl);
                 } else {
-                    std::cout << " skipped, because the modelling formalism is currently unsupported." << std::endl;
+                    STORM_PRINT_AND_LOG(" skipped, because the modelling formalism is currently unsupported." << std::endl);
                 }
             }
         }
@@ -190,18 +202,21 @@ namespace storm {
         template<storm::dd::DdType DdType>
         void verifySymbolicModelWithHybridEngine(std::shared_ptr<storm::models::symbolic::Model<DdType>> model, std::vector<storm::jani::Property> const& formulas, bool onlyInitialStatesRelevant = false) {
             for (auto const& property : formulas) {
-                std::cout << std::endl << "Model checking property: " << property << " ...";
+                STORM_PRINT_AND_LOG(std::endl << "Model checking property: " << *property.getRawFormula() << " ..." << std::endl);
                 std::cout.flush();
+                
+                storm::utility::Stopwatch modelCheckingWatch(true);
                 std::unique_ptr<storm::modelchecker::CheckResult> result(storm::verifySymbolicModelWithHybridEngine(model, property.getFilter().getFormula(), onlyInitialStatesRelevant));
+                modelCheckingWatch.stop();
 
                 if (result) {
-                    std::cout << " done." << std::endl;
-                    std::cout << "Result (initial states): ";
+                    STORM_PRINT_AND_LOG("Time for model checking: " << modelCheckingWatch << "." << std::endl);
+                    STORM_PRINT_AND_LOG("Result (initial states): ");
                     result->filter(storm::modelchecker::SymbolicQualitativeCheckResult<DdType>(model->getReachableStates(), model->getInitialStates()));
                     applyFilterFunctionAndOutput<double>(result, property.getFilter().getFilterType());
-                    std::cout << std::endl;
+                    STORM_PRINT_AND_LOG(std::endl);
                 } else {
-                    std::cout << " skipped, because the modelling formalism is currently unsupported." << std::endl;
+                    STORM_PRINT_AND_LOG(" skipped, because the modelling formalism is currently unsupported." << std::endl);
                 }
             }
         }
@@ -209,17 +224,20 @@ namespace storm {
         template<storm::dd::DdType DdType>
         void verifySymbolicModelWithDdEngine(std::shared_ptr<storm::models::symbolic::Model<DdType>> model, std::vector<storm::jani::Property> const& formulas, bool onlyInitialStatesRelevant = false) {
             for (auto const& property : formulas) {
-                std::cout << std::endl << "Model checking property: " << property << " ...";
+                STORM_PRINT_AND_LOG(std::endl << "Model checking property: " << *property.getRawFormula() << " ..." << std::endl);
                 std::cout.flush();
+                
+                storm::utility::Stopwatch modelCheckingWatch(true);
                 std::unique_ptr<storm::modelchecker::CheckResult> result(storm::verifySymbolicModelWithDdEngine(model, property.getFilter().getFormula(), onlyInitialStatesRelevant));
+                modelCheckingWatch.stop();
                 if (result) {
-                    std::cout << " done." << std::endl;
-                    std::cout << "Result (initial states): ";
+                    STORM_PRINT_AND_LOG("Time for model checking: " << modelCheckingWatch << "." << std::endl);
+                    STORM_PRINT_AND_LOG("Result (initial states): ");
                     result->filter(storm::modelchecker::SymbolicQualitativeCheckResult<DdType>(model->getReachableStates(), model->getInitialStates()));
                     applyFilterFunctionAndOutput<double>(result, property.getFilter().getFilterType());
-                    std::cout << std::endl;
+                    STORM_PRINT_AND_LOG(std::endl);
                 } else {
-                    std::cout << " skipped, because the modelling formalism is currently unsupported." << std::endl;
+                    STORM_PRINT_AND_LOG(" skipped, because the modelling formalism is currently unsupported." << std::endl);
                 }
             }
         }
@@ -267,7 +285,10 @@ namespace storm {
         template<storm::dd::DdType LibraryType>
         void buildAndCheckSymbolicModelWithSymbolicEngine(bool hybrid, storm::storage::SymbolicModelDescription const& model, std::vector<storm::jani::Property> const& properties, bool onlyInitialStatesRelevant = false) {
             // Start by building the model.
+            storm::utility::Stopwatch modelBuildingWatch(true);
             auto markovModel = buildSymbolicModel<double, LibraryType>(model, extractFormulasFromProperties(properties));
+            modelBuildingWatch.stop();
+            STORM_PRINT_AND_LOG("Time for model construction: " << modelBuildingWatch << "." << std::endl << std::endl);
             
             // Print some information about the model.
             markovModel->printModelInformationToStream(std::cout);
@@ -284,7 +305,10 @@ namespace storm {
         void buildAndCheckSymbolicModelWithSparseEngine(storm::storage::SymbolicModelDescription const& model, std::vector<storm::jani::Property> const& properties, bool onlyInitialStatesRelevant = false) {
             auto formulas = extractFormulasFromProperties(properties);
             // Start by building the model.
+            storm::utility::Stopwatch modelBuildingWatch(true);
             std::shared_ptr<storm::models::ModelBase> markovModel = buildSparseModel<ValueType>(model, formulas);
+            modelBuildingWatch.stop();
+            STORM_PRINT_AND_LOG("Time for model construction: " << modelBuildingWatch << "." << std::endl << std::endl);
 
             STORM_LOG_THROW(markovModel, storm::exceptions::UnexpectedException, "The model was not successfully built.");
             
@@ -359,7 +383,11 @@ namespace storm {
             storm::settings::modules::IOSettings const& settings = storm::settings::getModule<storm::settings::modules::IOSettings>();
 
             STORM_LOG_THROW(settings.isExplicitSet(), storm::exceptions::InvalidStateException, "Unable to build explicit model without model files.");
+
+            storm::utility::Stopwatch modelBuildingWatch(true);
             std::shared_ptr<storm::models::ModelBase> model = buildExplicitModel<ValueType>(settings.getTransitionFilename(), settings.getLabelingFilename(), settings.isStateRewardsSet() ? boost::optional<std::string>(settings.getStateRewardsFilename()) : boost::none, settings.isTransitionRewardsSet() ? boost::optional<std::string>(settings.getTransitionRewardsFilename()) : boost::none, settings.isChoiceLabelingSet() ? boost::optional<std::string>(settings.getChoiceLabelingFilename()) : boost::none);
+            modelBuildingWatch.stop();
+            STORM_PRINT_AND_LOG("Time for model construction: " << modelBuildingWatch << "." << std::endl);
             
             // Preprocess the model if needed.
             BRANCH_ON_MODELTYPE(model, model, ValueType, storm::dd::DdType::CUDD, preprocessModel, extractFormulasFromProperties(properties));
diff --git a/src/storm/logic/Formula.cpp b/src/storm/logic/Formula.cpp
index d61129914..9038a2307 100644
--- a/src/storm/logic/Formula.cpp
+++ b/src/storm/logic/Formula.cpp
@@ -443,6 +443,11 @@ namespace storm {
             return visitor.substitute(*this);
         }
         
+        std::shared_ptr<Formula> Formula::substitute(std::map<std::string, std::string> const& labelSubstitution) const {
+            LabelSubstitutionVisitor visitor(labelSubstitution);
+            return visitor.substitute(*this);
+        }
+        
         storm::expressions::Expression Formula::toExpression(storm::expressions::ExpressionManager const& manager, std::map<std::string, storm::expressions::Expression> const& labelToExpressionMapping) const {
             ToExpressionVisitor visitor;
             if (labelToExpressionMapping.empty()) {
diff --git a/src/storm/logic/Formula.h b/src/storm/logic/Formula.h
index 80f31203f..2ad5e9477 100644
--- a/src/storm/logic/Formula.h
+++ b/src/storm/logic/Formula.h
@@ -197,6 +197,7 @@ namespace storm {
             
             std::shared_ptr<Formula> substitute(std::map<storm::expressions::Variable, storm::expressions::Expression> const& substitution) const;
             std::shared_ptr<Formula> substitute(std::map<std::string, storm::expressions::Expression> const& labelSubstitution) const;
+            std::shared_ptr<Formula> substitute(std::map<std::string, std::string> const& labelSubstitution) const;
 
             /*!
              * Takes the formula and converts it to an equivalent expression. The formula may contain atomic labels, but
diff --git a/src/storm/logic/LabelSubstitutionVisitor.cpp b/src/storm/logic/LabelSubstitutionVisitor.cpp
index 9794b2530..1e17649a3 100644
--- a/src/storm/logic/LabelSubstitutionVisitor.cpp
+++ b/src/storm/logic/LabelSubstitutionVisitor.cpp
@@ -5,22 +5,35 @@
 namespace storm {
     namespace logic {
         
-        LabelSubstitutionVisitor::LabelSubstitutionVisitor(std::map<std::string, storm::expressions::Expression> const& labelToExpressionMapping) : labelToExpressionMapping(labelToExpressionMapping) {
+        LabelSubstitutionVisitor::LabelSubstitutionVisitor(std::map<std::string, storm::expressions::Expression> const& labelToExpressionMapping) : labelToExpressionMapping(&labelToExpressionMapping), labelToLabelMapping(nullptr) {
             // Intentionally left empty.
         }
 
+        LabelSubstitutionVisitor::LabelSubstitutionVisitor(std::map<std::string, std::string> const& labelToLabelMapping) : labelToExpressionMapping(nullptr), labelToLabelMapping(&labelToLabelMapping) {
+            // Intentionally left empty.
+        }
+        
         std::shared_ptr<Formula> LabelSubstitutionVisitor::substitute(Formula const& f) const {
             boost::any result = f.accept(*this, boost::any());
             return boost::any_cast<std::shared_ptr<Formula>>(result);
         }
         
         boost::any LabelSubstitutionVisitor::visit(AtomicLabelFormula const& f, boost::any const&) const {
-            auto it = labelToExpressionMapping.find(f.getLabel());
-            if (it != labelToExpressionMapping.end()) {
-                return std::static_pointer_cast<Formula>(std::make_shared<AtomicExpressionFormula>(it->second));
+            if (labelToExpressionMapping) {
+                auto it = labelToExpressionMapping->find(f.getLabel());
+                if (it != labelToExpressionMapping->end()) {
+                    return std::static_pointer_cast<Formula>(std::make_shared<AtomicExpressionFormula>(it->second));
+                } else {
+                    return f.asSharedPointer();
+                }
             } else {
-                return std::static_pointer_cast<Formula>(std::make_shared<AtomicLabelFormula>(f));
+                auto it = labelToLabelMapping->find(f.getLabel());
+                if (it != labelToLabelMapping->end()) {
+                    return std::static_pointer_cast<Formula>(std::make_shared<AtomicLabelFormula>(it->second));
+                } else {
+                    return f.asSharedPointer();
+                }
             }
-        }        
+        }
     }
 }
diff --git a/src/storm/logic/LabelSubstitutionVisitor.h b/src/storm/logic/LabelSubstitutionVisitor.h
index 928380be5..cac04fa72 100644
--- a/src/storm/logic/LabelSubstitutionVisitor.h
+++ b/src/storm/logic/LabelSubstitutionVisitor.h
@@ -13,13 +13,15 @@ namespace storm {
         class LabelSubstitutionVisitor : public CloneVisitor {
         public:
             LabelSubstitutionVisitor(std::map<std::string, storm::expressions::Expression> const& labelToExpressionMapping);
+            LabelSubstitutionVisitor(std::map<std::string, std::string> const& labelToLabelMapping);
             
             std::shared_ptr<Formula> substitute(Formula const& f) const;
             
             virtual boost::any visit(AtomicLabelFormula const& f, boost::any const& data) const override;
             
         private:
-            std::map<std::string, storm::expressions::Expression> const& labelToExpressionMapping;
+            std::map<std::string, storm::expressions::Expression> const* labelToExpressionMapping;
+            std::map<std::string, std::string> const* labelToLabelMapping;
         };
         
     }
diff --git a/src/storm/modelchecker/results/CheckResult.cpp b/src/storm/modelchecker/results/CheckResult.cpp
index 8410c8728..210ca3b59 100644
--- a/src/storm/modelchecker/results/CheckResult.cpp
+++ b/src/storm/modelchecker/results/CheckResult.cpp
@@ -39,7 +39,7 @@ namespace storm {
             return false;
         }
         
-        std::ostream& operator<<(std::ostream& out, CheckResult& checkResult) {
+        std::ostream& operator<<(std::ostream& out, CheckResult const& checkResult) {
             checkResult.writeToStream(out);
             return out;
         }
diff --git a/src/storm/modelchecker/results/CheckResult.h b/src/storm/modelchecker/results/CheckResult.h
index 8c6b3a880..6cf39669d 100644
--- a/src/storm/modelchecker/results/CheckResult.h
+++ b/src/storm/modelchecker/results/CheckResult.h
@@ -106,7 +106,7 @@ namespace storm {
             virtual std::ostream& writeToStream(std::ostream& out) const = 0;
         };
         
-        std::ostream& operator<<(std::ostream& out, CheckResult& checkResult);
+        std::ostream& operator<<(std::ostream& out, CheckResult const& checkResult);
     }
 }
 
diff --git a/src/storm/models/ModelBase.h b/src/storm/models/ModelBase.h
index c1a696c03..cbef9a9dc 100644
--- a/src/storm/models/ModelBase.h
+++ b/src/storm/models/ModelBase.h
@@ -60,14 +60,7 @@ namespace storm {
              * @return The number of (non-zero) transitions of the model.
              */
             virtual uint_fast64_t getNumberOfTransitions() const = 0;
-            
-            /*!
-             * Retrieves (an approximation of) the size of the model in bytes.
-             *
-             * @return The size of th model in bytes.
-             */
-            virtual std::size_t getSizeInBytes() const = 0;
-            
+                        
             /*!
              * Prints information about the model to the specified stream.
              *
diff --git a/src/storm/models/sparse/MarkovAutomaton.cpp b/src/storm/models/sparse/MarkovAutomaton.cpp
index 40f9003c5..069ed1936 100644
--- a/src/storm/models/sparse/MarkovAutomaton.cpp
+++ b/src/storm/models/sparse/MarkovAutomaton.cpp
@@ -236,11 +236,6 @@ namespace storm {
                 }
             }
             
-            template <typename ValueType, typename RewardModelType>
-            std::size_t MarkovAutomaton<ValueType, RewardModelType>::getSizeInBytes() const {
-                return NondeterministicModel<ValueType, RewardModelType>::getSizeInBytes() + markovianStates.getSizeInBytes() + exitRates.size() * sizeof(ValueType);
-            }
-            
             template <typename ValueType, typename RewardModelType>
             void MarkovAutomaton<ValueType, RewardModelType>::turnRatesToProbabilities() {
                 this->exitRates.resize(this->getNumberOfStates());
diff --git a/src/storm/models/sparse/MarkovAutomaton.h b/src/storm/models/sparse/MarkovAutomaton.h
index cd45b141b..477d0ee5f 100644
--- a/src/storm/models/sparse/MarkovAutomaton.h
+++ b/src/storm/models/sparse/MarkovAutomaton.h
@@ -194,9 +194,7 @@ namespace storm {
                 std::shared_ptr<storm::models::sparse::Ctmc<ValueType, RewardModelType>> convertToCTMC() const;
                 
                 virtual void writeDotToStream(std::ostream& outStream, bool includeLabeling = true, storm::storage::BitVector const* subsystem = nullptr, std::vector<ValueType> const* firstValue = nullptr, std::vector<ValueType> const* secondValue = nullptr, std::vector<uint_fast64_t> const* stateColoring = nullptr, std::vector<std::string> const* colors = nullptr, std::vector<uint_fast64_t>* scheduler = nullptr, bool finalizeOutput = true) const override;
-                
-                std::size_t getSizeInBytes() const override;
-                
+                                
                 virtual void printModelInformationToStream(std::ostream& out) const override;
                 
             private:
diff --git a/src/storm/models/sparse/Model.cpp b/src/storm/models/sparse/Model.cpp
index 890cfdae7..3f7774759 100644
--- a/src/storm/models/sparse/Model.cpp
+++ b/src/storm/models/sparse/Model.cpp
@@ -188,18 +188,6 @@ namespace storm {
                 return static_cast<bool>(choiceLabeling);
             }
             
-            template<typename ValueType, typename RewardModelType>
-            std::size_t Model<ValueType, RewardModelType>::getSizeInBytes() const {
-                std::size_t result = transitionMatrix.getSizeInBytes() + stateLabeling.getSizeInBytes();
-                for (auto const& rewardModel : this->rewardModels) {
-                    result += rewardModel.second.getSizeInBytes();
-                }
-                if (hasChoiceLabeling()) {
-                    result += getChoiceLabeling().size() * sizeof(LabelSet);
-                }
-                return result;
-            }
-            
             template<typename ValueType, typename RewardModelType>
             void Model<ValueType, RewardModelType>::printModelInformationToStream(std::ostream& out) const {
                 this->printModelInformationHeaderToStream(out);
@@ -219,7 +207,6 @@ namespace storm {
                 this->printRewardModelsInformationToStream(out);
                 this->getStateLabeling().printLabelingInformationToStream(out);
                 out << "choice labels: \t" << (this->hasChoiceLabeling() ? "yes" : "no")  << std::noboolalpha << std::endl;
-                out << "Size in memory: " << (this->getSizeInBytes())/1024 << " kbytes" << std::endl;
                 out << "-------------------------------------------------------------- " << std::endl;
             }
             
diff --git a/src/storm/models/sparse/Model.h b/src/storm/models/sparse/Model.h
index 70358191a..a545ae3ce 100644
--- a/src/storm/models/sparse/Model.h
+++ b/src/storm/models/sparse/Model.h
@@ -275,14 +275,7 @@ namespace storm {
                  * properties, but it preserves expected rewards.
                  */
                 virtual void reduceToStateBasedRewards() = 0;
-                
-                /*!
-                 * Retrieves (an approximation of) the size of the model in bytes.
-                 *
-                 * @return The size of the internal representation of the model measured in bytes.
-                 */
-                virtual std::size_t getSizeInBytes() const override;
-                
+                                
                 /*!
                  * Prints information about the model to the specified stream.
                  *
diff --git a/src/storm/models/sparse/StandardRewardModel.cpp b/src/storm/models/sparse/StandardRewardModel.cpp
index 59dfee941..fa2b63900 100644
--- a/src/storm/models/sparse/StandardRewardModel.cpp
+++ b/src/storm/models/sparse/StandardRewardModel.cpp
@@ -270,22 +270,6 @@ namespace storm {
                 return true;
             }
 
-
-            template<typename ValueType>
-            std::size_t StandardRewardModel<ValueType>::getSizeInBytes() const {
-                std::size_t result = 0;
-                if (this->hasStateRewards()) {
-                    result += this->getStateRewardVector().size() * sizeof(ValueType);
-                }
-                if (this->hasStateActionRewards()) {
-                    result += this->getStateActionRewardVector().size() * sizeof(ValueType);
-                }
-                if (this->hasTransitionRewards()) {
-                    result += this->getTransitionRewardMatrix().getSizeInBytes();
-                }
-                return result;
-            }
-            
             template <typename ValueType>
             std::ostream& operator<<(std::ostream& out, StandardRewardModel<ValueType> const& rewardModel) {
                 out << std::boolalpha << "reward model [state reward: "
diff --git a/src/storm/models/sparse/StandardRewardModel.h b/src/storm/models/sparse/StandardRewardModel.h
index 85d6b5382..dbf484c40 100644
--- a/src/storm/models/sparse/StandardRewardModel.h
+++ b/src/storm/models/sparse/StandardRewardModel.h
@@ -274,13 +274,6 @@ namespace storm {
                  * @param nrChoices The number of choices in the model
                  */
                 bool isCompatible(uint_fast64_t nrStates, uint_fast64_t nrChoices) const;
-
-                /*!
-                 * Retrieves (an approximation of) the size of the model in bytes.
-                 *
-                 * @return The size of the internal representation of the model measured in bytes.
-                 */
-                std::size_t getSizeInBytes() const;
                 
                 template <typename ValueTypePrime>
                 friend std::ostream& operator<<(std::ostream& out, StandardRewardModel<ValueTypePrime> const& rewardModel);
diff --git a/src/storm/models/sparse/StateLabeling.cpp b/src/storm/models/sparse/StateLabeling.cpp
index b3037d173..55eaa76d5 100644
--- a/src/storm/models/sparse/StateLabeling.cpp
+++ b/src/storm/models/sparse/StateLabeling.cpp
@@ -109,14 +109,6 @@ namespace storm {
                 this->labelings[nameToLabelingIndexMap.at(label)] = labeling;
             }
             
-            std::size_t StateLabeling::getSizeInBytes() const {
-                std::size_t result = sizeof(*this);
-                if (!labelings.empty()) {
-                    result += labelings.size() * labelings.front().getSizeInBytes();
-                }
-                return result;
-            }
-            
             void StateLabeling::printLabelingInformationToStream(std::ostream& out) const {
                 out << "Labels: \t" << this->getNumberOfLabels() << std::endl;
                 for (auto const& labelIndexPair : this->nameToLabelingIndexMap) {
diff --git a/src/storm/models/sparse/StateLabeling.h b/src/storm/models/sparse/StateLabeling.h
index c3de34599..330a4d8fe 100644
--- a/src/storm/models/sparse/StateLabeling.h
+++ b/src/storm/models/sparse/StateLabeling.h
@@ -145,14 +145,7 @@ namespace storm {
                  * @param labeling A bit vector that represents the set of states that will get this label.
                  */
                 void setStates(std::string const& label, storage::BitVector&& labeling);
-                
-                /*!
-                 * Returns (an approximation of) the size of the labeling measured in bytes.
-                 *
-                 * @return The size of the labeling measured in bytes.
-                 */
-                std::size_t getSizeInBytes() const;
-                
+                                
                 /*!
                  * Prints information about the labeling to the specified stream.
                  *
diff --git a/src/storm/models/symbolic/Model.cpp b/src/storm/models/symbolic/Model.cpp
index 29f1d6ec7..1ebf69f48 100644
--- a/src/storm/models/symbolic/Model.cpp
+++ b/src/storm/models/symbolic/Model.cpp
@@ -114,12 +114,6 @@ namespace storm {
                 return this->getTransitionMatrix().notZero();
             }
             
-            template<storm::dd::DdType Type, typename ValueType>
-            std::size_t Model<Type, ValueType>::getSizeInBytes() const {
-                // FIXME: This assumes a fixed value of 16 bytes per node, which isn't necessarily true.
-                return sizeof(*this) + 16 * (reachableStates.getNodeCount() + initialStates.getNodeCount() + transitionMatrix.getNodeCount());
-            }
-            
             template<storm::dd::DdType Type, typename ValueType>
             std::set<storm::expressions::Variable> const& Model<Type, ValueType>::getRowVariables() const {
                 return rowVariables;
@@ -222,7 +216,6 @@ namespace storm {
                 for (auto const& label : labelToExpressionMap) {
                     out << "   * " << label.first << std::endl;
                 }
-                out << "Size in memory: \t" << (this->getSizeInBytes())/1024 << " kbytes" << std::endl;
                 out << "-------------------------------------------------------------- " << std::endl;
             }
             
diff --git a/src/storm/models/symbolic/Model.h b/src/storm/models/symbolic/Model.h
index 629f39411..14c4edbc5 100644
--- a/src/storm/models/symbolic/Model.h
+++ b/src/storm/models/symbolic/Model.h
@@ -254,8 +254,6 @@ namespace storm {
                  */
                 uint_fast64_t getNumberOfRewardModels() const;
                 
-                virtual std::size_t getSizeInBytes() const override;
-                
                 virtual void printModelInformationToStream(std::ostream& out) const override;
                 
                 virtual bool isSymbolicModel() const override;
diff --git a/src/storm/parser/DeterministicSparseTransitionParser.cpp b/src/storm/parser/DeterministicSparseTransitionParser.cpp
index 984f3cbc2..7b53415cb 100644
--- a/src/storm/parser/DeterministicSparseTransitionParser.cpp
+++ b/src/storm/parser/DeterministicSparseTransitionParser.cpp
@@ -54,7 +54,7 @@ namespace storm {
             bool insertDiagonalEntriesIfMissing = !isRewardFile;
             DeterministicSparseTransitionParser<ValueType>::FirstPassResult firstPass = DeterministicSparseTransitionParser<ValueType>::firstPass(file.getData(), insertDiagonalEntriesIfMissing);
 
-            STORM_LOG_INFO("First pass on " << filename << " shows " << firstPass.numberOfNonzeroEntries << " NonZeros.");
+            STORM_LOG_TRACE("First pass on " << filename << " shows " << firstPass.numberOfNonzeroEntries << " non-zeros.");
 
             // If first pass returned zero, the file format was wrong.
             if (firstPass.numberOfNonzeroEntries == 0) {
diff --git a/src/storm/settings/modules/IOSettings.cpp b/src/storm/settings/modules/IOSettings.cpp
index c65b8797c..bf8055c31 100644
--- a/src/storm/settings/modules/IOSettings.cpp
+++ b/src/storm/settings/modules/IOSettings.cpp
@@ -195,7 +195,6 @@ namespace storm {
                 return storm::parser::parseCommaSeperatedValues(this->getOption(janiPropertyOptionName).getArgumentByName("values").getValueAsString());
             }
 
-            
             bool IOSettings::isPrismCompatibilityEnabled() const {
                 return this->getOption(prismCompatibilityOptionName).getHasOptionBeenSet();
             }
diff --git a/src/storm/solver/GmmxxLinearEquationSolver.cpp b/src/storm/solver/GmmxxLinearEquationSolver.cpp
index 5ecd10f4c..ecccd4682 100644
--- a/src/storm/solver/GmmxxLinearEquationSolver.cpp
+++ b/src/storm/solver/GmmxxLinearEquationSolver.cpp
@@ -138,7 +138,7 @@ namespace storm {
         bool GmmxxLinearEquationSolver<ValueType>::solveEquations(std::vector<ValueType>& x, std::vector<ValueType> const& b) const {
             auto method = this->getSettings().getSolutionMethod();
             auto preconditioner = this->getSettings().getPreconditioner();
-            STORM_LOG_INFO("Using method '" << method << "' with preconditioner '" << preconditioner << "' (max. " << this->getSettings().getMaximalNumberOfIterations() << " iterations).");
+            STORM_LOG_DEBUG("Using method '" << method << "' with preconditioner '" << preconditioner << "' (max. " << this->getSettings().getMaximalNumberOfIterations() << " iterations).");
             if (method == GmmxxLinearEquationSolverSettings<ValueType>::SolutionMethod::Jacobi && preconditioner != GmmxxLinearEquationSolverSettings<ValueType>::Preconditioner::None) {
                 STORM_LOG_WARN("Jacobi method currently does not support preconditioners. The requested preconditioner will be ignored.");
             }
@@ -193,7 +193,7 @@ namespace storm {
                 
                 // Check if the solver converged and issue a warning otherwise.
                 if (iter.converged()) {
-                    STORM_LOG_INFO("Iterative solver converged after " << iter.get_iteration() << " iterations.");
+                    STORM_LOG_DEBUG("Iterative solver converged after " << iter.get_iteration() << " iterations.");
                     return true;
                 } else {
                     STORM_LOG_WARN("Iterative solver did not converge.");
@@ -204,7 +204,7 @@ namespace storm {
                 
                 // Check if the solver converged and issue a warning otherwise.
                 if (iterations < this->getSettings().getMaximalNumberOfIterations()) {
-                    STORM_LOG_INFO("Iterative solver converged after " << iterations << " iterations.");
+                    STORM_LOG_DEBUG("Iterative solver converged after " << iterations << " iterations.");
                     return true;
                 } else {
                     STORM_LOG_WARN("Iterative solver did not converge.");
diff --git a/src/storm/storage/SparseMatrix.cpp b/src/storm/storage/SparseMatrix.cpp
index 8794ac759..3f70b8f2a 100644
--- a/src/storm/storage/SparseMatrix.cpp
+++ b/src/storm/storage/SparseMatrix.cpp
@@ -1295,19 +1295,6 @@ namespace storm {
             }
         }
         
-        template<typename ValueType>
-        std::size_t SparseMatrix<ValueType>::getSizeInBytes() const {
-            uint_fast64_t size = sizeof(*this);
-            
-            // Add size of columns and values.
-            size += sizeof(MatrixEntry<index_type, ValueType>) * columnsAndValues.capacity();
-            
-            // Add row_indications size.
-            size += sizeof(uint_fast64_t) * rowIndications.capacity();
-            
-            return size;
-        }
-        
         template<typename ValueType>
         typename SparseMatrix<ValueType>::const_rows SparseMatrix<ValueType>::getRows(index_type startRow, index_type endRow) const {
             return const_rows(this->columnsAndValues.begin() + this->rowIndications[startRow], this->rowIndications[endRow] - this->rowIndications[startRow]);
diff --git a/src/storm/storage/SparseMatrix.h b/src/storm/storage/SparseMatrix.h
index a7cfc7b83..f897e1606 100644
--- a/src/storm/storage/SparseMatrix.h
+++ b/src/storm/storage/SparseMatrix.h
@@ -846,13 +846,6 @@ namespace storm {
              * @out The stream to output to.
              */
             void printAsMatlabMatrix(std::ostream& out) const;
-
-            /*!
-             * Returns the size of the matrix in memory measured in bytes.
-             *
-             * @return The size of the matrix in memory measured in bytes.
-             */
-            std::size_t getSizeInBytes() const;
             
             /*!
              * Calculates a hash value over all values contained in the matrix.
diff --git a/src/storm/storage/SymbolicModelDescription.cpp b/src/storm/storage/SymbolicModelDescription.cpp
index da985d103..92b52c783 100644
--- a/src/storm/storage/SymbolicModelDescription.cpp
+++ b/src/storm/storage/SymbolicModelDescription.cpp
@@ -126,6 +126,18 @@ namespace storm {
             }
         }
         
+        std::pair<SymbolicModelDescription, std::map<std::string, std::string>> SymbolicModelDescription::toJaniWithLabelRenaming(bool makeVariablesGlobal) const {
+            if (this->isJaniModel()) {
+                return std::make_pair(*this, std::map<std::string, std::string>());
+            }
+            if (this->isPrismProgram()) {
+                auto modelAndRenaming = this->asPrismProgram().toJaniWithLabelRenaming(makeVariablesGlobal);
+                return std::make_pair(SymbolicModelDescription(modelAndRenaming.first), modelAndRenaming.second);
+            } else {
+                STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Cannot transform model description to the JANI format.");
+            }
+        }
+        
         SymbolicModelDescription SymbolicModelDescription::preprocess(std::string const& constantDefinitionString) const {
             std::map<storm::expressions::Variable, storm::expressions::Expression> substitution = parseConstantDefinitions(constantDefinitionString);
             if (this->isJaniModel()) {
diff --git a/src/storm/storage/SymbolicModelDescription.h b/src/storm/storage/SymbolicModelDescription.h
index 8ed9fa7e0..05daa5ec2 100644
--- a/src/storm/storage/SymbolicModelDescription.h
+++ b/src/storm/storage/SymbolicModelDescription.h
@@ -39,6 +39,7 @@ namespace storm {
             std::vector<std::string> getParameterNames() const;
             
             SymbolicModelDescription toJani(bool makeVariablesGlobal = true) const;
+            std::pair<SymbolicModelDescription, std::map<std::string, std::string>> toJaniWithLabelRenaming(bool makeVariablesGlobal = true) const;
             
             SymbolicModelDescription preprocess(std::string const& constantDefinitionString = "") const;
             SymbolicModelDescription preprocess(std::map<storm::expressions::Variable, storm::expressions::Expression> const& constantDefinitions) const;
diff --git a/src/storm/storage/jani/Property.cpp b/src/storm/storage/jani/Property.cpp
index 896810526..e46d2ee50 100644
--- a/src/storm/storage/jani/Property.cpp
+++ b/src/storm/storage/jani/Property.cpp
@@ -32,6 +32,10 @@ namespace storm {
             return Property(name, filterExpression.substitute(substitution), comment);
         }
         
+        Property Property::substituteLabels(std::map<std::string, std::string> const& substitution) const {
+            return Property(name, filterExpression.substituteLabels(substitution), comment);
+        }
+        
         FilterExpression const& Property::getFilter() const {
             return this->filterExpression;
         }
diff --git a/src/storm/storage/jani/Property.h b/src/storm/storage/jani/Property.h
index 9d28d715a..f97341c53 100644
--- a/src/storm/storage/jani/Property.h
+++ b/src/storm/storage/jani/Property.h
@@ -42,6 +42,11 @@ namespace storm {
             FilterExpression substitute(std::map<storm::expressions::Variable, storm::expressions::Expression> const& substitution) const {
                 return FilterExpression(formula->substitute(substitution), ft);
             }
+            
+            FilterExpression substituteLabels(std::map<std::string, std::string> const& labelSubstitution) const {
+                return FilterExpression(formula->substitute(labelSubstitution), ft);
+            }
+
         private:
             // For now, we assume that the states are always the initial states.
             std::shared_ptr<storm::logic::Formula const> formula;
@@ -84,6 +89,7 @@ namespace storm {
             std::string const& getComment() const;
             
             Property substitute(std::map<storm::expressions::Variable, storm::expressions::Expression> const& substitution) const;
+            Property substituteLabels(std::map<std::string, std::string> const& labelSubstitution) const;
             
             FilterExpression const& getFilter() const;
             
diff --git a/src/storm/storage/prism/Program.cpp b/src/storm/storage/prism/Program.cpp
index c521d9c29..b96a1c7d2 100644
--- a/src/storm/storage/prism/Program.cpp
+++ b/src/storm/storage/prism/Program.cpp
@@ -5,6 +5,7 @@
 #include <boost/algorithm/string/join.hpp>
 
 #include "storm/storage/jani/Model.h"
+#include "storm/storage/jani/Property.h"
 
 #include "storm/storage/expressions/ExpressionManager.h"
 #include "storm/settings/SettingsManager.h"
@@ -1009,7 +1010,6 @@ namespace storm {
             std::set<storm::expressions::Variable> variablesAndConstants;
             std::set_union(variables.begin(), variables.end(), constants.begin(), constants.end(), std::inserter(variablesAndConstants, variablesAndConstants.begin()));
             
-            
             // Check the commands of the modules.
             bool hasProbabilisticCommand = false;
             bool hasMarkovianCommand = false;
@@ -1627,9 +1627,17 @@ namespace storm {
         
         storm::jani::Model Program::toJani(bool allVariablesGlobal) const {
             ToJaniConverter converter;
-            return converter.convert(*this, allVariablesGlobal);
+            storm::jani::Model resultingModel = converter.convert(*this, allVariablesGlobal);
+            STORM_LOG_WARN_COND(!converter.labelsWereRenamed(), "Labels were renamed in PRISM-to-JANI conversion, but the mapping is not stored.");
+            return resultingModel;
         }
-        
+
+        std::pair<storm::jani::Model, std::map<std::string, std::string>> Program::toJaniWithLabelRenaming(bool allVariablesGlobal) const {
+            ToJaniConverter converter;
+            storm::jani::Model resultingModel = converter.convert(*this, allVariablesGlobal);
+            return std::make_pair(resultingModel, converter.getLabelRenaming());
+        }
+
         storm::expressions::ExpressionManager& Program::getManager() const {
             return *this->manager;
         }
diff --git a/src/storm/storage/prism/Program.h b/src/storm/storage/prism/Program.h
index a33484833..ccf56ead6 100644
--- a/src/storm/storage/prism/Program.h
+++ b/src/storm/storage/prism/Program.h
@@ -22,6 +22,7 @@
 namespace storm {
     namespace jani {
         class Model;
+        class Property;
     }
     
     namespace prism {
@@ -587,6 +588,12 @@ namespace storm {
              */
             storm::jani::Model toJani(bool allVariablesGlobal = false) const;
             
+            /*!
+             * Converts the PRISM model into an equivalent JANI model and retrieves possible label renamings that had
+             * to be performed in the process.
+             */
+            std::pair<storm::jani::Model, std::map<std::string, std::string>> toJaniWithLabelRenaming(bool allVariablesGlobal = false) const;
+            
         private:
             /*!
              * This function builds a command that corresponds to the synchronization of the given list of commands.
diff --git a/src/storm/storage/prism/ToJaniConverter.cpp b/src/storm/storage/prism/ToJaniConverter.cpp
index 4e451d177..21a806374 100644
--- a/src/storm/storage/prism/ToJaniConverter.cpp
+++ b/src/storm/storage/prism/ToJaniConverter.cpp
@@ -12,7 +12,7 @@
 namespace storm {
     namespace prism {
         
-        storm::jani::Model ToJaniConverter::convert(storm::prism::Program const& program, bool allVariablesGlobal) const {
+        storm::jani::Model ToJaniConverter::convert(storm::prism::Program const& program, bool allVariablesGlobal) {
             std::shared_ptr<storm::expressions::ExpressionManager> manager = program.getManager().getSharedPointer();
                         
             // Start by creating an empty JANI model.
@@ -97,9 +97,14 @@ namespace storm {
             // Go through the labels and construct assignments to transient variables that are added to the loctions.
             std::vector<storm::jani::Assignment> transientLocationAssignments;
             for (auto const& label : program.getLabels()) {
-                auto newExpressionVariable = manager->declareBooleanVariable("label_" + label.getName());
+                bool renameLabel = manager->hasVariable(label.getName()) || program.hasRewardModel(label.getName());
+                std::string finalLabelName = renameLabel ? "label_" + label.getName() : label.getName();
+                if (renameLabel) {
+                    STORM_LOG_WARN_COND(!renameLabel, "Label '" << label.getName() << "' was renamed to '" << finalLabelName << "' in PRISM-to-JANI conversion, as another variable with that name already exists.");
+                    labelRenaming[label.getName()] = finalLabelName;
+                }
+                auto newExpressionVariable = manager->declareBooleanVariable(finalLabelName);
                 storm::jani::BooleanVariable const& newTransientVariable = janiModel.addVariable(storm::jani::BooleanVariable(newExpressionVariable.getName(), newExpressionVariable, manager->boolean(false), true));
-
                 transientLocationAssignments.emplace_back(newTransientVariable, label.getStatePredicateExpression());
             }
             
@@ -284,5 +289,13 @@ namespace storm {
             return janiModel;
         }
         
+        bool ToJaniConverter::labelsWereRenamed() const {
+            return !labelRenaming.empty();
+        }
+        
+        std::map<std::string, std::string> const& ToJaniConverter::getLabelRenaming() const {
+            return labelRenaming;
+        }
+        
     }
 }
diff --git a/src/storm/storage/prism/ToJaniConverter.h b/src/storm/storage/prism/ToJaniConverter.h
index bc32a3dd8..2ab5c0509 100644
--- a/src/storm/storage/prism/ToJaniConverter.h
+++ b/src/storm/storage/prism/ToJaniConverter.h
@@ -1,5 +1,8 @@
 #pragma once
 
+#include <map>
+#include <string>
+
 namespace storm {
     namespace jani {
         class Model;
@@ -11,7 +14,13 @@ namespace storm {
         
         class ToJaniConverter {
         public:
-            storm::jani::Model convert(storm::prism::Program const& program, bool allVariablesGlobal = false) const;
+            storm::jani::Model convert(storm::prism::Program const& program, bool allVariablesGlobal = false);
+            
+            bool labelsWereRenamed() const;
+            std::map<std::string, std::string> const& getLabelRenaming() const;
+            
+        private:
+            std::map<std::string, std::string> labelRenaming;
         };
         
     }
diff --git a/src/storm/storm.cpp b/src/storm/storm.cpp
index 7ce7611dc..20126f488 100644
--- a/src/storm/storm.cpp
+++ b/src/storm/storm.cpp
@@ -32,7 +32,7 @@ int main(const int argc, const char** argv) {
         totalTimer.stop();
 
         if (storm::settings::getModule<storm::settings::modules::ResourceSettings>().isPrintTimeAndMemorySet()) {
-            storm::cli::showTimeAndMemoryStatistics(totalTimer.getTimeMilliseconds());
+            storm::cli::showTimeAndMemoryStatistics(totalTimer.getTimeInMilliseconds());
         }
         return 0;
     } catch (storm::exceptions::BaseException const& exception) {
diff --git a/src/storm/utility/Stopwatch.cpp b/src/storm/utility/Stopwatch.cpp
new file mode 100644
index 000000000..fd77c0d59
--- /dev/null
+++ b/src/storm/utility/Stopwatch.cpp
@@ -0,0 +1,51 @@
+#include "storm/utility/Stopwatch.h"
+
+namespace storm {
+    namespace utility {
+        
+        Stopwatch::Stopwatch(bool startNow) : accumulatedTime(std::chrono::nanoseconds::zero()), stopped(true), startOfCurrentMeasurement(std::chrono::nanoseconds::zero()) {
+            if (startNow) {
+                start();
+            }
+        }
+        
+        Stopwatch::SecondType Stopwatch::getTimeInSeconds() const {
+            return std::chrono::duration_cast<std::chrono::seconds>(accumulatedTime).count();
+        }
+        
+        Stopwatch::MilisecondType Stopwatch::getTimeInMilliseconds() const {
+            return std::chrono::duration_cast<std::chrono::milliseconds>(accumulatedTime).count();
+        }
+        
+        Stopwatch::NanosecondType Stopwatch::getTimeInNanoseconds() const {
+            return accumulatedTime.count();
+        }
+        
+        void Stopwatch::addToTime(std::chrono::nanoseconds timeNanoseconds) {
+            accumulatedTime += timeNanoseconds;
+        }
+        
+        void Stopwatch::stop() {
+            STORM_LOG_WARN_COND(!stopped, "Stopwatch is already paused.");
+            stopped = true;
+            accumulatedTime += std::chrono::high_resolution_clock::now() - startOfCurrentMeasurement;
+        }
+        
+        void Stopwatch::start() {
+            STORM_LOG_WARN_COND(stopped, "Stopwatch is already running.");
+            stopped = false;
+            startOfCurrentMeasurement = std::chrono::high_resolution_clock::now();
+        }
+        
+        void Stopwatch::reset() {
+            accumulatedTime = std::chrono::nanoseconds::zero();
+            stopped = true;
+        }
+        
+        std::ostream& operator<<(std::ostream& out, Stopwatch const& stopwatch) {
+            out << stopwatch.getTimeInSeconds() << "." << (stopwatch.getTimeInMilliseconds() % 1000) << "s";
+            return out;
+        }
+        
+    }
+}
diff --git a/src/storm/utility/Stopwatch.h b/src/storm/utility/Stopwatch.h
index a30b40683..4f81ea8a8 100644
--- a/src/storm/utility/Stopwatch.h
+++ b/src/storm/utility/Stopwatch.h
@@ -13,106 +13,68 @@ namespace storm {
          */
         class Stopwatch {
         public:
-
+            typedef decltype(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::seconds::zero()).count()) SecondType;
+            typedef decltype(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::milliseconds::zero()).count()) MilisecondType;
+            typedef decltype(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::nanoseconds::zero()).count()) NanosecondType;
+            
             /*!
              * Constructor.
              *
              * @param startNow If true, the stopwatch starts right away.
              */
-            Stopwatch(bool startNow = false) : accumulatedTime(std::chrono::nanoseconds::zero()), stopped(true), startOfCurrentMeasurement(std::chrono::nanoseconds::zero()) {
-                if (startNow) {
-                    start();
-                }
-            }
-
-            /*!
-             * Destructor.
-             */
-            ~Stopwatch() = default;
+            Stopwatch(bool startNow = false);
 
             /*!
-             * Get measured time in seconds.
-             *
-             * @return seconds as floating point number.
+             * Gets the measured time in seconds.
              */
-            double getTimeSeconds() const {
-                return std::chrono::duration<float>(accumulatedTime).count();
-            }
+            SecondType getTimeInSeconds() const;
 
             /*!
-             * Get measured time in milliseconds.
-             *
-             * @return Milliseconds.
+             * Gets the measured time in milliseconds.
              */
-            unsigned long long int getTimeMilliseconds() const {
-                return std::chrono::duration_cast<std::chrono::milliseconds>(accumulatedTime).count();
-            }
+            MilisecondType getTimeInMilliseconds() const;
 
             /*!
-             * Get measured time in nanoseconds.
-             *
-             * @return Nanoseconds.
+             * Gets the measured time in nanoseconds.
              */
-            unsigned long long int getTimeNanoseconds() const {
-                return accumulatedTime.count();
-            }
+            NanosecondType getTimeInNanoseconds() const;
 
             /*!
              * Add given time to measured time.
              *
              * @param timeNanoseconds Additional time in nanoseconds.
              */
-            void addToTime(std::chrono::nanoseconds timeNanoseconds) {
-                accumulatedTime += timeNanoseconds;
-            }
+            void addToTime(std::chrono::nanoseconds timeNanoseconds);
 
             /*!
              * Stop stopwatch and add measured time to total time.
              */
-            void stop() {
-                if (stopped) {
-                    // Assertions are only available in DEBUG build and therefore not used here.
-                    STORM_LOG_WARN("Stopwatch is already paused.");
-                }
-                stopped = true;
-                accumulatedTime += std::chrono::high_resolution_clock::now() - startOfCurrentMeasurement;
-            }
+            void stop();
 
             /*!
              * Start stopwatch (again) and start measuring time.
              */
-            void start() {
-                if (!stopped) {
-                    // Assertions are only available in DEBUG build and therefore not used here.
-                    STORM_LOG_WARN("Stopwatch is already running.");
-                }
-                stopped = false;
-                startOfCurrentMeasurement = std::chrono::high_resolution_clock::now();
-            }
+            void start();
             
             /*!
-             * Reset the stopwatch. Reset the measured time to zero and stop the stopwatch.
+             * Reset the stopwatch.
              */
-            void reset() {
-                accumulatedTime = std::chrono::nanoseconds::zero();
-                stopped = true;
-            }
+            void reset();
 
-            friend std::ostream& operator<<(std::ostream& out, Stopwatch const& stopwatch) {
-                out << stopwatch.getTimeSeconds();
-                return out;
-            }
-            
+            friend std::ostream& operator<<(std::ostream& out, Stopwatch const& stopwatch);
             
         private:
-            // Total measured time
+            // The time accumulated so far.
             std::chrono::nanoseconds accumulatedTime;
-            // Flag indicating if the stopwatch is stopped right now.
+            
+            // A flag indicating if the stopwatch is stopped right now.
             bool stopped;
-            // Timepoint when the stopwatch was started the last time.
-            std::chrono::high_resolution_clock::time_point startOfCurrentMeasurement;
             
+            // The timepoint when the stopwatch was started the last time (if it's not stopped).
+            std::chrono::high_resolution_clock::time_point startOfCurrentMeasurement;
         };
+        
+        std::ostream& operator<<(std::ostream& out, Stopwatch const& stopwatch);
     }
 }
 
diff --git a/src/storm/utility/storm.h b/src/storm/utility/storm.h
index 46fcfdb67..7e81f2a36 100644
--- a/src/storm/utility/storm.h
+++ b/src/storm/utility/storm.h
@@ -96,7 +96,7 @@
 #include "storm/exceptions/NotImplementedException.h"
 #include "storm/exceptions/NotSupportedException.h"
 
-#include "storm/storage/jani/JSONExporter.h"
+#include "storm/utility/Stopwatch.h"
 
 namespace storm {
 
@@ -231,7 +231,11 @@ namespace storm {
     
     template<typename ModelType>
     std::shared_ptr<storm::models::ModelBase> preprocessModel(std::shared_ptr<storm::models::ModelBase> model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas) {
+        storm::utility::Stopwatch preprocessingWatch(true);
+        
+        bool operationPerformed = false;
         if (model->getType() == storm::models::ModelType::MarkovAutomaton && model->isSparseModel()) {
+            operationPerformed = true;
             std::shared_ptr<storm::models::sparse::MarkovAutomaton<typename ModelType::ValueType>> ma = model->template as<storm::models::sparse::MarkovAutomaton<typename ModelType::ValueType>>();
             ma->close();
             if (ma->hasOnlyTrivialNondeterminism()) {
@@ -241,6 +245,7 @@ namespace storm {
         }
 
         if (model->isSparseModel() && storm::settings::getModule<storm::settings::modules::GeneralSettings>().isBisimulationSet()) {
+            operationPerformed = true;
             storm::storage::BisimulationType bisimType = storm::storage::BisimulationType::Strong;
             if (storm::settings::getModule<storm::settings::modules::BisimulationSettings>().isWeakBisimulationSet()) {
                 bisimType = storm::storage::BisimulationType::Weak;
@@ -250,6 +255,11 @@ namespace storm {
             return performBisimulationMinimization<ModelType>(model->template as<storm::models::sparse::Model<typename ModelType::ValueType>>(), formulas, bisimType);
         }
 
+        preprocessingWatch.stop();
+        if (operationPerformed) {
+            STORM_PRINT_AND_LOG(std::endl << "Time for model preprocessing: " << preprocessingWatch << "." << std::endl << std::endl);
+        }
+        
         return model;
     }
 
@@ -300,17 +310,17 @@ namespace storm {
         switch(storm::settings::getModule<storm::settings::modules::CoreSettings>().getEngine()) {
             case storm::settings::modules::CoreSettings::Engine::Sparse: {
                 std::shared_ptr<storm::models::sparse::Model<ValueType>> sparseModel = model->template as<storm::models::sparse::Model<ValueType>>();
-                STORM_LOG_THROW(sparseModel != nullptr, storm::exceptions::InvalidArgumentException, "Sparse engine requires a sparse input model");
+                STORM_LOG_THROW(sparseModel != nullptr, storm::exceptions::InvalidArgumentException, "Sparse engine requires a sparse input model.");
                 return (sparseModel, formula, onlyInitialStatesRelevant);
             }
             case storm::settings::modules::CoreSettings::Engine::Hybrid: {
                 std::shared_ptr<storm::models::symbolic::Model<DdType>> ddModel = model->template as<storm::models::symbolic::Model<DdType>>();
-                STORM_LOG_THROW(ddModel != nullptr, storm::exceptions::InvalidArgumentException, "Hybrid engine requires a dd input model");
+                STORM_LOG_THROW(ddModel != nullptr, storm::exceptions::InvalidArgumentException, "Hybrid engine requires a DD-based input model.");
                 return verifySymbolicModelWithHybridEngine(ddModel, formula, onlyInitialStatesRelevant);
             }
             case storm::settings::modules::CoreSettings::Engine::Dd: {
                 std::shared_ptr<storm::models::symbolic::Model<DdType>> ddModel = model->template as<storm::models::symbolic::Model<DdType>>();
-                STORM_LOG_THROW(ddModel != nullptr, storm::exceptions::InvalidArgumentException, "Dd engine requires a dd input model");
+                STORM_LOG_THROW(ddModel != nullptr, storm::exceptions::InvalidArgumentException, "Dd engine requires a DD-based input model.");
                 return verifySymbolicModelWithDdEngine(ddModel, formula, onlyInitialStatesRelevant);
             }
             default: {