#include <boost/container/flat_set.hpp> #include <map> #include "DFT.h" #include "DFTBuilder.h" #include "src/exceptions/NotSupportedException.h" #include "DFTIsomorphism.h" #include "utility/iota_n.h" #include "utility/vector.h" namespace storm { namespace storage { template<typename ValueType> DFT<ValueType>::DFT(DFTElementVector const& elements, DFTElementPointer const& tle) : mElements(elements), mNrOfBEs(0), mNrOfSpares(0), mTopLevelIndex(tle->id()), mMaxSpareChildCount(0) { assert(elementIndicesCorrect()); size_t nrRepresentatives = 0; for (auto& elem : mElements) { if (isRepresentative(elem->id())) { ++nrRepresentatives; } if(elem->isBasicElement()) { ++mNrOfBEs; } else if (elem->isSpareGate()) { ++mNrOfSpares; bool firstChild = true; mMaxSpareChildCount = std::max(mMaxSpareChildCount, std::static_pointer_cast<DFTSpare<ValueType>>(elem)->children().size()); for(auto const& spareReprs : std::static_pointer_cast<DFTSpare<ValueType>>(elem)->children()) { std::set<size_t> module = {spareReprs->id()}; spareReprs->extendSpareModule(module); std::vector<size_t> sparesAndBes; for(size_t modelem : module) { if (spareReprs->id() != modelem && (isRepresentative(modelem) || (!firstChild && mTopLevelIndex == modelem))) { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Module for '" << spareReprs->name() << "' contains more than one representative."); } if(mElements[modelem]->isSpareGate() || mElements[modelem]->isBasicElement()) { sparesAndBes.push_back(modelem); mRepresentants.insert(std::make_pair(modelem, spareReprs->id())); } } mSpareModules.insert(std::make_pair(spareReprs->id(), sparesAndBes)); firstChild = false; } } else if (elem->isDependency()) { mDependencies.push_back(elem->id()); } } // For the top module, we assume, contrary to [Jun15], that we have all spare gates and basic elements which are not in another module. std::set<size_t> topModuleSet; // Initialize with all ids. for(auto const& elem : mElements) { if (elem->isBasicElement() || elem->isSpareGate()) { topModuleSet.insert(elem->id()); } } // Erase spare modules for(auto const& module : mSpareModules) { for(auto const& index : module.second) { topModuleSet.erase(index); } } // Extend top module and insert those elements which are part of the top module and a spare module mElements[mTopLevelIndex]->extendSpareModule(topModuleSet); mTopModule = std::vector<size_t>(topModuleSet.begin(), topModuleSet.end()); // Clear all spare modules where at least one element is also in the top module if (!mTopModule.empty()) { for (auto& module : mSpareModules) { if (std::find(module.second.begin(), module.second.end(), mTopModule.front()) != module.second.end()) { module.second.clear(); } } } //Reserve space for failed spares ++mMaxSpareChildCount; size_t usageInfoBits = storm::utility::math::uint64_log2(mMaxSpareChildCount) + 1; mStateVectorSize = nrElements() * 2 + mNrOfSpares * usageInfoBits + nrRepresentatives; } template<typename ValueType> DFTStateGenerationInfo DFT<ValueType>::buildStateGenerationInfo(storm::storage::DFTIndependentSymmetries const& symmetries) const { // Use symmetry // Collect all elements in the first subtree // TODO make recursive to use for nested subtrees DFTStateGenerationInfo generationInfo(nrElements(), mMaxSpareChildCount); // Generate Pre and Post info for restrictions for(auto const& elem : mElements) { if(!elem->isDependency() && !elem->isRestriction()) { generationInfo.setRestrictionPreElements(elem->id(), elem->seqRestrictionPres()); generationInfo.setRestrictionPostElements(elem->id(), elem->seqRestrictionPosts()); } } // Perform DFS and insert all elements of subtree sequentially size_t stateIndex = 0; std::queue<size_t> visitQueue; storm::storage::BitVector visited(nrElements(), false); // TODO make subfunction for this? if (symmetries.groups.empty()) { // Perform DFS for whole tree visitQueue.push(mTopLevelIndex); stateIndex = performStateGenerationInfoDFS(generationInfo, visitQueue, visited, stateIndex); } else { for (size_t symmetryIndex : symmetries.sortedSymmetries) { assert(!visited[symmetryIndex]); auto const& symmetryGroup = symmetries.groups.at(symmetryIndex); assert(!symmetryGroup.empty()); // Insert all elements of first subtree of each symmetry size_t groupIndex = stateIndex; for (std::vector<size_t> const& symmetryElement : symmetryGroup) { if (visited[symmetryElement[0]]) { groupIndex = std::min(groupIndex, generationInfo.getStateIndex(symmetryElement[0])); } else { stateIndex = generateStateInfo(generationInfo, symmetryElement[0], visited, stateIndex); } } size_t offset = stateIndex - groupIndex; // Mirror symmetries size_t noSymmetricElements = symmetryGroup.front().size(); assert(noSymmetricElements > 1); for (std::vector<size_t> symmetricElements : symmetryGroup) { assert(symmetricElements.size() == noSymmetricElements); if (visited[symmetricElements[1]]) { // Elements already mirrored for (size_t index : symmetricElements) { assert(visited[index]); } continue; } // Initialize for original element size_t originalElement = symmetricElements[0]; size_t index = generationInfo.getStateIndex(originalElement); size_t activationIndex = isRepresentative(originalElement) ? generationInfo.getSpareActivationIndex(originalElement) : 0; size_t usageIndex = mElements[originalElement]->isSpareGate() ? generationInfo.getSpareUsageIndex(originalElement) : 0; // Mirror symmetry for each element for (size_t i = 1; i < symmetricElements.size(); ++i) { size_t symmetricElement = symmetricElements[i]; visited.set(symmetricElement); generationInfo.addStateIndex(symmetricElement, index + offset * i); stateIndex += 2; assert((activationIndex > 0) == isRepresentative(symmetricElement)); if (activationIndex > 0) { generationInfo.addSpareActivationIndex(symmetricElement, activationIndex + offset * i); ++stateIndex; } assert((usageIndex > 0) == mElements[symmetricElement]->isSpareGate()); if (usageIndex > 0) { generationInfo.addSpareUsageIndex(symmetricElement, usageIndex + offset * i); stateIndex += generationInfo.usageInfoBits(); } } } // Store starting indices of symmetry groups std::vector<size_t> symmetryIndices; for (size_t i = 0; i < noSymmetricElements; ++i) { symmetryIndices.push_back(groupIndex + i * offset); } generationInfo.addSymmetry(offset, symmetryIndices); } } // TODO symmetries in dependencies? // Consider dependencies for (size_t idDependency : getDependencies()) { std::shared_ptr<DFTDependency<ValueType> const> dependency = getDependency(idDependency); visitQueue.push(dependency->id()); visitQueue.push(dependency->triggerEvent()->id()); visitQueue.push(dependency->dependentEvent()->id()); } stateIndex = performStateGenerationInfoDFS(generationInfo, visitQueue, visited, stateIndex); // Visit all remaining states for (size_t i = 0; i < visited.size(); ++i) { if (!visited[i]) { visitQueue.push(i); stateIndex = performStateGenerationInfoDFS(generationInfo, visitQueue, visited, stateIndex); } } generationInfo.generateSymmetries(symmetries); STORM_LOG_TRACE(generationInfo); assert(stateIndex == mStateVectorSize); assert(visited.full()); return generationInfo; } template<typename ValueType> size_t DFT<ValueType>::generateStateInfo(DFTStateGenerationInfo& generationInfo, size_t id, storm::storage::BitVector& visited, size_t stateIndex) const { assert(!visited[id]); visited.set(id); // Reserve bits for element generationInfo.addStateIndex(id, stateIndex); stateIndex += 2; if (isRepresentative(id)) { generationInfo.addSpareActivationIndex(id, stateIndex); ++stateIndex; } if (mElements[id]->isSpareGate()) { generationInfo.addSpareUsageIndex(id, stateIndex); stateIndex += generationInfo.usageInfoBits(); } return stateIndex; } template<typename ValueType> size_t DFT<ValueType>::performStateGenerationInfoDFS(DFTStateGenerationInfo& generationInfo, std::queue<size_t>& visitQueue, storm::storage::BitVector& visited, size_t stateIndex) const { while (!visitQueue.empty()) { size_t id = visitQueue.front(); visitQueue.pop(); if (visited[id]) { // Already visited continue; } stateIndex = generateStateInfo(generationInfo, id, visited, stateIndex); // Insert children if (mElements[id]->isGate()) { for (auto const& child : std::static_pointer_cast<DFTGate<ValueType>>(mElements[id])->children()) { visitQueue.push(child->id()); } } } return stateIndex; } template<typename ValueType> std::vector<DFT<ValueType>> DFT<ValueType>::topModularisation() const { assert(isGate(mTopLevelIndex)); auto const& children = getGate(mTopLevelIndex)->children(); std::map<size_t, std::vector<size_t>> subdfts; for(auto const& child : children) { std::vector<size_t> isubdft; if(child->nrParents() > 1 || child->hasOutgoingDependencies()) { return {*this}; } if (isGate(child->id())) { isubdft = getGate(child->id())->independentSubDft(false); } else { assert(isBasicElement(child->id())); if(getBasicElement(child->id())->hasIngoingDependencies()) { return {*this}; } } if(isubdft.empty()) { return {*this}; } else { subdfts[child->id()] = isubdft; } } std::vector<DFT<ValueType>> res; for(auto const& subdft : subdfts) { DFTBuilder<ValueType> builder; for(size_t id : subdft.second) { builder.copyElement(mElements[id]); } builder.setTopLevel(mElements[subdft.first]->name()); res.push_back(builder.build()); } return res; } template<typename ValueType> DFT<ValueType> DFT<ValueType>::optimize() const { std::vector<size_t> modIdea = findModularisationRewrite(); STORM_LOG_DEBUG("Modularisation idea: " << storm::utility::vector::toString(modIdea)); if (modIdea.empty()) { // No rewrite needed return *this; } std::vector<std::vector<size_t>> rewriteIds; rewriteIds.push_back(modIdea); DFTBuilder<ValueType> builder; // Accumulate elements which must be rewritten std::set<size_t> rewriteSet; for (std::vector<size_t> rewrites : rewriteIds) { rewriteSet.insert(rewrites.front()); } // Copy all other elements which do not change for (auto elem : mElements) { if (rewriteSet.count(elem->id()) == 0) { builder.copyElement(elem); } } // Add rewritten elements for (std::vector<size_t> rewrites : rewriteIds) { assert(rewrites.size() > 1); assert(mElements[rewrites[1]]->hasParents()); assert(mElements[rewrites[1]]->parents().front()->isGate()); DFTGatePointer originalParent = std::static_pointer_cast<DFTGate<ValueType>>(mElements[rewrites[1]]->parents().front()); std::string newParentName = builder.getUniqueName(originalParent->name()); // Accumulate children names std::vector<std::string> childrenNames; for (size_t i = 1; i < rewrites.size(); ++i) { assert(mElements[rewrites[i]]->parents().front()->id() == originalParent->id()); // Children have the same father childrenNames.push_back(mElements[rewrites[i]]->name()); } // Add element inbetween parent and children switch (originalParent->type()) { case DFTElementType::AND: builder.addAndElement(newParentName, childrenNames); break; case DFTElementType::OR: builder.addOrElement(newParentName, childrenNames); break; case DFTElementType::BE: case DFTElementType::CONSTF: case DFTElementType::CONSTS: case DFTElementType::VOT: case DFTElementType::PAND: case DFTElementType::SPARE: case DFTElementType::POR: case DFTElementType::PDEP: case DFTElementType::SEQ: case DFTElementType::MUTEX: // Other elements are not supported assert(false); break; default: assert(false); } // Add parent with the new child newParent and all its remaining children childrenNames.clear(); childrenNames.push_back(newParentName); for (auto const& child : originalParent->children()) { if (std::find(rewrites.begin()+1, rewrites.end(), child->id()) == rewrites.end()) { // Child was not rewritten and must be kept childrenNames.push_back(child->name()); } } builder.copyGate(originalParent, childrenNames); } builder.setTopLevel(mElements[mTopLevelIndex]->name()); // TODO use reference? DFT<ValueType> newDft = builder.build(); STORM_LOG_TRACE(newDft.getElementsString()); return newDft.optimize(); } template<typename ValueType> std::string DFT<ValueType>::getElementsString() const { std::stringstream stream; for (auto const& elem : mElements) { stream << "[" << elem->id() << "]" << elem->toString() << std::endl; } return stream.str(); } template<typename ValueType> std::string DFT<ValueType>::getInfoString() const { std::stringstream stream; stream << "Top level index: " << mTopLevelIndex << ", Nr BEs" << mNrOfBEs; return stream.str(); } template<typename ValueType> std::string DFT<ValueType>::getSpareModulesString() const { std::stringstream stream; stream << "[" << mElements[mTopLevelIndex]->id() << "] {"; std::vector<size_t>::const_iterator it = mTopModule.begin(); if (it == mTopModule.end()) { stream << "}" << std::endl; return stream.str(); } assert(it != mTopModule.end()); stream << mElements[(*it)]->name(); ++it; while(it != mTopModule.end()) { stream << ", " << mElements[(*it)]->name(); ++it; } stream << "}" << std::endl; for(auto const& spareModule : mSpareModules) { stream << "[" << mElements[spareModule.first]->name() << "] = {"; if (!spareModule.second.empty()) { std::vector<size_t>::const_iterator it = spareModule.second.begin(); assert(it != spareModule.second.end()); stream << mElements[(*it)]->name(); ++it; while(it != spareModule.second.end()) { stream << ", " << mElements[(*it)]->name(); ++it; } } stream << "}" << std::endl; } return stream.str(); } template<typename ValueType> std::string DFT<ValueType>::getElementsWithStateString(DFTStatePointer const& state) const{ std::stringstream stream; for (auto const& elem : mElements) { stream << "[" << elem->id() << "]"; stream << elem->toString(); if (elem->isDependency()) { stream << "\t** " << storm::storage::toChar(state->getDependencyState(elem->id())); } else { stream << "\t** " << storm::storage::toChar(state->getElementState(elem->id())); if(elem->isSpareGate()) { size_t useId = state->uses(elem->id()); if(useId == elem->id() || state->isActive(useId)) { stream << "actively "; } stream << "using " << useId; } } stream << std::endl; } return stream.str(); } // TODO rewrite to only use bitvector and id template<typename ValueType> std::string DFT<ValueType>::getStateString(DFTStatePointer const& state) const{ std::stringstream stream; stream << "(" << state->getId() << ") "; for (auto const& elem : mElements) { if (elem->isDependency()) { stream << storm::storage::toChar(state->getDependencyState(elem->id())) << "[dep]"; } else { stream << storm::storage::toChar(state->getElementState(elem->id())); if(elem->isSpareGate()) { stream << "["; size_t useId = state->uses(elem->id()); if(useId == elem->id() || state->isActive(useId)) { stream << "actively "; } stream << "using " << useId << "]"; } } } return stream.str(); } template<typename ValueType> size_t DFT<ValueType>::getChild(size_t spareId, size_t nrUsedChild) const { assert(mElements[spareId]->isSpareGate()); return getGate(spareId)->children()[nrUsedChild]->id(); } template<typename ValueType> size_t DFT<ValueType>::getNrChild(size_t spareId, size_t childId) const { assert(mElements[spareId]->isSpareGate()); DFTElementVector children = getGate(spareId)->children(); for (size_t nrChild = 0; nrChild < children.size(); ++nrChild) { if (children[nrChild]->id() == childId) { return nrChild; } } assert(false); } template <typename ValueType> std::vector<size_t> DFT<ValueType>::getIndependentSubDftRoots(size_t index) const { auto elem = getElement(index); auto ISD = elem->independentSubDft(false); return ISD; } template<typename ValueType> std::vector<size_t> DFT<ValueType>::immediateFailureCauses(size_t index) const { if(isGate(index)) { } else { return {index}; } } template<typename ValueType> DFTColouring<ValueType> DFT<ValueType>::colourDFT() const { return DFTColouring<ValueType>(*this); } template<typename ValueType> std::map<size_t, size_t> DFT<ValueType>::findBijection(size_t index1, size_t index2, DFTColouring<ValueType> const& colouring, bool sparesAsLeaves) const { STORM_LOG_TRACE("Considering ids " << index1 << ", " << index2 << " for isomorphism."); bool sharedSpareMode = false; std::map<size_t, size_t> bijection; if (isBasicElement(index1)) { if (!isBasicElement(index2)) { return {}; } if (colouring.hasSameColour(index1, index2)) { bijection[index1] = index2; return bijection; } else { return {}; } } assert(isGate(index1)); assert(isGate(index2)); std::vector<size_t> isubdft1 = getGate(index1)->independentSubDft(false); std::vector<size_t> isubdft2 = getGate(index2)->independentSubDft(false); if(isubdft1.empty() || isubdft2.empty() || isubdft1.size() != isubdft2.size()) { if (isubdft1.empty() && isubdft2.empty() && sparesAsLeaves) { // Check again for shared spares sharedSpareMode = true; isubdft1 = getGate(index1)->independentSubDft(false, true); isubdft2 = getGate(index2)->independentSubDft(false, true); if(isubdft1.empty() || isubdft2.empty() || isubdft1.size() != isubdft2.size()) { return {}; } } else { return {}; } } STORM_LOG_TRACE("Checking subdfts from " << index1 << ", " << index2 << " for isomorphism."); auto LHS = colouring.colourSubdft(isubdft1); auto RHS = colouring.colourSubdft(isubdft2); auto IsoCheck = DFTIsomorphismCheck<ValueType>(LHS, RHS, *this); while (IsoCheck.findNextIsomorphism()) { bijection = IsoCheck.getIsomorphism(); if (sharedSpareMode) { bool bijectionSpareCompatible = true; for (size_t elementId : isubdft1) { if (getElement(elementId)->isSpareGate()) { std::shared_ptr<DFTSpare<ValueType>> spareLeft = std::static_pointer_cast<DFTSpare<ValueType>>(mElements[elementId]); std::shared_ptr<DFTSpare<ValueType>> spareRight = std::static_pointer_cast<DFTSpare<ValueType>>(mElements[bijection.at(elementId)]); if (spareLeft->nrChildren() != spareRight->nrChildren()) { bijectionSpareCompatible = false; break; } // Check bijection for spare children for (size_t i = 0; i < spareLeft->nrChildren(); ++i) { size_t childLeftId = spareLeft->children().at(i)->id(); size_t childRightId = spareRight->children().at(i)->id(); assert(bijection.count(childLeftId) == 0); if (childLeftId == childRightId) { // Ignore shared child continue; } // TODO generalize for more than one parent if (spareLeft->children().at(i)->nrParents() != 1 || spareRight->children().at(i)->nrParents() != 1) { bijectionSpareCompatible = false; break; } std::map<size_t, size_t> tmpBijection = findBijection(childLeftId, childRightId, colouring, false); if (!tmpBijection.empty()) { bijection.insert(tmpBijection.begin(), tmpBijection.end()); } else { bijectionSpareCompatible = false; break; } } if (!bijectionSpareCompatible) { break; } } } if (bijectionSpareCompatible) { return bijection; } } else { return bijection; } } // end while return {}; } template<typename ValueType> DFTIndependentSymmetries DFT<ValueType>::findSymmetries(DFTColouring<ValueType> const& colouring) const { std::vector<size_t> vec; vec.reserve(nrElements()); storm::utility::iota_n(std::back_inserter(vec), nrElements(), 0); BijectionCandidates<ValueType> completeCategories = colouring.colourSubdft(vec); std::map<size_t, std::vector<std::vector<size_t>>> res; for(auto const& colourClass : completeCategories.gateCandidates) { if(colourClass.second.size() > 1) { std::set<size_t> foundEqClassFor; for(auto it1 = colourClass.second.cbegin(); it1 != colourClass.second.cend(); ++it1) { std::vector<std::vector<size_t>> symClass; if(foundEqClassFor.count(*it1) > 0) { // This item is already in a class. continue; } if(!getGate(*it1)->hasOnlyStaticParents()) { continue; } std::pair<std::vector<size_t>, std::vector<size_t>> influencedElem1Ids = getSortedParentAndOutDepIds(*it1); auto it2 = it1; for(++it2; it2 != colourClass.second.cend(); ++it2) { if(!getGate(*it2)->hasOnlyStaticParents()) { continue; } std::vector<size_t> sortedParent2Ids = getGate(*it2)->parentIds(); std::sort(sortedParent2Ids.begin(), sortedParent2Ids.end()); if(influencedElem1Ids == getSortedParentAndOutDepIds(*it2)) { std::map<size_t, size_t> bijection = findBijection(*it1, *it2, colouring, true); if (!bijection.empty()) { STORM_LOG_TRACE("Subdfts are symmetric"); foundEqClassFor.insert(*it2); if(symClass.empty()) { for(auto const& i : bijection) { symClass.push_back(std::vector<size_t>({i.first})); } } auto symClassIt = symClass.begin(); for(auto const& i : bijection) { symClassIt->emplace_back(i.second); ++symClassIt; } } } } if(!symClass.empty()) { res.emplace(*it1, symClass); } } } } return DFTIndependentSymmetries(res); } template<typename ValueType> std::vector<size_t> DFT<ValueType>::findModularisationRewrite() const { for(auto const& e : mElements) { if(e->isGate() && (e->type() == DFTElementType::AND || e->type() == DFTElementType::OR) ) { // suitable parent gate! - Lets check the independent submodules of the children auto const& children = std::static_pointer_cast<DFTGate<ValueType>>(e)->children(); for(auto const& child : children) { auto ISD = std::static_pointer_cast<DFTGate<ValueType>>(child)->independentSubDft(true); // In the ISD, check for other children: std::vector<size_t> rewrite = {e->id(), child->id()}; for(size_t isdElemId : ISD) { if(isdElemId == child->id()) continue; if(std::find_if(children.begin(), children.end(), [&isdElemId](std::shared_ptr<DFTElement<ValueType>> const& e) { return e->id() == isdElemId; } ) != children.end()) { rewrite.push_back(isdElemId); } } if(rewrite.size() > 2 && rewrite.size() < children.size() - 1) { return rewrite; } } } } return {}; } template<typename ValueType> std::pair<std::vector<size_t>, std::vector<size_t>> DFT<ValueType>::getSortedParentAndOutDepIds(size_t index) const { std::pair<std::vector<size_t>, std::vector<size_t>> res; res.first = getElement(index)->parentIds(); std::sort(res.first.begin(), res.first.end()); for(auto const& dep : getElement(index)->outgoingDependencies()) { res.second.push_back(dep->id()); } std::sort(res.second.begin(), res.second.end()); return res; } // Explicitly instantiate the class. template class DFT<double>; #ifdef STORM_HAVE_CARL template class DFT<RationalFunction>; #endif } }