Browse Source

Started refactoring bit vector class.

Former-commit-id: a2fecfce2b
main
dehnert 12 years ago
parent
commit
07fbff7a07
  1. 50
      src/counterexamples/PathBasedSubsystemGenerator.h
  2. 2
      src/counterexamples/SMTMinimalCommandSetGenerator.h
  3. 3
      src/models/AtomicPropositionsLabeling.h
  4. 508
      src/storage/BitVector.cpp
  5. 674
      src/storage/BitVector.h
  6. 2
      src/storage/StronglyConnectedComponentDecomposition.cpp

50
src/counterexamples/PathBasedSubsystemGenerator.h

@ -55,25 +55,25 @@ public:
// First store all transitions from initial states // First store all transitions from initial states
// Also save all found initial states in array of discovered states. // Also save all found initial states in array of discovered states.
for(storm::storage::BitVector::constIndexIterator init = subSysStates.begin(); init != subSysStates.end(); ++init) {
for(auto init : subSysStates) {
//use init state only if it is allowed //use init state only if it is allowed
if(allowedStates.get(*init)) {
if(allowedStates.get(init)) {
if(terminalStates.get(*init)) {
if(terminalStates.get(init)) {
// it's an init -> target search // it's an init -> target search
// save target state as discovered and get it's outgoing transitions // save target state as discovered and get it's outgoing transitions
distances[*init].first = *init;
distances[*init].second = (T) 1;
distances[init].first = init;
distances[init].second = (T) 1;
} }
typename storm::storage::SparseMatrix<T>::ConstRowIterator rowIt = transMat.begin(*init);
typename storm::storage::SparseMatrix<T>::ConstRowIterator rowIt = transMat.begin(init);
for(auto trans = rowIt.begin() ; trans != rowIt.end(); ++trans) { for(auto trans = rowIt.begin() ; trans != rowIt.end(); ++trans) {
//save transition only if it's no 'virtual transition of prob 0 and it doesn't go from init state to init state. //save transition only if it's no 'virtual transition of prob 0 and it doesn't go from init state to init state.
if(trans.value() != (T) 0 && !subSysStates.get(trans.column())) { if(trans.value() != (T) 0 && !subSysStates.get(trans.column())) {
//new state? //new state?
if(distances[trans.column()].second == (T) -1) { if(distances[trans.column()].second == (T) -1) {
distances[trans.column()].first = *init;
distances[trans.column()].first = init;
distances[trans.column()].second = trans.value(); distances[trans.column()].second = trans.value();
activeSet.insert(std::pair<uint_fast64_t, T>(trans.column(), distances[trans.column()].second)); activeSet.insert(std::pair<uint_fast64_t, T>(trans.column(), distances[trans.column()].second));
@ -91,7 +91,7 @@ public:
} }
} }
distances[trans.column()].first = *init;
distances[trans.column()].first = init;
distances[trans.column()].second = trans.value(); distances[trans.column()].second = trans.value();
activeSet.insert(std::pair<uint_fast64_t, T>(trans.column(), trans.value())); activeSet.insert(std::pair<uint_fast64_t, T>(trans.column(), trans.value()));
@ -171,30 +171,30 @@ public:
// First store all transitions from initial states // First store all transitions from initial states
// Also save all found initial states in array of discovered states. // Also save all found initial states in array of discovered states.
for(storm::storage::BitVector::constIndexIterator init = subSysStates.begin(); init != subSysStates.end(); ++init) {
for(auto init : subSysStates) {
//use init state only if it is allowed //use init state only if it is allowed
if(allowedStates.get(*init)) {
if(allowedStates.get(init)) {
if(terminalStates.get(*init)) {
if(terminalStates.get(init)) {
// it's a subsys -> subsys search // it's a subsys -> subsys search
// ignore terminal state completely // ignore terminal state completely
// (since any target state that is only reached by a path going through this state should not be reached) // (since any target state that is only reached by a path going through this state should not be reached)
continue; continue;
} }
typename storm::storage::SparseMatrix<T>::ConstRowIterator rowIt = transMat.begin(*init);
typename storm::storage::SparseMatrix<T>::ConstRowIterator rowIt = transMat.begin(init);
for(typename storm::storage::SparseMatrix<T>::ConstIterator trans = rowIt.begin(); trans != rowIt.end(); ++trans) { for(typename storm::storage::SparseMatrix<T>::ConstIterator trans = rowIt.begin(); trans != rowIt.end(); ++trans) {
//save transition only if it's no 'virtual transition of prob 0 and it doesn't go from init state to init state. //save transition only if it's no 'virtual transition of prob 0 and it doesn't go from init state to init state.
if(trans.value() != (T) 0 && !subSysStates.get(trans.column())) { if(trans.value() != (T) 0 && !subSysStates.get(trans.column())) {
//new state? //new state?
if(distances[trans.column()].second == (T) -1) { if(distances[trans.column()].second == (T) -1) {
//for initialization of subsys -> subsys search use prob (init -> subsys state -> found state) instead of prob(subsys state -> found state) //for initialization of subsys -> subsys search use prob (init -> subsys state -> found state) instead of prob(subsys state -> found state)
distances[trans.column()].first = *init;
distances[trans.column()].second = trans.value() * (itDistances[*init].second == -1 ? 1 : itDistances[*init].second);
distances[trans.column()].first = init;
distances[trans.column()].second = trans.value() * (itDistances[init].second == -1 ? 1 : itDistances[init].second);
activeSet.insert(std::pair<uint_fast64_t, T>(trans.column(), distances[trans.column()].second)); activeSet.insert(std::pair<uint_fast64_t, T>(trans.column(), distances[trans.column()].second));
} }
else if(distances[trans.column()].second < trans.value() * itDistances[*init].second){
else if(distances[trans.column()].second < trans.value() * itDistances[init].second){
//This state has already been discovered //This state has already been discovered
//And the distance can be improved by using this transition. //And the distance can be improved by using this transition.
@ -208,8 +208,8 @@ public:
} }
//for initialization of subsys -> subsys search use prob (init -> subsys state -> found state) instead of prob(subsys state -> found state) //for initialization of subsys -> subsys search use prob (init -> subsys state -> found state) instead of prob(subsys state -> found state)
distances[trans.column()].first = *init;
distances[trans.column()].second = trans.value() * (itDistances[*init].second == -1 ? 1 : itDistances[*init].second);
distances[trans.column()].first = init;
distances[trans.column()].second = trans.value() * (itDistances[init].second == -1 ? 1 : itDistances[init].second);
activeSet.insert(std::pair<uint_fast64_t, T>(trans.column(), trans.value())); activeSet.insert(std::pair<uint_fast64_t, T>(trans.column(), trans.value()));
} }
@ -429,12 +429,12 @@ public:
uint_fast64_t bestIndex = 0; uint_fast64_t bestIndex = 0;
T bestValue = (T) 0; T bestValue = (T) 0;
for(storm::storage::BitVector::constIndexIterator term = terminalStates.begin(); term != terminalStates.end(); ++term) {
for(auto term : terminalStates) {
//the terminal state might not have been found if it is in a system of forbidden states //the terminal state might not have been found if it is in a system of forbidden states
if(distances[*term].second != -1 && distances[*term].second > bestValue){
bestIndex = *term;
bestValue = distances[*term].second;
if(distances[term].second != -1 && distances[term].second > bestValue){
bestIndex = term;
bestValue = distances[term].second;
//if set, stop since the first target that is not null was the only one found //if set, stop since the first target that is not null was the only one found
if(stopAtFirstTarget) break; if(stopAtFirstTarget) break;
@ -443,12 +443,12 @@ public:
if(!itSearch) { if(!itSearch) {
// it's a subSys->subSys search. So target states are terminals and subsys states // it's a subSys->subSys search. So target states are terminals and subsys states
for(auto term = subSysStates.begin(); term != subSysStates.end(); ++term) {
for(auto term : subSysStates) {
//the terminal state might not have been found if it is in a system of forbidden states //the terminal state might not have been found if it is in a system of forbidden states
if(distances[*term].second != -1 && distances[*term].second > bestValue){
bestIndex = *term;
bestValue = distances[*term].second;
if(distances[term].second != -1 && distances[term].second > bestValue){
bestIndex = term;
bestValue = distances[term].second;
//if set, stop since the first target that is not null was the only one found //if set, stop since the first target that is not null was the only one found
if(stopAtFirstTarget) break; if(stopAtFirstTarget) break;

2
src/counterexamples/SMTMinimalCommandSetGenerator.h

@ -107,7 +107,7 @@ namespace storm {
relevancyInformation.relevantStates &= ~psiStates; relevancyInformation.relevantStates &= ~psiStates;
LOG4CPLUS_DEBUG(logger, "Found " << relevancyInformation.relevantStates.getNumberOfSetBits() << " relevant states."); LOG4CPLUS_DEBUG(logger, "Found " << relevancyInformation.relevantStates.getNumberOfSetBits() << " relevant states.");
LOG4CPLUS_DEBUG(logger, relevancyInformation.relevantStates.toString());
LOG4CPLUS_DEBUG(logger, relevancyInformation.relevantStates);
// Retrieve some references for convenient access. // Retrieve some references for convenient access.
storm::storage::SparseMatrix<T> const& transitionMatrix = labeledMdp.getTransitionMatrix(); storm::storage::SparseMatrix<T> const& transitionMatrix = labeledMdp.getTransitionMatrix();

3
src/models/AtomicPropositionsLabeling.h

@ -10,6 +10,7 @@
#include "src/storage/BitVector.h" #include "src/storage/BitVector.h"
#include "src/exceptions/OutOfRangeException.h" #include "src/exceptions/OutOfRangeException.h"
#include "src/exceptions/InvalidArgumentException.h"
#include <ostream> #include <ostream>
#include <stdexcept> #include <stdexcept>
#include <set> #include <set>
@ -311,7 +312,7 @@ public:
} }
for (auto it = singleLabelings.begin(); it != singleLabelings.end(); ++it) { for (auto it = singleLabelings.begin(); it != singleLabelings.end(); ++it) {
boost::hash_combine(result, it->getHash());
boost::hash_combine(result, it->hash());
} }
return result; return result;

508
src/storage/BitVector.cpp

@ -0,0 +1,508 @@
#include "src/storage/BitVector.h"
#include "src/exceptions/InvalidArgumentException.h"
#include "src/exceptions/OutOfRangeException.h"
#include "src/utility/OsDetection.h"
#include "src/utility/Hash.h"
#include "log4cplus/logger.h"
#include "log4cplus/loggingmacros.h"
extern log4cplus::Logger logger;
namespace storm {
namespace storage {
BitVector::const_iterator::const_iterator(const BitVector& bitVector, uint_fast64_t startIndex, uint_fast64_t endIndex, bool setOnFirstBit) : bitVector(bitVector), endIndex(endIndex) {
if (setOnFirstBit) {
// Set the index of the first set bit in the vector.
currentIndex = bitVector.getNextSetIndex(startIndex, endIndex);
} else {
currentIndex = startIndex;
}
}
BitVector::const_iterator& BitVector::const_iterator::operator++() {
currentIndex = bitVector.getNextSetIndex(++currentIndex, endIndex);
return *this;
}
uint_fast64_t BitVector::const_iterator::operator*() const {
return currentIndex;
}
bool BitVector::const_iterator::operator!=(const_iterator const& otherIterator) const {
return currentIndex != otherIterator.currentIndex;
}
BitVector::BitVector() : bitCount(0), bucketVector() {
// Intentionally left empty.
}
BitVector::BitVector(uint_fast64_t length, bool initTrue) : bitCount(length) {
// Compute the correct number of buckets needed to store the given number of bits.
uint_fast64_t bucketCount = length >> 6;
if ((length & mod64mask) != 0) {
++bucketCount;
}
// Initialize the storage with the required values.
if (initTrue) {
bucketVector = std::vector<uint64_t>(bucketCount, -1ll);
truncateLastBucket();
} else {
bucketVector = std::vector<uint64_t>(bucketCount, 0);
}
}
template<typename InputIterator>
BitVector::BitVector(uint_fast64_t length, InputIterator begin, InputIterator end) : BitVector(length) {
set(begin, end);
}
BitVector::BitVector(BitVector const& other) : bitCount(other.bitCount), bucketVector(other.bucketVector) {
// Intentionally left empty.
}
BitVector::BitVector(BitVector const& other, BitVector const& filter) : bitCount(filter.getNumberOfSetBits()) {
uint_fast64_t bucketCount = bitCount >> 6;
if ((bitCount & mod64mask) != 0) {
++bucketCount;
}
bucketVector = std::vector<uint64_t>(bucketCount);
// Now copy over all bits given by the filter.
uint_fast64_t nextPosition = 0;
for (auto position : filter) {
set(nextPosition, other.get(position));
nextPosition++;
}
}
BitVector::BitVector(BitVector&& other) : bitCount(other.bitCount), bucketVector(std::move(other.bucketVector)) {
// Intentionally left empty.
}
BitVector& BitVector::operator=(BitVector const& other) {
// Only perform the assignment if the source and target are not identical.
if (this != &other) {
bitCount = other.bitCount;
bucketVector = std::vector<uint64_t>(other.bucketVector);
updateSizeChange();
}
return *this;
}
BitVector& BitVector::operator=(BitVector&& other) {
// Only perform the assignment if the source and target are not identical.
if (this != &other) {
bitCount = other.bitCount;
bucketVector = std::move(other.bucketVector);
updateSizeChange();
}
return *this;
}
bool BitVector::operator==(BitVector const& other) {
// If the lengths of the vectors do not match, they are considered unequal.
if (this->bitCount != other.bitCount) return false;
// If the lengths match, we compare the buckets one by one.
for (std::vector<uint64_t>::const_iterator it1 = bucketVector.begin(), it2 = other.bucketVector.begin(); it1 != bucketVector.end(); ++it1, ++it2) {
if (*it1 != *it2) {
return false;
}
}
// All buckets were equal, so the bit vectors are equal.
return true;
}
void BitVector::set(uint_fast64_t index, bool value) {
uint_fast64_t bucket = index >> 6;
if (index >= bitCount) throw storm::exceptions::OutOfRangeException() << "Invalid call to BitVector::set: written index " << index << " out of bounds.";
uint_fast64_t mask = static_cast<uint_fast64_t>(1) << (index & mod64mask);
if (value) {
bucketVector[bucket] |= mask;
} else {
bucketVector[bucket] &= ~mask;
}
}
template<typename InputIterator>
void BitVector::set(InputIterator begin, InputIterator end) {
for (InputIterator it = begin; it != end; ++it) {
this->set(*it);
}
}
bool BitVector::operator[](uint_fast64_t index) const {
uint_fast64_t bucket = index >> 6;
uint_fast64_t mask = static_cast<uint_fast64_t>(1) << (index & mod64mask);
return (this->bucketVector[bucket] & mask) == mask;
}
bool BitVector::get(uint_fast64_t index) const {
if (index >= bitCount) throw storm::exceptions::OutOfRangeException() << "Invalid call to BitVector::get: read index " << index << " out of bounds.";
return (*this)[index];
}
void BitVector::resize(uint_fast64_t newLength, bool initTrue) {
if (newLength > bitCount) {
uint_fast64_t newBucketCount = newLength >> 6;
if ((newLength & mod64mask) != 0) {
++newBucketCount;
}
if (newBucketCount > bucketVector.size()) {
if (initTrue) {
bucketVector.back() |= (1ll << (bitCount * 64 - bitCount * 63 - bitCount & mod64mask)) - 1ll;
bucketVector.resize(newBucketCount, -1ll);
} else {
bucketVector.resize(newBucketCount, 0);
}
bitCount = newLength;
} else {
// If the underlying storage does not need to grow, we have to insert the missing bits.
if (initTrue) {
bucketVector.back() |= (1ll << (bitCount * 64 - bitCount * 63 - bitCount & mod64mask)) - 1ll;
bitCount = newLength;
} else {
bitCount = newLength;
}
}
updateSizeChange();
} else {
bitCount = newLength;
bitCount = newLength;
uint_fast64_t newBucketCount = newLength >> 6;
if ((newLength & mod64mask) != 0) {
++newBucketCount;
}
bucketVector.resize(newBucketCount);
updateSizeChange();
}
}
BitVector BitVector::operator&(BitVector const& other) const {
uint_fast64_t minSize = std::min(other.bitCount, bitCount);
BitVector result(minSize);
std::vector<uint64_t>::iterator it = result.bucketVector.begin();
for (std::vector<uint64_t>::const_iterator it1 = bucketVector.begin(), it2 = other.bucketVector.begin(); it != result.bucketVector.end(); ++it1, ++it2, ++it) {
*it = *it1 & *it2;
}
return result;
}
BitVector& BitVector::operator&=(BitVector const& other) {
uint_fast64_t minSize = std::min(other.bucketVector.size(), bucketVector.size());
std::vector<uint64_t>::iterator it = bucketVector.begin();
for (std::vector<uint64_t>::const_iterator otherIt = other.bucketVector.begin(); it != bucketVector.end() && otherIt != other.bucketVector.end(); ++it, ++otherIt) {
*it &= *otherIt;
}
return *this;
}
BitVector BitVector::operator|(BitVector const& other) const {
uint_fast64_t minSize = std::min(other.bitCount, bitCount);
BitVector result(minSize);
std::vector<uint64_t>::iterator it = result.bucketVector.begin();
for (std::vector<uint64_t>::const_iterator it1 = bucketVector.begin(), it2 = other.bucketVector.begin(); it != result.bucketVector.end(); ++it1, ++it2, ++it) {
*it = *it1 | *it2;
}
result.truncateLastBucket();
return result;
}
BitVector& BitVector::operator|=(BitVector const& other) {
uint_fast64_t minSize = std::min(other.bucketVector.size(), bucketVector.size());
std::vector<uint64_t>::iterator it = bucketVector.begin();
for (std::vector<uint64_t>::const_iterator otherIt = other.bucketVector.begin(); it != bucketVector.end() && otherIt != other.bucketVector.end(); ++it, ++otherIt) {
*it |= *otherIt;
}
truncateLastBucket();
return *this;
}
BitVector BitVector::operator^(BitVector const& other) const {
uint_fast64_t minSize = std::min(other.bitCount, bitCount);
BitVector result(minSize);
std::vector<uint64_t>::iterator it = result.bucketVector.begin();
for (std::vector<uint64_t>::const_iterator it1 = bucketVector.begin(), it2 = other.bucketVector.begin(); it != result.bucketVector.end(); ++it1, ++it2, ++it) {
*it = *it1 ^ *it2 ;
}
result.truncateLastBucket();
return result;
}
BitVector BitVector::operator~() const {
BitVector result(this->bitCount);
std::vector<uint64_t>::iterator it = result.bucketVector.begin();
for (std::vector<uint64_t>::const_iterator it1 = bucketVector.begin(); it != result.bucketVector.end(); ++it1, ++it) {
*it = ~(*it1);
}
result.truncateLastBucket();
return result;
}
void BitVector::complement() {
for (auto& element : bucketVector) {
element = ~element;
}
truncateLastBucket();
}
BitVector BitVector::implies(BitVector const& other) const {
uint_fast64_t minSize = std::min(other.bitCount, bitCount);
BitVector result(minSize);
std::vector<uint64_t>::iterator it = result.bucketVector.begin();
for (std::vector<uint64_t>::const_iterator it1 = bucketVector.begin(), it2 = other.bucketVector.begin(); it != result.bucketVector.end(); ++it1, ++it2, ++it) {
*it = ~(*it1) | *it2;
}
result.truncateLastBucket();
return result;
}
bool BitVector::isSubsetOf(BitVector const& other) const {
if (bitCount != other.bitCount) {
throw storm::exceptions::InvalidArgumentException() << "Invalid call to BitVector::isSubsetOf: length mismatch of operands.";
}
for (std::vector<uint64_t>::const_iterator it1 = bucketVector.begin(), it2 = other.bucketVector.begin(); it1 != bucketVector.end(); ++it1, ++it2) {
if ((*it1 & *it2) != *it1) {
return false;
}
}
return true;
}
bool BitVector::isDisjointFrom(BitVector const& other) const {
if (bitCount != other.bitCount) {
throw storm::exceptions::InvalidArgumentException() << "Invalid call to BitVector::isDisjointFrom: length mismatch of operands.";
}
for (std::vector<uint64_t>::const_iterator it1 = bucketVector.begin(), it2 = other.bucketVector.begin(); it1 != bucketVector.end(); ++it1, ++it2) {
if ((*it1 & *it2) != 0) {
return false;
}
}
return true;
}
BitVector BitVector::operator%(BitVector const& other) const {
if (bitCount != other.bitCount) {
throw storm::exceptions::InvalidArgumentException() << "Invalid call to BitVector::operator%: length mismatch of operands.";
}
// Create resulting bit vector.
BitVector result(this->getNumberOfSetBits());
// If the current bit vector has not too many elements compared to the given bit vector we prefer iterating
// over its elements.
if (this->getNumberOfSetBits() / 10 < other.getNumberOfSetBits()) {
uint_fast64_t position = 0;
for (auto bit : *this) {
if (other[bit]) {
result.set(position);
}
++position;
}
} else {
// If the given bit vector had much less elements, we iterate over its elements and accept calling the more
// costly operation getNumberOfSetBitsBeforeIndex on the current bit vector.
for (auto bit : other) {
if ((*this)[bit]) {
result.set(this->getNumberOfSetBitsBeforeIndex(bit));
}
}
}
return result;
}
bool BitVector::empty() const {
for (auto& element : bucketVector) {
if (element != 0) {
return false;
}
}
return true;
}
void BitVector::clear() {
for (auto& element : bucketVector) {
element = 0;
}
}
std::vector<uint_fast64_t> BitVector::getSetIndicesList() const {
std::vector<uint_fast64_t> result;
result.reserve(this->getNumberOfSetBits());
for (auto index : *this) {
result.push_back(index);
}
return result;
}
void BitVector::addSetIndicesToVector(std::vector<uint_fast64_t>& vector) const {
for (auto index : *this) {
vector.push_back(index);
}
}
uint_fast64_t BitVector::getNumberOfSetBits() const {
return getNumberOfSetBitsBeforeIndex(bucketVector.size() << 6);
}
uint_fast64_t BitVector::getNumberOfSetBitsBeforeIndex(uint_fast64_t index) const {
uint_fast64_t result = 0;
// First, count all full buckets.
uint_fast64_t bucket = index >> 6;
for (uint_fast64_t i = 0; i < bucket; ++i) {
// Check if we are using g++ or clang++ and, if so, use the built-in function
#if (defined (__GNUG__) || defined(__clang__))
result += __builtin_popcountll(bucketVector[i]);
#elif defined WINDOWS
#include <nmmintrin.h>
// if the target machine does not support SSE4, this will fail.
result += _mm_popcnt_u64(bucketVector[i]);
#else
uint_fast32_t cnt;
uint_fast64_t bitset = bucketVector[i];
for (cnt = 0; bitset; cnt++) {
bitset &= bitset - 1;
}
result += cnt;
#endif
}
// Now check if we have to count part of a bucket.
uint64_t tmp = index & mod64mask;
if (tmp != 0) {
tmp = ((1ll << (tmp & mod64mask)) - 1ll);
tmp &= bucketVector[bucket];
// Check if we are using g++ or clang++ and, if so, use the built-in function
#if (defined (__GNUG__) || defined(__clang__))
result += __builtin_popcountll(tmp);
#else
uint_fast32_t cnt;
uint64_t bitset = tmp;
for (cnt = 0; bitset; cnt++) {
bitset &= bitset - 1;
}
result += cnt;
#endif
}
return result;
}
size_t BitVector::size() const {
return static_cast<size_t>(bitCount);
}
uint_fast64_t BitVector::getSizeInMemory() const {
return sizeof(*this) + sizeof(uint64_t) * bucketVector.size();
}
BitVector::const_iterator BitVector::begin() const {
return const_iterator(*this, 0, bitCount);
}
BitVector::const_iterator BitVector::end() const {
return const_iterator(*this, bitCount, bitCount, false);
}
std::size_t BitVector::hash() const {
std::size_t result = 0;
boost::hash_combine(result, bucketVector.size());
boost::hash_combine(result, bitCount);
for (auto& element : bucketVector) {
boost::hash_combine(result, element);
}
return result;
}
uint_fast64_t BitVector::getNextSetIndex(uint_fast64_t startingIndex) const {
return getNextSetIndex(startingIndex, bitCount);
}
uint_fast64_t BitVector::getNextSetIndex(uint_fast64_t startingIndex, uint_fast64_t endIndex) const {
uint_fast8_t currentBitInByte = startingIndex & mod64mask;
startingIndex >>= 6;
std::vector<uint64_t>::const_iterator bucketIt = bucketVector.begin() + startingIndex;
while ((startingIndex << 6) < endIndex) {
// Compute the remaining bucket content by a right shift
// to the current bit.
uint_fast64_t remainingInBucket = *bucketIt >> currentBitInByte;
// Check if there is at least one bit in the remainder of the bucket
// that is set to true.
if (remainingInBucket != 0) {
// Find that bit.
while ((remainingInBucket & 1) == 0) {
remainingInBucket >>= 1;
++currentBitInByte;
}
// Only return the index of the set bit if we are still in the
// valid range.
if ((startingIndex << 6) + currentBitInByte < endIndex) {
return (startingIndex << 6) + currentBitInByte;
} else {
return endIndex;
}
}
// Advance to the next bucket.
++startingIndex; ++bucketIt; currentBitInByte = 0;
}
return endIndex;
}
void BitVector::truncateLastBucket() {
if ((bitCount & mod64mask) != 0) {
bucketVector.back() &= (1ll << (bitCount & mod64mask)) - 1ll;
}
}
void BitVector::updateSizeChange() {
truncateLastBucket();
}
std::ostream& operator<<(std::ostream& out, BitVector const& bitVector) {
out << "bit vector(" << bitVector.getNumberOfSetBits() << "/" << bitVector.bitCount << ") [";
for (auto index : bitVector) {
out << index << " ";
}
out << "]";
return out;
}
// All necessary explicit template instantiations.
template BitVector::BitVector(uint_fast64_t length, std::vector<uint_fast64_t>::iterator begin, std::vector<uint_fast64_t>::iterator end);
template BitVector::BitVector(uint_fast64_t length, std::vector<uint_fast64_t>::const_iterator begin, std::vector<uint_fast64_t>::const_iterator end);
template void BitVector::set(std::vector<uint_fast64_t>::iterator begin, std::vector<uint_fast64_t>::iterator end);
template void BitVector::set(std::vector<uint_fast64_t>::const_iterator begin, std::vector<uint_fast64_t>::const_iterator end);
}
}

674
src/storage/BitVector.h

@ -1,110 +1,78 @@
#ifndef STORM_STORAGE_BITVECTOR_H_ #ifndef STORM_STORAGE_BITVECTOR_H_
#define STORM_STORAGE_BITVECTOR_H_ #define STORM_STORAGE_BITVECTOR_H_
#include <exception>
#include <new>
#include <cmath> #include <cmath>
#include <cstdint> #include <cstdint>
#include "src/storage/VectorSet.h"
#include "src/exceptions/InvalidStateException.h"
#include "src/exceptions/InvalidArgumentException.h"
#include "src/exceptions/OutOfRangeException.h"
#include "src/utility/OsDetection.h"
#include "src/utility/Hash.h"
#include "log4cplus/logger.h"
#include "log4cplus/loggingmacros.h"
extern log4cplus::Logger logger;
#include <iostream>
#include <ostream>
#include <vector>
namespace storm { namespace storm {
namespace storage {
namespace storage {
/*!
* A bit vector that is internally represented by an array of 64-bit values.
/*!
* A bit vector that is internally represented as a vector of 64-bit values.
*/ */
class BitVector {
public:
class BitVector {
public:
/*! /*!
* A class that enables iterating over the indices of the bit vector whose
* bits are set to true. Note that this is a const iterator, which cannot
* alter the bit vector.
* A class that enables iterating over the indices of the bit vector whose corresponding bits are set to true.
* Note that this is a const iterator, which cannot alter the bit vector.
*/ */
class constIndexIterator {
class const_iterator {
// Declare the BitVector class as a friend class to access its internal storage.
friend class BitVector; friend class BitVector;
public:
public:
/*! /*!
* Constructs a const iterator over the indices of the set bits in the
* given bit vector, starting and stopping, respectively, at the given
* indices.
* @param bitVector The bit vector to iterate over.
* Constructs an iterator over the indices of the set bits in the given bit vector, starting and stopping,
* respectively, at the given indices.
*
* @param bitVector The bit vector over whose bits to iterate.
* @param startIndex The index where to begin looking for set bits. * @param startIndex The index where to begin looking for set bits.
* @param setOnFirstBit If set, upon construction, the current index is
* set to the first set bit in the bit vector.
* @param endIndex The number of elements to iterate over.
* @param setOnFirstBit A flag that indicates whether the iterator is to be moved to the index of the first
* bit upon construction.
* @param endIndex The index at which to abort the iteration process.
*/ */
constIndexIterator(const BitVector& bitVector, uint_fast64_t startIndex, uint_fast64_t endIndex, bool setOnFirstBit = true) : bitVector(bitVector), endIndex(endIndex) {
if (setOnFirstBit) {
currentIndex = bitVector.getNextSetIndex(startIndex, endIndex);
} else {
currentIndex = startIndex;
}
}
const_iterator(BitVector const& bitVector, uint_fast64_t startIndex, uint_fast64_t endIndex, bool setOnFirstBit = true);
/*! /*!
* Increases the position of the iterator to the position of the next bit that
* is set to true.
* Increases the position of the iterator to the position of the next bit that is set to true in the underlying
* bit vector.
*
* @return A reference to this iterator. * @return A reference to this iterator.
*/ */
constIndexIterator& operator++() {
currentIndex = bitVector.getNextSetIndex(++currentIndex, endIndex);
return *this;
}
const_iterator& operator++();
/*! /*!
* Returns the index of the current bit that is set to true.
* @return The index of the current bit that is set to true.
* Returns the index of the current bit to which this iterator points.
*
* @return The index of the current bit to which this iterator points.
*/ */
uint_fast64_t operator*() const {
return currentIndex;
}
uint_fast64_t operator*() const;
/*! /*!
* Compares the iterator with another iterator to determine whether
* the iteration process has reached the end.
* Compares the iterator with another iterator for inequality.
*
* @param otherIterator The iterator with respect to which inequality is checked.
* @return True if the two iterators are unequal.
*/ */
bool operator!=(const constIndexIterator& rhs) const {
return currentIndex != rhs.currentIndex;
}
bool operator!=(const const_iterator& otherIterator) const;
private: private:
// The underlying bit vector of this iterator.
BitVector const& bitVector;
/*! The bit vector to search for set bits. */
const BitVector& bitVector;
/*! The index of the most recently discovered set bit. */
// The index of the bit this iterator currently points to.
uint_fast64_t currentIndex; uint_fast64_t currentIndex;
/*!
* The index of the element past the end. Used for properly terminating
* the search for set bits.
*/
// The index of the bit that is past the end of the range of this iterator.
uint_fast64_t endIndex; uint_fast64_t endIndex;
}; };
/* /*
* Standard constructor. Constructs an empty bit vector of length 0.
* Constructs an empty bit vector of length 0.
*/ */
BitVector() : bucketCount(0), bitCount(0), bucketArray(nullptr), endIterator(*this, 0, 0, false), truncateMask(0) {
// Intentionally left empty.
}
BitVector();
/*! /*!
* Constructs a bit vector which can hold the given number of bits and * Constructs a bit vector which can hold the given number of bits and
@ -112,47 +80,7 @@ public:
* @param length The number of bits the bit vector should be able to hold. * @param length The number of bits the bit vector should be able to hold.
* @param initTrue The initial value of the first |length| bits. * @param initTrue The initial value of the first |length| bits.
*/ */
BitVector(uint_fast64_t length, bool initTrue = false) : bitCount(length), endIterator(*this, length, length, false), truncateMask((1ll << (bitCount & mod64mask)) - 1ll) {
// Check whether the given length is valid.
if (length == 0) {
LOG4CPLUS_ERROR(logger, "Cannot create bit vector of size 0.");
throw storm::exceptions::InvalidArgumentException() << "Cannot create bit vector of size 0.";
}
// Compute the correct number of buckets needed to store the given number of bits
bucketCount = length >> 6;
if ((length & mod64mask) != 0) {
++bucketCount;
}
if (initTrue) {
// Finally, create the full bucket array.
this->bucketArray = new uint64_t[bucketCount];
// Now initialize the values.
for (uint_fast64_t i = 0; i < bucketCount; ++i) {
this->bucketArray[i] = -1ll;
}
truncateLastBucket();
} else {
// Finally, create the full bucket array.
this->bucketArray = new uint64_t[bucketCount]();
}
}
/*!
* Creates a bit vector that has exactly those bits set that are defined by the given set.
*
* @param length The length of the bit vector to create.
* @param bitsToSet A set of indices whose bits to set in the bit vector.
* @param initTrue The initial value of the first |length| bits.
*/
BitVector(uint_fast64_t length, storm::storage::VectorSet<uint_fast64_t> const& bitsToSet, bool initTrue = false) : BitVector(length, initTrue) {
for (auto bit : bitsToSet) {
this->set(bit, true);
}
}
BitVector(uint_fast64_t length, bool initTrue = false);
/*! /*!
* Creates a bit vector that has exactly the bits set that are given by the provided iterator range. * Creates a bit vector that has exactly the bits set that are given by the provided iterator range.
@ -162,78 +90,31 @@ public:
* @param end The end of the iterator range. * @param end The end of the iterator range.
*/ */
template<typename InputIterator> template<typename InputIterator>
BitVector(uint_fast64_t length, InputIterator begin, InputIterator end) : BitVector(length) {
for (InputIterator it = begin; it != end; ++it) {
this->set(*it, true);
}
}
BitVector(uint_fast64_t length, InputIterator begin, InputIterator end);
/*! /*!
* Copy Constructor. Performs a deep copy of the given bit vector. * Copy Constructor. Performs a deep copy of the given bit vector.
* @param bv A reference to the bit vector to be copied. * @param bv A reference to the bit vector to be copied.
*/ */
BitVector(const BitVector & bv) : bucketCount(bv.bucketCount), bitCount(bv.bitCount), endIterator(*this, bitCount, bitCount, false), truncateMask((1ll << (bitCount & mod64mask)) - 1ll) {
LOG4CPLUS_DEBUG(logger, "Invoking copy constructor.");
bucketArray = new uint64_t[bucketCount];
std::copy(bv.bucketArray, bv.bucketArray + this->bucketCount, this->bucketArray);
}
BitVector(BitVector const& other);
/*! /*!
* Copy Constructor. Performs a deep copy of the bits in the given bit vector that are given by the filter. * Copy Constructor. Performs a deep copy of the bits in the given bit vector that are given by the filter.
* @param bv A reference to the bit vector to be copied. * @param bv A reference to the bit vector to be copied.
* @param filter The filter to apply for copying. * @param filter The filter to apply for copying.
*/ */
BitVector(BitVector const& other, BitVector const& filter) : bitCount(filter.getNumberOfSetBits()), endIterator(*this, bitCount, bitCount, false), truncateMask((1ll << (bitCount & mod64mask)) - 1ll) {
// Determine the number of buckets we need for the given bit count and create bucket array.
bucketCount = bitCount >> 6;
if ((bitCount & mod64mask) != 0) {
++bucketCount;
}
this->bucketArray = new uint64_t[bucketCount]();
// Now copy over all bits given by the filter.
uint_fast64_t nextPosition = 0;
for (auto position : filter) {
this->set(nextPosition, other.get(position));
nextPosition++;
}
}
BitVector(BitVector const& other, BitVector const& filter);
/*! /*!
* Move constructor. Move constructs the bit vector from the given bit vector. * Move constructor. Move constructs the bit vector from the given bit vector.
* *
*/ */
BitVector(BitVector&& bv) : bucketCount(bv.bucketCount), bitCount(bv.bitCount), bucketArray(bv.bucketArray), endIterator(*this, bitCount, bitCount, false), truncateMask((1ll << (bitCount & mod64mask)) - 1ll) {
LOG4CPLUS_DEBUG(logger, "Invoking move constructor.");
bv.bucketArray = nullptr;
}
/*!
* Destructor. Frees the underlying bucket array.
*/
~BitVector() {
if (this->bucketArray != nullptr) {
delete[] this->bucketArray;
}
}
BitVector(BitVector&& bv);
/*! /*!
* Compares the given bit vector with the current one. * Compares the given bit vector with the current one.
*/ */
bool operator==(BitVector const& bv) {
// If the lengths of the vectors do not match, they are considered unequal.
if (this->bitCount != bv.bitCount) return false;
// If the lengths match, we compare the buckets one by one.
for (uint_fast64_t index = 0; index < this->bucketCount; ++index) {
if (this->bucketArray[index] != bv.bucketArray[index]) {
return false;
}
}
// All buckets were equal, so the bit vectors are equal.
return true;
}
bool operator==(BitVector const& bv);
/*! /*!
* Assigns the given bit vector to the current bit vector by a deep copy. * Assigns the given bit vector to the current bit vector by a deep copy.
@ -241,20 +122,7 @@ public:
* @return A reference to this bit vector after it has been assigned the * @return A reference to this bit vector after it has been assigned the
* given bit vector by means of a deep copy. * given bit vector by means of a deep copy.
*/ */
BitVector& operator=(BitVector const& bv) {
LOG4CPLUS_DEBUG(logger, "Performing copy assignment.");
// Check if we need to dispose of our current storage.
if (this->bucketArray != nullptr) {
delete[] this->bucketArray;
}
// Copy the values from the other bit vector.
bucketCount = bv.bucketCount;
bitCount = bv.bitCount;
bucketArray = new uint64_t[bucketCount];
std::copy(bv.bucketArray, bv.bucketArray + bucketCount, bucketArray);
updateSizeChange();
return *this;
}
BitVector& operator=(BitVector const& bv);
/*! /*!
* Move assigns the given bit vector to the current bit vector. * Move assigns the given bit vector to the current bit vector.
@ -263,77 +131,14 @@ public:
* @return A reference to this bit vector after the contents of the given bit vector * @return A reference to this bit vector after the contents of the given bit vector
* have been moved into it. * have been moved into it.
*/ */
BitVector& operator=(BitVector&& bv) {
LOG4CPLUS_DEBUG(logger, "Performing move assignment.");
// Only perform the assignment if the source and target are not identical.
if (this != &bv) {
// Check if we need to dispose of our current storage.
if (this->bucketArray != nullptr) {
delete[] this->bucketArray;
}
// Copy the values from the other bit vector, but directly steal its storage.
bucketCount = bv.bucketCount;
bitCount = bv.bitCount;
bucketArray = bv.bucketArray;
updateSizeChange();
// Now alter the other bit vector such that it does not dispose of our stolen storage.
bv.bucketArray = nullptr;
}
return *this;
}
/*!
* Resizes the bit vector to hold the given new number of bits.
* @param newLength The new number of bits the bit vector can hold.
*/
void resize(uint_fast64_t newLength) {
bitCount = newLength;
uint_fast64_t newBucketCount = newLength >> 6;
if ((newLength & mod64mask) != 0) {
++newBucketCount;
}
// Reserve a temporary array for copying.
uint64_t* tempArray = new uint64_t[newBucketCount];
// Copy over the elements from the old bit vector.
uint_fast64_t copySize = (newBucketCount <= bucketCount) ? newBucketCount : bucketCount;
std::copy(this->bucketArray, this->bucketArray + copySize, tempArray);
// Initialize missing values in the new bit vector.
for (uint_fast64_t i = copySize; i < newBucketCount; ++i) {
tempArray[i] = 0;
}
updateSizeChange();
// Dispose of the old bit vector and set the new one.
delete[] this->bucketArray;
this->bucketArray = tempArray;
this->bucketCount = newBucketCount;
}
BitVector& operator=(BitVector&& bv);
/*! /*!
* Sets the given truth value at the given index. * Sets the given truth value at the given index.
* @param index The index where to set the truth value. * @param index The index where to set the truth value.
* @param value The truth value to set. * @param value The truth value to set.
*/ */
void set(const uint_fast64_t index, bool value = true) {
uint_fast64_t bucket = index >> 6;
if (bucket >= this->bucketCount) throw storm::exceptions::OutOfRangeException() << "Written index " << index << " out of bounds.";
uint_fast64_t mask = static_cast<uint_fast64_t>(1) << (index & mod64mask);
if (value) {
this->bucketArray[bucket] |= mask;
} else {
this->bucketArray[bucket] &= ~mask;
}
if (bucket == this->bucketCount - 1) {
truncateLastBucket();
}
}
void set(const uint_fast64_t index, bool value = true);
/*! /*!
* Sets all bits in the given iterator range. * Sets all bits in the given iterator range.
@ -342,22 +147,33 @@ public:
* @param end The element past the last element of the iterator range. * @param end The element past the last element of the iterator range.
*/ */
template<typename InputIterator> template<typename InputIterator>
void set(InputIterator begin, InputIterator end) {
for (InputIterator it = begin; it != end; ++it) {
this->set(*it);
}
}
void set(InputIterator begin, InputIterator end);
/*!
* Retrieves the truth value of the bit at the given index. Note: this does not check whether the
* given index is within bounds.
*
* @param index The index of the bit to access.
* @return True if the bit at the given index is set.
*/
bool operator[](uint_fast64_t index) const;
/*! /*!
* Retrieves the truth value at the given index.
* @param index The index from which to retrieve the truth value.
* Retrieves the truth value of the bit at the given index and performs a bound check.
*
* @param index The index of the bit to access.
* @return True if the bit at the given index is set.
*/
bool get(const uint_fast64_t index) const;
/*!
* Resizes the bit vector to hold the given new number of bits. If the bit vector becomes smaller this way, the
* bits are truncated. Otherwise, the new bits are initialized to the given value.
*
* @param newLength The new number of bits the bit vector can hold.
* @param initTrue Sets whether newly created bits are initialized to true instead of false.
*/ */
bool get(const uint_fast64_t index) const {
uint_fast64_t bucket = index >> 6;
if (bucket >= this->bucketCount) throw storm::exceptions::OutOfRangeException() << "Read index " << index << " out of bounds.";
uint_fast64_t mask = static_cast<uint_fast64_t>(1) << (index & mod64mask);
return ((this->bucketArray[bucket] & mask) == mask);
}
void resize(uint_fast64_t newLength, bool initTrue = false);
/*! /*!
* Performs a logical "and" with the given bit vector. In case the sizes of the bit vectors * Performs a logical "and" with the given bit vector. In case the sizes of the bit vectors
@ -366,17 +182,7 @@ public:
* @param bv A reference to the bit vector to use for the operation. * @param bv A reference to the bit vector to use for the operation.
* @return A bit vector corresponding to the logical "and" of the two bit vectors. * @return A bit vector corresponding to the logical "and" of the two bit vectors.
*/ */
BitVector operator&(BitVector const& bv) const {
uint_fast64_t minSize = (bv.bitCount < this->bitCount) ? bv.bitCount : this->bitCount;
// Create resulting bit vector and perform the operation on the individual elements.
BitVector result(minSize);
for (uint_fast64_t i = 0; i < result.bucketCount; ++i) {
result.bucketArray[i] = this->bucketArray[i] & bv.bucketArray[i];
}
return result;
}
BitVector operator&(BitVector const& bv) const;
/*! /*!
* Performs a logical "and" with the given bit vector and assigns the result to the * Performs a logical "and" with the given bit vector and assigns the result to the
@ -386,15 +192,7 @@ public:
* @return A reference to the current bit vector corresponding to the logical "and" * @return A reference to the current bit vector corresponding to the logical "and"
* of the two bit vectors. * of the two bit vectors.
*/ */
BitVector& operator&=(BitVector const& bv) {
uint_fast64_t minSize = (bv.bucketCount < this->bucketCount) ? bv.bucketCount : this->bucketCount;
for (uint_fast64_t i = 0; i < minSize; ++i) {
this->bucketArray[i] &= bv.bucketArray[i];
}
return *this;
}
BitVector& operator&=(BitVector const& bv);
/*! /*!
* Performs a logical "or" with the given bit vector. In case the sizes of the bit vectors * Performs a logical "or" with the given bit vector. In case the sizes of the bit vectors
@ -403,18 +201,7 @@ public:
* @param bv A reference to the bit vector to use for the operation. * @param bv A reference to the bit vector to use for the operation.
* @return A bit vector corresponding to the logical "or" of the two bit vectors. * @return A bit vector corresponding to the logical "or" of the two bit vectors.
*/ */
BitVector operator|(BitVector const& bv) const {
uint_fast64_t minSize = (bv.bitCount < this->bitCount) ? bv.bitCount : this->bitCount;
// Create resulting bit vector and perform the operation on the individual elements.
BitVector result(minSize);
for (uint_fast64_t i = 0; i < result.bucketCount; ++i) {
result.bucketArray[i] = this->bucketArray[i] | bv.bucketArray[i];
}
result.truncateLastBucket();
return result;
}
BitVector operator|(BitVector const& bv) const;
/*! /*!
* Performs a logical "or" with the given bit vector and assigns the result to the * Performs a logical "or" with the given bit vector and assigns the result to the
@ -424,16 +211,7 @@ public:
* @return A reference to the current bit vector corresponding to the logical "or" * @return A reference to the current bit vector corresponding to the logical "or"
* of the two bit vectors. * of the two bit vectors.
*/ */
BitVector& operator|=(BitVector const& bv) {
uint_fast64_t minSize = (bv.bucketCount < this->bucketCount) ? bv.bucketCount : this->bucketCount;
for (uint_fast64_t i = 0; i < minSize; ++i) {
this->bucketArray[i] |= bv.bucketArray[i];
}
truncateLastBucket();
return *this;
}
BitVector& operator|=(BitVector const& bv);
/*! /*!
* Performs a logical "xor" with the given bit vector. In case the sizes of the bit vectors * Performs a logical "xor" with the given bit vector. In case the sizes of the bit vectors
@ -442,43 +220,18 @@ public:
* @param bv A reference to the bit vector to use for the operation. * @param bv A reference to the bit vector to use for the operation.
* @return A bit vector corresponding to the logical "xor" of the two bit vectors. * @return A bit vector corresponding to the logical "xor" of the two bit vectors.
*/ */
BitVector operator^(BitVector const& bv) const {
uint_fast64_t minSize = (bv.bitCount < this->bitCount) ? bv.bitCount : this->bitCount;
// Create resulting bit vector and perform the operation on the individual elements.
BitVector result(minSize);
for (uint_fast64_t i = 0; i < result.bucketCount; ++i) {
result.bucketArray[i] = this->bucketArray[i] ^ bv.bucketArray[i];
}
result.truncateLastBucket();
return result;
}
BitVector operator^(BitVector const& bv) const;
/*! /*!
* Performs a logical "not" on the bit vector. * Performs a logical "not" on the bit vector.
* @return A bit vector corresponding to the logical "not" of the bit vector. * @return A bit vector corresponding to the logical "not" of the bit vector.
*/ */
BitVector operator~() const {
// Create resulting bit vector and perform the operation on the individual elements.
BitVector result(this->bitCount);
for (uint_fast64_t i = 0; i < this->bucketCount; ++i) {
result.bucketArray[i] = ~this->bucketArray[i];
}
result.truncateLastBucket();
return result;
}
BitVector operator~() const;
/*! /*!
* Negates all bits in the bit vector. * Negates all bits in the bit vector.
*/ */
void complement() {
for (uint_fast64_t i = 0; i < this->bucketCount; ++i) {
this->bucketArray[i] = ~this->bucketArray[i];
}
truncateLastBucket();
}
void complement();
/*! /*!
* Performs a logical "implies" with the given bit vector. In case the sizes of the bit vectors * Performs a logical "implies" with the given bit vector. In case the sizes of the bit vectors
@ -488,18 +241,7 @@ public:
* @param bv A reference to the bit vector to use for the operation. * @param bv A reference to the bit vector to use for the operation.
* @return A bit vector corresponding to the logical "implies" of the two bit vectors. * @return A bit vector corresponding to the logical "implies" of the two bit vectors.
*/ */
BitVector implies(BitVector const& bv) const {
uint_fast64_t minSize = (bv.bitCount < this->bitCount) ? bv.bitCount : this->bitCount;
// Create resulting bit vector and perform the operation on the individual elements.
BitVector result(minSize);
for (uint_fast64_t i = 0; i < result.bucketCount - 1; ++i) {
result.bucketArray[i] = ~this->bucketArray[i] | bv.bucketArray[i];
}
result.truncateLastBucket();
return result;
}
BitVector implies(BitVector const& bv) const;
/*! /*!
* Checks whether all bits that are set in the current bit vector are also set in the given bit * Checks whether all bits that are set in the current bit vector are also set in the given bit
@ -510,14 +252,7 @@ public:
* @return True iff all bits that are set in the current bit vector are also set in the given bit * @return True iff all bits that are set in the current bit vector are also set in the given bit
* vector. * vector.
*/ */
bool isSubsetOf(BitVector const& bv) const {
for (uint_fast64_t i = 0; i < this->bucketCount; ++i) {
if ((this->bucketArray[i] & bv.bucketArray[i]) != this->bucketArray[i]) {
return false;
}
}
return true;
}
bool isSubsetOf(BitVector const& bv) const;
/*! /*!
* Checks whether none of the bits that are set in the current bit vector are also set in the * Checks whether none of the bits that are set in the current bit vector are also set in the
@ -528,14 +263,7 @@ public:
* @returns True iff none of the bits that are set in the current bit vector are also set in the * @returns True iff none of the bits that are set in the current bit vector are also set in the
* given bit vector. * given bit vector.
*/ */
bool isDisjointFrom(BitVector const& bv) const {
for (uint_fast64_t i = 0; i < this->bucketCount; ++i) {
if ((this->bucketArray[i] & bv.bucketArray[i]) != 0) {
return false;
}
}
return true;
}
bool isDisjointFrom(BitVector const& bv) const;
/*! /*!
* Computes a bit vector such that bit i is set iff the i-th set bit of the current bit vector is also contained * Computes a bit vector such that bit i is set iff the i-th set bit of the current bit vector is also contained
@ -545,55 +273,19 @@ public:
* @return A bit vector whose i-th bit is set iff the i-th set bit of the current bit vector is also contained * @return A bit vector whose i-th bit is set iff the i-th set bit of the current bit vector is also contained
* in the given bit vector. * in the given bit vector.
*/ */
BitVector operator%(BitVector const& bv) const {
// Create resulting bit vector.
BitVector result(this->getNumberOfSetBits());
// If the current bit vector has not too many elements compared to the given bit vector we prefer iterating
// over its elements.
if (this->getNumberOfSetBits() / 10 < bv.getNumberOfSetBits()) {
uint_fast64_t position = 0;
for (auto bit : *this) {
if (bv.get(bit)) {
result.set(position, true);
}
++position;
}
} else {
// If the given bit vector had much less elements, we iterate over its elements and accept calling the more
// costly operation getNumberOfSetBitsBeforeIndex on the current bit vector.
for (auto bit : bv) {
if (this->get(bit)) {
result.set(this->getNumberOfSetBitsBeforeIndex(bit), true);
}
}
}
return result;
}
BitVector operator%(BitVector const& bv) const;
/*! /*!
* Retrieves whether there is at least one bit set in the vector. * Retrieves whether there is at least one bit set in the vector.
* *
* @return True if there is at least one bit set in this vector. * @return True if there is at least one bit set in this vector.
*/ */
bool empty() const {
for (uint_fast64_t i = 0; i < this->bucketCount; ++i) {
if (this->bucketArray[i] != 0) {
return false;
}
}
return true;
}
bool empty() const;
/*! /*!
* Removes all set bits from the bit vector. * Removes all set bits from the bit vector.
*/ */
void clear() {
for (uint_fast64_t i = 0; i < this->bucketCount; ++i) {
this->bucketArray[i] = 0;
}
}
void clear();
/*! /*!
* Returns a list containing all indices such that the bits at these indices are set to true * Returns a list containing all indices such that the bits at these indices are set to true
@ -601,138 +293,75 @@ public:
* *
* @return A vector of indices of set bits in the bit vector. * @return A vector of indices of set bits in the bit vector.
*/ */
std::vector<uint_fast64_t> getSetIndicesList() const {
std::vector<uint_fast64_t> result;
result.reserve(this->getNumberOfSetBits());
for (auto index : *this) {
result.push_back(index);
}
return result;
}
std::vector<uint_fast64_t> getSetIndicesList() const;
/*! /*!
* Adds all indices of bits set to one to the given list. * Adds all indices of bits set to one to the given list.
* *
* @param list The list to which to append the indices. * @param list The list to which to append the indices.
*/ */
void addSetIndicesToVector(std::vector<uint_fast64_t>& vector) const {
for (auto index : *this) {
vector.push_back(index);
}
}
void addSetIndicesToVector(std::vector<uint_fast64_t>& vector) const;
/*! /*!
* Returns the number of bits that are set (to one) in this bit vector. * Returns the number of bits that are set (to one) in this bit vector.
*
* @return The number of bits that are set (to one) in this bit vector. * @return The number of bits that are set (to one) in this bit vector.
*/ */
uint_fast64_t getNumberOfSetBits() const {
return getNumberOfSetBitsBeforeIndex(bucketCount << 6);
}
uint_fast64_t getNumberOfSetBitsBeforeIndex(uint_fast64_t index) const {
uint_fast64_t result = 0;
// First, count all full buckets.
uint_fast64_t bucket = index >> 6;
for (uint_fast64_t i = 0; i < bucket; ++i) {
// Check if we are using g++ or clang++ and, if so, use the built-in function
#if (defined (__GNUG__) || defined(__clang__))
result += __builtin_popcountll(this->bucketArray[i]);
#elif defined WINDOWS
#include <nmmintrin.h>
// if the target machine does not support SSE4, this will fail.
result += _mm_popcnt_u64(this->bucketArray[i]);
#else
uint_fast32_t cnt;
uint_fast64_t bitset = this->bucketArray[i];
for (cnt = 0; bitset; cnt++) {
bitset &= bitset - 1;
}
result += cnt;
#endif
}
// Now check if we have to count part of a bucket.
uint64_t tmp = index & mod64mask;
if (tmp != 0) {
tmp = ((1ll << (tmp & mod64mask)) - 1ll);
tmp &= bucketArray[bucket];
// Check if we are using g++ or clang++ and, if so, use the built-in function
#if (defined (__GNUG__) || defined(__clang__))
result += __builtin_popcountll(tmp);
#else
uint_fast32_t cnt;
uint64_t bitset = tmp;
for (cnt = 0; bitset; cnt++) {
bitset &= bitset - 1;
}
result += cnt;
#endif
}
return result;
}
uint_fast64_t getNumberOfSetBits() const;
/*!
* Retrieves the number of bits set in this bit vector with an index strictly smaller than the given one.
*
* @param index The index for which to retrieve the number of set bits with a smaller index.
* @return The number of bits set in this bit vector with an index strictly smaller than the given one.
*/
uint_fast64_t getNumberOfSetBitsBeforeIndex(uint_fast64_t index) const;
/*! /*!
* Retrieves the number of bits this bit vector can store. * Retrieves the number of bits this bit vector can store.
*
* @return The number of bits this bit vector can hold.
*/ */
size_t size() const {
return static_cast<size_t>(bitCount);
}
size_t size() const;
/*! /*!
* Returns the size of the bit vector in memory measured in bytes. * Returns the size of the bit vector in memory measured in bytes.
*
* @return The size of the bit vector in memory measured in bytes. * @return The size of the bit vector in memory measured in bytes.
*/ */
uint_fast64_t getSizeInMemory() const {
return sizeof(*this) + sizeof(uint_fast64_t) * bucketCount;
}
uint_fast64_t getSizeInMemory() const;
/*! /*!
* Returns an iterator to the indices of the set bits in the bit vector. * Returns an iterator to the indices of the set bits in the bit vector.
*/ */
constIndexIterator begin() const {
return constIndexIterator(*this, 0, bitCount);
}
const_iterator begin() const;
/*! /*!
* Returns an iterator pointing at the element past the bit vector. * Returns an iterator pointing at the element past the bit vector.
*/ */
const constIndexIterator& end() const {
return endIterator;
}
/*!
* Returns a string representation of the bit vector.
*/
std::string toString() const {
std::stringstream result;
result << "bit vector(" << this->getNumberOfSetBits() << "/" << bitCount << ") [";
for (auto index : *this) {
result << index << " ";
}
result << "]";
return result.str();
}
const_iterator end() const;
/*! /*!
* Calculates a hash over all values contained in this Sparse Matrix. * Calculates a hash over all values contained in this Sparse Matrix.
* @return size_t A Hash Value * @return size_t A Hash Value
*/ */
std::size_t getHash() const {
std::size_t result = 0;
boost::hash_combine(result, bucketCount);
boost::hash_combine(result, bitCount);
std::size_t hash() const;
for (uint_fast64_t i = 0; i < bucketCount; ++i) {
boost::hash_combine(result, bucketArray[i]);
}
/*
* Retrieves the index of the bit that is the next bit set to true in the bit vector. If there
* is none, this function returns the number of bits this vector holds in total.
* Put differently, if the return value is equal to a call to size(), then there is
* no bit set after the specified position.
*
* @param startingIndex The index at which to start the search for the next bit that is set. The
* bit at this index itself is not considered.
* @return The index of the next bit that is set after the given index.
*/
uint_fast64_t getNextSetIndex(uint_fast64_t startingIndex) const;
return result;
}
friend std::ostream& operator<<(std::ostream& out, BitVector const& bitVector);
private:
private:
/*! /*!
* Retrieves the index of the bit that is set after the given starting index, * Retrieves the index of the bit that is set after the given starting index,
@ -744,77 +373,30 @@ private:
* but before the given end index in the given bit vector or endIndex in case * but before the given end index in the given bit vector or endIndex in case
* the end index was reached. * the end index was reached.
*/ */
uint_fast64_t getNextSetIndex(uint_fast64_t startingIndex, uint_fast64_t endIndex) const {
uint_fast8_t currentBitInByte = startingIndex & mod64mask;
startingIndex >>= 6;
uint64_t* bucketPtr = this->bucketArray + startingIndex;
while ((startingIndex << 6) < endIndex) {
// Compute the remaining bucket content by a right shift
// to the current bit.
uint_fast64_t remainingInBucket = *bucketPtr >> currentBitInByte;
// Check if there is at least one bit in the remainder of the bucket
// that is set to true.
if (remainingInBucket != 0) {
// Find that bit.
while ((remainingInBucket & 1) == 0) {
remainingInBucket >>= 1;
++currentBitInByte;
}
// Only return the index of the set bit if we are still in the
// valid range.
if ((startingIndex << 6) + currentBitInByte < endIndex) {
return (startingIndex << 6) + currentBitInByte;
} else {
return endIndex;
}
}
// Advance to the next bucket.
++startingIndex; ++bucketPtr; currentBitInByte = 0;
}
return endIndex;
}
/*!
* Truncate the last bucket so that no bits are set in the range starting
* from (bitCount + 1).
*/
void truncateLastBucket() {
if ((bitCount & mod64mask) != 0) {
bucketArray[bucketCount - 1] = bucketArray[bucketCount - 1] & truncateMask;
}
}
uint_fast64_t getNextSetIndex(uint_fast64_t startingIndex, uint_fast64_t endIndex) const;
/*!
* Truncate the last bucket so that no bits are set in the range starting from (bitCount + 1).
*/
void truncateLastBucket();
/*! /*!
* Updates internal structures in case the size of the bit vector changed. Needs to be called * Updates internal structures in case the size of the bit vector changed. Needs to be called
* after the size of the bit vector changed. * after the size of the bit vector changed.
*/ */
void updateSizeChange() {
endIterator.currentIndex = bitCount;
truncateMask = (1ll << (bitCount & mod64mask)) - 1ll;
}
/*! The number of 64-bit buckets we use as internal storage. */
uint_fast64_t bucketCount;
void updateSizeChange();
/*! The number of bits that have to be stored */ /*! The number of bits that have to be stored */
uint_fast64_t bitCount; uint_fast64_t bitCount;
/*! Array of 64-bit buckets to store the bits. */ /*! Array of 64-bit buckets to store the bits. */
uint64_t* bucketArray;
/*! An iterator marking the end of the bit vector. */
constIndexIterator endIterator;
std::vector<uint64_t> bucketVector;
/*! A bit mask that can be used to reduce a modulo operation to a logical "and". */ /*! A bit mask that can be used to reduce a modulo operation to a logical "and". */
static const uint_fast64_t mod64mask = (1 << 6) - 1; static const uint_fast64_t mod64mask = (1 << 6) - 1;
};
uint64_t truncateMask;
};
} // namespace storage
} // namespace storage
} // namespace storm } // namespace storm
#endif // STORM_STORAGE_BITVECTOR_H_ #endif // STORM_STORAGE_BITVECTOR_H_

2
src/storage/StronglyConnectedComponentDecomposition.cpp

@ -15,7 +15,7 @@ namespace storm {
template <typename ValueType> template <typename ValueType>
StronglyConnectedComponentDecomposition<ValueType>::StronglyConnectedComponentDecomposition(storm::models::AbstractModel<ValueType> const& model, StateBlock const& block, bool dropNaiveSccs, bool onlyBottomSccs) { StronglyConnectedComponentDecomposition<ValueType>::StronglyConnectedComponentDecomposition(storm::models::AbstractModel<ValueType> const& model, StateBlock const& block, bool dropNaiveSccs, bool onlyBottomSccs) {
storm::storage::BitVector subsystem(model.getNumberOfStates(), block);
storm::storage::BitVector subsystem(model.getNumberOfStates(), block.begin(), block.end());
performSccDecomposition(model, subsystem, dropNaiveSccs, onlyBottomSccs); performSccDecomposition(model, subsystem, dropNaiveSccs, onlyBottomSccs);
} }

Loading…
Cancel
Save