#include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h" #include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" #include "storm/storage/BitVector.h" #include "storm/utility/macros.h" #include "storm/utility/constants.h" #include "storm/utility/vector.h" #include "storm/exceptions/InvalidOperationException.h" #include "storm/exceptions/InvalidAccessException.h" #include "storm/adapters/CarlAdapter.h" namespace storm { namespace modelchecker { template ExplicitQuantitativeCheckResult::ExplicitQuantitativeCheckResult() : values(map_type()) { // Intentionally left empty. } template ExplicitQuantitativeCheckResult::ExplicitQuantitativeCheckResult(map_type const& values) : values(values) { // Intentionally left empty. } template ExplicitQuantitativeCheckResult::ExplicitQuantitativeCheckResult(map_type&& values) : values(std::move(values)) { // Intentionally left empty. } template ExplicitQuantitativeCheckResult::ExplicitQuantitativeCheckResult(storm::storage::sparse::state_type const& state, ValueType const& value) : values(map_type()) { boost::get(values).emplace(state, value); } template ExplicitQuantitativeCheckResult::ExplicitQuantitativeCheckResult(vector_type const& values) : values(values) { // Intentionally left empty. } template ExplicitQuantitativeCheckResult::ExplicitQuantitativeCheckResult(vector_type&& values) : values(std::move(values)) { // Intentionally left empty. } template typename ExplicitQuantitativeCheckResult::vector_type const& ExplicitQuantitativeCheckResult::getValueVector() const { return boost::get(values); } template typename ExplicitQuantitativeCheckResult::vector_type& ExplicitQuantitativeCheckResult::getValueVector() { return boost::get(values); } template typename ExplicitQuantitativeCheckResult::map_type const& ExplicitQuantitativeCheckResult::getValueMap() const { return boost::get(values); } template void ExplicitQuantitativeCheckResult::filter(QualitativeCheckResult const& filter) { STORM_LOG_THROW(filter.isExplicitQualitativeCheckResult(), storm::exceptions::InvalidOperationException, "Cannot filter explicit check result with non-explicit filter."); STORM_LOG_THROW(filter.isResultForAllStates(), storm::exceptions::InvalidOperationException, "Cannot filter check result with non-complete filter."); ExplicitQualitativeCheckResult const& explicitFilter = filter.asExplicitQualitativeCheckResult(); ExplicitQualitativeCheckResult::vector_type const& filterTruthValues = explicitFilter.getTruthValuesVector(); if (this->isResultForAllStates()) { map_type newMap; for (auto const& element : filterTruthValues) { STORM_LOG_THROW(element < this->getValueVector().size(), storm::exceptions::InvalidAccessException, "Invalid index in results."); newMap.emplace(element, this->getValueVector()[element]); } this->values = newMap; } else { map_type const& map = boost::get(values); map_type newMap; for (auto const& element : map) { if (filterTruthValues.get(element.first)) { newMap.insert(element); } } STORM_LOG_THROW(newMap.size() == filterTruthValues.getNumberOfSetBits(), storm::exceptions::InvalidOperationException, "The check result fails to contain some results referred to by the filter."); this->values = newMap; } } template ValueType ExplicitQuantitativeCheckResult::getMin() const { STORM_LOG_THROW(!values.empty(), storm::exceptions::InvalidOperationException, "Minimum of empty set is not defined."); if (this->isResultForAllStates()) { return storm::utility::minimum(boost::get(values)); } else { return storm::utility::minimum(boost::get(values)); } } template ValueType ExplicitQuantitativeCheckResult::getMax() const { STORM_LOG_THROW(!values.empty(), storm::exceptions::InvalidOperationException, "Minimum of empty set is not defined."); if (this->isResultForAllStates()) { return storm::utility::maximum(boost::get(values)); } else { return storm::utility::maximum(boost::get(values)); } } template std::pair ExplicitQuantitativeCheckResult::getMinMax() const { STORM_LOG_THROW(!values.empty(), storm::exceptions::InvalidOperationException, "Minimum/maximum of empty set is not defined."); if (this->isResultForAllStates()) { return storm::utility::minmax(boost::get(values)); } else { return storm::utility::minmax(boost::get(values)); } } template ValueType ExplicitQuantitativeCheckResult::sum() const { STORM_LOG_THROW(!values.empty(),storm::exceptions::InvalidOperationException, "Minimum of empty set is not defined"); ValueType sum = storm::utility::zero(); if (this->isResultForAllStates()) { for (auto& element : boost::get(values)) { STORM_LOG_THROW(element != storm::utility::infinity(), storm::exceptions::InvalidOperationException, "Cannot compute the sum of values containing infinity."); sum += element; } } else { for (auto& element : boost::get(values)) { STORM_LOG_THROW(element.second != storm::utility::infinity(), storm::exceptions::InvalidOperationException, "Cannot compute the sum of values containing infinity."); sum += element.second; } } return sum; } template ValueType ExplicitQuantitativeCheckResult::average() const { STORM_LOG_THROW(!values.empty(),storm::exceptions::InvalidOperationException, "Minimum of empty set is not defined"); ValueType sum = storm::utility::zero(); if (this->isResultForAllStates()) { for (auto& element : boost::get(values)) { STORM_LOG_THROW(element != storm::utility::infinity(), storm::exceptions::InvalidOperationException, "Cannot compute the average of values containing infinity."); sum += element; } return sum / boost::get(values).size(); } else { for (auto& element : boost::get(values)) { STORM_LOG_THROW(element.second != storm::utility::infinity(), storm::exceptions::InvalidOperationException, "Cannot compute the average of values containing infinity."); sum += element.second; } return sum / boost::get(values).size(); } } template bool ExplicitQuantitativeCheckResult::hasScheduler() const { return static_cast(scheduler); } template void ExplicitQuantitativeCheckResult::setScheduler(std::unique_ptr&& scheduler) { this->scheduler = std::move(scheduler); } template storm::storage::Scheduler const& ExplicitQuantitativeCheckResult::getScheduler() const { STORM_LOG_THROW(this->hasScheduler(), storm::exceptions::InvalidOperationException, "Unable to retrieve non-existing scheduler."); return *scheduler.get(); } template storm::storage::Scheduler& ExplicitQuantitativeCheckResult::getScheduler() { STORM_LOG_THROW(this->hasScheduler(), storm::exceptions::InvalidOperationException, "Unable to retrieve non-existing scheduler."); return *scheduler.get(); } template void print(std::ostream& out, ValueType const& value) { if (value == storm::utility::infinity()) { out << "inf"; } else { out << value; if (std::is_same::value) { out << " (approx. " << storm::utility::convertNumber(value) << ")"; } } } template void printRange(std::ostream& out, ValueType const& min, ValueType const& max) { out << "["; if (min == storm::utility::infinity()) { out << "inf"; } else { out << min; } out << ", "; if (max == storm::utility::infinity()) { out << "inf"; } else { out << max; } out << "]"; if (std::is_same::value) { out << " (approx. ["; if (min == storm::utility::infinity()) { out << "inf"; } else { out << storm::utility::convertNumber(min); } out << ", "; if (max == storm::utility::infinity()) { out << "inf"; } else { out << storm::utility::convertNumber(max); } out << "])"; } out << " (range)"; } template std::ostream& ExplicitQuantitativeCheckResult::writeToStream(std::ostream& out) const { bool minMaxSupported = std::is_same::value || std::is_same::value; bool printAsRange = false; if (this->isResultForAllStates()) { vector_type const& valuesAsVector = boost::get(values); if (valuesAsVector.size() >= 10 && minMaxSupported) { printAsRange = true; } else { out << "{"; bool first = true; for (auto const& element : valuesAsVector) { if (!first) { out << ", "; } else { first = false; } print(out, element); } out << "}"; } } else { map_type const& valuesAsMap = boost::get(values); if (valuesAsMap.size() >= 10 && minMaxSupported) { printAsRange = true; } else { if (valuesAsMap.size() == 1) { print(out, valuesAsMap.begin()->second); } else { out << "{"; bool first = true; for (auto const& element : valuesAsMap) { if (!first) { out << ", "; } else { first = false; } print(out, element.second); } out << "}"; } } } if (printAsRange) { std::pair minmax = this->getMinMax(); printRange(out, minmax.first, minmax.second); } return out; } template std::unique_ptr ExplicitQuantitativeCheckResult::compareAgainstBound(storm::logic::ComparisonType comparisonType, ValueType const& bound) const { if (this->isResultForAllStates()) { vector_type const& valuesAsVector = boost::get(values); storm::storage::BitVector result(valuesAsVector.size()); switch (comparisonType) { case logic::ComparisonType::Less: for (uint_fast64_t index = 0; index < valuesAsVector.size(); ++index) { if (valuesAsVector[index] < bound) { result.set(index); } } break; case logic::ComparisonType::LessEqual: for (uint_fast64_t index = 0; index < valuesAsVector.size(); ++index) { if (valuesAsVector[index] <= bound) { result.set(index); } } break; case logic::ComparisonType::Greater: for (uint_fast64_t index = 0; index < valuesAsVector.size(); ++index) { if (valuesAsVector[index] > bound) { result.set(index); } } break; case logic::ComparisonType::GreaterEqual: for (uint_fast64_t index = 0; index < valuesAsVector.size(); ++index) { if (valuesAsVector[index] >= bound) { result.set(index); } } break; } return std::unique_ptr(new ExplicitQualitativeCheckResult(std::move(result))); } else { map_type const& valuesAsMap = boost::get(values); std::map result; switch (comparisonType) { case logic::ComparisonType::Less: for (auto const& element : valuesAsMap) { result[element.first] = element.second < bound; } break; case logic::ComparisonType::LessEqual: for (auto const& element : valuesAsMap) { result[element.first] = element.second <= bound; } break; case logic::ComparisonType::Greater: for (auto const& element : valuesAsMap) { result[element.first] = element.second > bound; } break; case logic::ComparisonType::GreaterEqual: for (auto const& element : valuesAsMap) { result[element.first] = element.second >= bound; } break; } return std::unique_ptr(new ExplicitQualitativeCheckResult(std::move(result))); } } #ifdef STORM_HAVE_CARL template<> std::unique_ptr ExplicitQuantitativeCheckResult::compareAgainstBound(storm::logic::ComparisonType comparisonType, storm::RationalFunction const& bound) const { // Since it is not possible to compare rational functions against bounds, we simply call the base class method. return QuantitativeCheckResult::compareAgainstBound(comparisonType, bound); } #endif template ValueType& ExplicitQuantitativeCheckResult::operator[](storm::storage::sparse::state_type state) { if (this->isResultForAllStates()) { return boost::get(values)[state]; } else { return boost::get(values)[state]; } } template ValueType const& ExplicitQuantitativeCheckResult::operator[](storm::storage::sparse::state_type state) const { if (this->isResultForAllStates()) { return boost::get(values)[state]; } else { map_type const& valuesAsMap = boost::get(values); auto const& keyValuePair = valuesAsMap.find(state); STORM_LOG_THROW(keyValuePair != valuesAsMap.end(), storm::exceptions::InvalidOperationException, "Unknown key '" << state << "'."); return keyValuePair->second; } } template bool ExplicitQuantitativeCheckResult::isExplicit() const { return true; } template bool ExplicitQuantitativeCheckResult::isResultForAllStates() const { return values.which() == 0; } template bool ExplicitQuantitativeCheckResult::isExplicitQuantitativeCheckResult() const { return true; } template void ExplicitQuantitativeCheckResult::oneMinus() { if (this->isResultForAllStates()) { for (auto& element : boost::get(values)) { element = storm::utility::one() - element; } } else { for (auto& element : boost::get(values)) { element.second = storm::utility::one() - element.second; } } } template class ExplicitQuantitativeCheckResult; #ifdef STORM_HAVE_CARL template class ExplicitQuantitativeCheckResult; template class ExplicitQuantitativeCheckResult; #endif } }