#include "storm/storage/dd/DdManager.h" #include "storm/storage/expressions/ExpressionManager.h" #include "storm/utility/macros.h" #include "storm/utility/constants.h" #include "storm/exceptions/InvalidArgumentException.h" #include "storm/exceptions/NotSupportedException.h" #include "storm/exceptions/InvalidOperationException.h" #include "storm-config.h" #include "storm/adapters/RationalFunctionAdapter.h" #include #include namespace storm { namespace dd { template DdManager::DdManager() : internalDdManager(), metaVariableMap(), manager(new storm::expressions::ExpressionManager()) { // Intentionally left empty. } template std::shared_ptr> DdManager::asSharedPointer() { return this->shared_from_this(); } template std::shared_ptr const> DdManager::asSharedPointer() const { return this->shared_from_this(); } template Bdd DdManager::getBddOne() const { return Bdd(*this, internalDdManager.getBddOne()); } template template Add DdManager::getAddOne() const { return Add(*this, internalDdManager.template getAddOne()); } template Bdd DdManager::getBddZero() const { return Bdd(*this, internalDdManager.getBddZero()); } template template Add DdManager::getAddZero() const { return Add(*this, internalDdManager.template getAddZero()); } template template Add DdManager::getAddUndefined() const { return Add(*this, internalDdManager.template getAddUndefined()); } template template Add DdManager::getInfinity() const { return getConstant(storm::utility::infinity()); } template template Add DdManager::getConstant(ValueType const& value) const { return Add(*this, internalDdManager.getConstant(value)); } template Bdd DdManager::getEncoding(storm::expressions::Variable const& variable, int_fast64_t value, bool mostSignificantBitAtTop) const { DdMetaVariable const& metaVariable = this->getMetaVariable(variable); STORM_LOG_THROW(metaVariable.canRepresent(value), storm::exceptions::InvalidArgumentException, "Illegal value " << value << " for meta variable '" << variable.getName() << "'."); // Now compute the encoding relative to the low value of the meta variable. value -= metaVariable.getLow(); std::vector> const& ddVariables = metaVariable.getDdVariables(); Bdd result; if (mostSignificantBitAtTop) { if (value & (1ull << (ddVariables.size() - 1))) { result = ddVariables[0]; } else { result = !ddVariables[0]; } for (std::size_t i = 1; i < ddVariables.size(); ++i) { if (value & (1ull << (ddVariables.size() - i - 1))) { result &= ddVariables[i]; } else { result &= !ddVariables[i]; } } } else { if (value & 1ull) { result = ddVariables[0]; } else { result = !ddVariables[0]; } value >>= 1; for (std::size_t i = 1; i < ddVariables.size(); ++i) { if (value & 1ull) { result &= ddVariables[i]; } else { result &= !ddVariables[i]; } value >>= 1; } } return result; } template Bdd DdManager::getRange(storm::expressions::Variable const& variable) const { storm::dd::DdMetaVariable const& metaVariable = this->getMetaVariable(variable); if (metaVariable.hasHigh()) { return Bdd(*this, internalDdManager.getBddEncodingLessOrEqualThan(static_cast(metaVariable.getHigh() - metaVariable.getLow()), metaVariable.getCube().getInternalBdd(), metaVariable.getNumberOfDdVariables()), {variable}); } else { // If there is no upper bound on this variable, the whole range is valid. Bdd result = this->getBddOne(); result.addMetaVariable(variable); return result; } } template template Add DdManager::getIdentity(storm::expressions::Variable const& variable) const { storm::dd::DdMetaVariable const& metaVariable = this->getMetaVariable(variable); STORM_LOG_THROW(metaVariable.hasHigh(), storm::exceptions::InvalidOperationException, "Cannot create identity for meta variable."); Add result = this->getAddZero(); for (int_fast64_t value = metaVariable.getLow(); value <= metaVariable.getHigh(); ++value) { result += this->getEncoding(variable, value).template toAdd() * this->getConstant(storm::utility::convertNumber(value)); } return result; } template Bdd DdManager::getIdentity(std::vector> const& variablePairs, bool restrictToFirstRange) const { auto result = this->getBddOne(); for (auto const& pair : variablePairs) { result &= this->getIdentity(pair.first, pair.second, restrictToFirstRange); } return result; } template Bdd DdManager::getIdentity(storm::expressions::Variable const& first, storm::expressions::Variable const& second, bool restrictToFirstRange) const { auto const& firstMetaVariable = this->getMetaVariable(first); auto const& secondMetaVariable = this->getMetaVariable(second); STORM_LOG_THROW(firstMetaVariable.getNumberOfDdVariables() == secondMetaVariable.getNumberOfDdVariables(), storm::exceptions::InvalidOperationException, "Mismatching sizes of meta variables."); auto const& firstDdVariables = firstMetaVariable.getDdVariables(); auto const& secondDdVariables = secondMetaVariable.getDdVariables(); auto result = restrictToFirstRange ? this->getRange(first) : this->getBddOne(); for (auto it1 = firstDdVariables.begin(), it2 = secondDdVariables.begin(), ite1 = firstDdVariables.end(); it1 != ite1; ++it1, ++it2) { result &= it1->iff(*it2); } return result; } #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Winfinite-recursion" #endif template Bdd DdManager::getCube(storm::expressions::Variable const& variable) const { return getCube({variable}); } #if defined(__clang__) #pragma clang diagnostic pop #endif template Bdd DdManager::getCube(std::set const& variables) const { Bdd result = this->getBddOne(); for (auto const& variable : variables) { storm::dd::DdMetaVariable const& metaVariable = this->getMetaVariable(variable); result &= metaVariable.getCube(); } return result; } template std::vector DdManager::cloneVariable(storm::expressions::Variable const& variable, std::string const& newMetaVariableName, boost::optional const& numberOfLayers) { std::vector newMetaVariables; auto const& ddMetaVariable = this->getMetaVariable(variable); if (ddMetaVariable.getType() == storm::dd::MetaVariableType::Bool) { newMetaVariables = this->addMetaVariable(newMetaVariableName, 3); } else if (ddMetaVariable.getType() == storm::dd::MetaVariableType::Int) { newMetaVariables = this->addMetaVariable(newMetaVariableName, ddMetaVariable.getLow(), ddMetaVariable.getHigh(), 3); } else if (ddMetaVariable.getType() == storm::dd::MetaVariableType::BitVector) { newMetaVariables = this->addBitVectorMetaVariable(newMetaVariableName, ddMetaVariable.getNumberOfDdVariables(), 3); } return newMetaVariables; } template std::pair DdManager::addMetaVariable(std::string const& name, int_fast64_t low, int_fast64_t high, boost::optional> const& position) { std::vector result = addMetaVariable(name, low, high, 2, position); return std::make_pair(result[0], result[1]); } template std::vector DdManager::addMetaVariable(std::string const& name, int_fast64_t low, int_fast64_t high, uint64_t numberOfLayers, boost::optional> const& position) { return this->addMetaVariableHelper(MetaVariableType::Int, name, std::max(static_cast(std::ceil(std::log2(high - low + 1))), static_cast(1)), numberOfLayers, position, std::make_pair(low, high)); } template std::vector DdManager::addBitVectorMetaVariable(std::string const& variableName, uint64_t bits, uint64_t numberOfLayers, boost::optional> const& position) { return this->addMetaVariableHelper(MetaVariableType::BitVector, variableName, bits, numberOfLayers, position); } template std::pair DdManager::addMetaVariable(std::string const& name, boost::optional> const& position) { std::vector result = this->addMetaVariableHelper(MetaVariableType::Bool, name, 1, 2, position); return std::make_pair(result[0], result[1]); } template std::vector DdManager::addMetaVariable(std::string const& name, uint64_t numberOfLayers, boost::optional> const& position) { return this->addMetaVariableHelper(MetaVariableType::Bool, name, 1, numberOfLayers, position); } template std::vector DdManager::addMetaVariableHelper(MetaVariableType const& type, std::string const& name, uint64_t numberOfDdVariables, uint64_t numberOfLayers, boost::optional> const& position, boost::optional> const& bounds) { // Check whether number of layers is legal. STORM_LOG_THROW(numberOfLayers >= 1, storm::exceptions::InvalidArgumentException, "Layers must be at least 1."); // Check that the number of DD variables is legal. STORM_LOG_THROW(numberOfDdVariables >= 1, storm::exceptions::InvalidArgumentException, "Illegal number of DD variables."); // Check whether the variable name is legal. STORM_LOG_THROW(name != "" && name.back() != '\'', storm::exceptions::InvalidArgumentException, "Illegal name of meta variable: '" << name << "'."); // Check whether a meta variable already exists. STORM_LOG_THROW(!this->hasMetaVariable(name), storm::exceptions::InvalidArgumentException, "A meta variable '" << name << "' already exists."); // If a specific position was requested, we compute it now. boost::optional level; if (position) { storm::dd::DdMetaVariable beforeVariable = this->getMetaVariable(position.get().second); level = position.get().first == MetaVariablePosition::Above ? std::numeric_limits::max() : std::numeric_limits::min(); for (auto const& ddVariable : beforeVariable.getDdVariables()) { level = position.get().first == MetaVariablePosition::Above ? std::min(level.get(), ddVariable.getLevel()) : std::max(level.get(), ddVariable.getLevel()); } if (position.get().first == MetaVariablePosition::Below) { ++level.get(); } } STORM_LOG_TRACE("Creating meta variable with " << numberOfDdVariables << " bit(s) and " << numberOfLayers << " layer(s)."); std::stringstream tmp1; std::vector result; for (uint64_t layer = 0; layer < numberOfLayers; ++layer) { if (type == MetaVariableType::Int) { result.emplace_back(manager->declareIntegerVariable(name + tmp1.str())); } else if (type == MetaVariableType::Bool) { result.emplace_back(manager->declareBooleanVariable(name + tmp1.str())); } else if (type == MetaVariableType::BitVector) { result.emplace_back(manager->declareBitVectorVariable(name + tmp1.str(), numberOfDdVariables)); } tmp1 << "'"; } std::vector>> variables(numberOfLayers); for (std::size_t i = 0; i < numberOfDdVariables; ++i) { std::vector> ddVariables = internalDdManager.createDdVariables(numberOfLayers, level); for (uint64_t layer = 0; layer < numberOfLayers; ++layer) { variables[layer].emplace_back(Bdd(*this, ddVariables[layer], {result[layer]})); } // If we are inserting the variable at a specific level, we need to prepare the level for the next pair // of variables. if (level) { level.get() += numberOfLayers; } } std::stringstream tmp2; for (uint64_t layer = 0; layer < numberOfLayers; ++layer) { if (bounds) { metaVariableMap.emplace(result[layer], DdMetaVariable(name + tmp2.str(), bounds.get().first, bounds.get().second, variables[layer])); } else { metaVariableMap.emplace(result[layer], DdMetaVariable(type, name + tmp2.str(), variables[layer])); } tmp2 << "'"; } return result; } template DdMetaVariable const& DdManager::getMetaVariable(storm::expressions::Variable const& variable) const { auto const& variablePair = metaVariableMap.find(variable); // Check whether the meta variable exists. STORM_LOG_THROW(variablePair != metaVariableMap.end(), storm::exceptions::InvalidArgumentException, "Unknown meta variable name '" << variable.getName() << "'."); return variablePair->second; } template std::set DdManager::getAllMetaVariableNames() const { std::set result; for (auto const& variablePair : metaVariableMap) { result.insert(variablePair.first.getName()); } return result; } template std::size_t DdManager::getNumberOfMetaVariables() const { return this->metaVariableMap.size(); } template bool DdManager::hasMetaVariable(std::string const& metaVariableName) const { return manager->hasVariable(metaVariableName); } template storm::expressions::Variable DdManager::getMetaVariable(std::string const& metaVariableName) const { // Check whether the meta variable exists. STORM_LOG_THROW(hasMetaVariable(metaVariableName), storm::exceptions::InvalidArgumentException, "Unknown meta variable name '" << metaVariableName << "'."); return manager->getVariable(metaVariableName); } template bool DdManager::supportsOrderedInsertion() const { return internalDdManager.supportsOrderedInsertion(); } template storm::expressions::ExpressionManager const& DdManager::getExpressionManager() const { return *manager; } template storm::expressions::ExpressionManager& DdManager::getExpressionManager() { return *manager; } template std::vector DdManager::getDdVariableNames() const { // First, we initialize a list DD variables and their names. std::vector> variablePairs; for (auto const& variablePair : this->metaVariableMap) { DdMetaVariable const& metaVariable = variablePair.second; // If the meta variable is of type bool, we don't need to suffix it with the bit number. if (metaVariable.getType() == MetaVariableType::Bool) { variablePairs.emplace_back(metaVariable.getDdVariables().front().getIndex(), variablePair.first.getName()); } else { // For integer-valued meta variables, we, however, have to add the suffix. for (uint_fast64_t variableIndex = 0; variableIndex < metaVariable.getNumberOfDdVariables(); ++variableIndex) { variablePairs.emplace_back(metaVariable.getDdVariables()[variableIndex].getIndex(), variablePair.first.getName() + '.' + std::to_string(variableIndex)); } } } // Then, we sort this list according to the indices of the ADDs. std::sort(variablePairs.begin(), variablePairs.end(), [](std::pair const& a, std::pair const& b) { return a.first < b.first; }); // Now, we project the sorted vector to its second component. std::vector result; for (auto const& element : variablePairs) { result.push_back(element.second); } return result; } template std::vector DdManager::getDdVariables() const { // First, we initialize a list DD variables and their names. std::vector> variablePairs; for (auto const& variablePair : this->metaVariableMap) { DdMetaVariable const& metaVariable = variablePair.second; // If the meta variable is of type bool, we don't need to suffix it with the bit number. if (metaVariable.getType() == MetaVariableType::Bool) { variablePairs.emplace_back(metaVariable.getDdVariables().front().getIndex(), variablePair.first); } else { // For integer-valued meta variables, we, however, have to add the suffix. for (uint_fast64_t variableIndex = 0; variableIndex < metaVariable.getNumberOfDdVariables(); ++variableIndex) { variablePairs.emplace_back(metaVariable.getDdVariables()[variableIndex].getIndex(), variablePair.first); } } } // Then, we sort this list according to the indices of the ADDs. std::sort(variablePairs.begin(), variablePairs.end(), [](std::pair const& a, std::pair const& b) { return a.first < b.first; }); // Now, we project the sorted vector to its second component. std::vector result; for (auto const& element : variablePairs) { result.push_back(element.second); } return result; } template void DdManager::allowDynamicReordering(bool value) { internalDdManager.allowDynamicReordering(value); } template bool DdManager::isDynamicReorderingAllowed() const { return internalDdManager.isDynamicReorderingAllowed(); } template void DdManager::triggerReordering() { internalDdManager.triggerReordering(); } template std::set DdManager::getAllMetaVariables() const { std::set result; for (auto const& variable : this->metaVariableMap) { result.insert(variable.first); } return result; } template std::vector DdManager::getSortedVariableIndices() const { return this->getSortedVariableIndices(this->getAllMetaVariables()); } template std::vector DdManager::getSortedVariableIndices(std::set const& metaVariables) const { std::vector ddVariableIndices; for (auto const& metaVariable : metaVariables) { for (auto const& ddVariable : metaVariableMap.at(metaVariable).getDdVariables()) { ddVariableIndices.push_back(ddVariable.getIndex()); } } // Next, we need to sort them, since they may be arbitrarily ordered otherwise. std::sort(ddVariableIndices.begin(), ddVariableIndices.end()); return ddVariableIndices; } template InternalDdManager& DdManager::getInternalDdManager() { return internalDdManager; } template InternalDdManager const& DdManager::getInternalDdManager() const { return internalDdManager; } template InternalDdManager* DdManager::getInternalDdManagerPointer() { return &internalDdManager; } template InternalDdManager const* DdManager::getInternalDdManagerPointer() const { return &internalDdManager; } template void DdManager::debugCheck() const { internalDdManager.debugCheck(); } template class DdManager; template Add DdManager::getAddZero() const; template Add DdManager::getAddZero() const; #ifdef STORM_HAVE_CARL template Add DdManager::getAddZero() const; #endif template Add DdManager::getAddOne() const; template Add DdManager::getAddOne() const; #ifdef STORM_HAVE_CARL template Add DdManager::getAddOne() const; #endif template Add DdManager::getInfinity() const; template Add DdManager::getInfinity() const; template Add DdManager::getConstant(double const& value) const; template Add DdManager::getConstant(uint_fast64_t const& value) const; #ifdef STORM_HAVE_CARL template Add DdManager::getConstant(storm::RationalNumber const& value) const; #endif template Add DdManager::getIdentity(storm::expressions::Variable const& variable) const; template Add DdManager::getIdentity(storm::expressions::Variable const& variable) const; template class DdManager; template Add DdManager::getAddZero() const; template Add DdManager::getAddZero() const; #ifdef STORM_HAVE_CARL template Add DdManager::getAddZero() const; template Add DdManager::getAddZero() const; #endif template Add DdManager::getAddUndefined() const; template Add DdManager::getAddUndefined() const; #ifdef STORM_HAVE_CARL template Add DdManager::getAddUndefined() const; template Add DdManager::getAddUndefined() const; #endif template Add DdManager::getAddOne() const; template Add DdManager::getAddOne() const; #ifdef STORM_HAVE_CARL template Add DdManager::getAddOne() const; template Add DdManager::getAddOne() const; #endif template Add DdManager::getInfinity() const; template Add DdManager::getInfinity() const; #ifdef STORM_HAVE_CARL template Add DdManager::getInfinity() const; template Add DdManager::getInfinity() const; #endif template Add DdManager::getConstant(double const& value) const; template Add DdManager::getConstant(uint_fast64_t const& value) const; #ifdef STORM_HAVE_CARL template Add DdManager::getConstant(storm::RationalNumber const& value) const; template Add DdManager::getConstant(storm::RationalFunction const& value) const; #endif template Add DdManager::getIdentity(storm::expressions::Variable const& variable) const; template Add DdManager::getIdentity(storm::expressions::Variable const& variable) const; #ifdef STORM_HAVE_CARL template Add DdManager::getIdentity(storm::expressions::Variable const& variable) const; template Add DdManager::getIdentity(storm::expressions::Variable const& variable) const; #endif } }