9 changed files with 0 additions and 1984 deletions
-
31src/storm/modelchecker/multiobjective/rewardbounded/Dimension.h
-
301src/storm/modelchecker/multiobjective/rewardbounded/EpochManager.cpp
-
62src/storm/modelchecker/multiobjective/rewardbounded/EpochManager.h
-
89src/storm/modelchecker/multiobjective/rewardbounded/MemoryStateManager.cpp
-
45src/storm/modelchecker/multiobjective/rewardbounded/MemoryStateManager.h
-
639src/storm/modelchecker/multiobjective/rewardbounded/MultiDimensionalRewardUnfolding.cpp
-
127src/storm/modelchecker/multiobjective/rewardbounded/MultiDimensionalRewardUnfolding.h
-
608src/storm/modelchecker/multiobjective/rewardbounded/ProductModel.cpp
-
82src/storm/modelchecker/multiobjective/rewardbounded/ProductModel.h
@ -1,31 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include <boost/optional.hpp> |
|
||||
|
|
||||
#include "storm/storage/BitVector.h" |
|
||||
#include "storm/storage/SparseMatrix.h" |
|
||||
#include "storm/modelchecker/multiobjective/Objective.h" |
|
||||
#include "storm/modelchecker/multiobjective/rewardbounded/EpochManager.h" |
|
||||
#include "storm/modelchecker/multiobjective/rewardbounded/ProductModel.h" |
|
||||
#include "storm/models/sparse/Mdp.h" |
|
||||
#include "storm/utility/vector.h" |
|
||||
#include "storm/storage/memorystructure/MemoryStructure.h" |
|
||||
#include "storm/utility/Stopwatch.h" |
|
||||
|
|
||||
namespace storm { |
|
||||
namespace modelchecker { |
|
||||
namespace multiobjective { |
|
||||
|
|
||||
template<typename ValueType> |
|
||||
struct Dimension { |
|
||||
std::shared_ptr<storm::logic::Formula const> formula; |
|
||||
uint64_t objectiveIndex; |
|
||||
boost::optional<std::string> memoryLabel; |
|
||||
bool isUpperBounded; |
|
||||
ValueType scalingFactor; |
|
||||
storm::storage::BitVector dependentDimensions; |
|
||||
boost::optional<uint64_t> maxValue; |
|
||||
}; |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,301 +0,0 @@ |
|||||
#include "storm/modelchecker/multiobjective/rewardbounded/EpochManager.h"
|
|
||||
|
|
||||
#include "storm/utility/macros.h"
|
|
||||
|
|
||||
#include "storm/exceptions/IllegalArgumentException.h"
|
|
||||
|
|
||||
namespace storm { |
|
||||
namespace modelchecker { |
|
||||
namespace multiobjective { |
|
||||
|
|
||||
EpochManager::EpochManager() : dimensionCount(0) { |
|
||||
// Intentionally left empty
|
|
||||
} |
|
||||
|
|
||||
EpochManager::EpochManager(uint64_t dimensionCount) : dimensionCount(dimensionCount) { |
|
||||
STORM_LOG_THROW(dimensionCount > 0, storm::exceptions::IllegalArgumentException, "Invoked EpochManager with zero dimension count."); |
|
||||
STORM_LOG_THROW(dimensionCount <= 64, storm::exceptions::IllegalArgumentException, "Invoked EpochManager with too many dimensions."); |
|
||||
bitsPerDimension = 64 / dimensionCount; |
|
||||
if (dimensionCount == 1) { |
|
||||
dimensionBitMask = -1ull; |
|
||||
} else { |
|
||||
dimensionBitMask = (1ull << bitsPerDimension) - 1; |
|
||||
} |
|
||||
|
|
||||
if (dimensionCount * bitsPerDimension == 64ull) { |
|
||||
relevantBitsMask = -1ull; |
|
||||
} else { |
|
||||
relevantBitsMask = (1ull << (dimensionCount * bitsPerDimension)) - 1; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
typename EpochManager::Epoch EpochManager::getBottomEpoch() const { |
|
||||
return relevantBitsMask; |
|
||||
} |
|
||||
|
|
||||
typename EpochManager::Epoch EpochManager::getZeroEpoch() const { |
|
||||
return 0; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
uint64_t const& EpochManager::getDimensionCount() const { |
|
||||
STORM_LOG_ASSERT(dimensionCount > 0, "Invoked EpochManager with zero dimension count."); |
|
||||
return dimensionCount; |
|
||||
} |
|
||||
|
|
||||
bool EpochManager::compareEpochClass(Epoch const& epoch1, Epoch const& epoch2) const { |
|
||||
STORM_LOG_ASSERT(dimensionCount > 0, "Invoked EpochManager with zero dimension count."); |
|
||||
uint64_t mask = dimensionBitMask; |
|
||||
for (uint64_t d = 0; d < dimensionCount; ++d) { |
|
||||
if (((epoch1 & mask) == mask) != ((epoch2 & mask) == mask)) { |
|
||||
assert(getEpochClass(epoch1) != getEpochClass(epoch2)); |
|
||||
return false; |
|
||||
} |
|
||||
mask = mask << bitsPerDimension; |
|
||||
} |
|
||||
assert(getEpochClass(epoch1) == getEpochClass(epoch2)); |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
typename EpochManager::EpochClass EpochManager::getEpochClass(Epoch const& epoch) const { |
|
||||
STORM_LOG_ASSERT(dimensionCount > 0, "Invoked EpochManager with zero dimension count."); |
|
||||
EpochClass result = 0; |
|
||||
uint64_t dimensionMask = dimensionBitMask; |
|
||||
uint64_t classMask = 1; |
|
||||
for (uint64_t d = 0; d < dimensionCount; ++d) { |
|
||||
if ((epoch & dimensionMask) == dimensionMask) { |
|
||||
result |= classMask; |
|
||||
} |
|
||||
classMask = classMask << 1; |
|
||||
dimensionMask = dimensionMask << bitsPerDimension; |
|
||||
} |
|
||||
return result; |
|
||||
} |
|
||||
|
|
||||
typename EpochManager::Epoch EpochManager::getSuccessorEpoch(Epoch const& epoch, Epoch const& step) const { |
|
||||
STORM_LOG_ASSERT(dimensionCount > 0, "Invoked EpochManager with zero dimension count."); |
|
||||
STORM_LOG_ASSERT(!hasBottomDimension(step), "The given step has at least one bottom dimension."); |
|
||||
|
|
||||
// Start with dimension zero
|
|
||||
uint64_t mask = dimensionBitMask; |
|
||||
uint64_t e_d = epoch & mask; |
|
||||
uint64_t s_d = step & mask; |
|
||||
uint64_t result = (e_d < s_d || e_d == mask) ? mask : e_d - s_d; |
|
||||
|
|
||||
// Consider the remaining dimensions
|
|
||||
for (uint64_t d = 1; d < dimensionCount; ++d) { |
|
||||
mask = mask << bitsPerDimension; |
|
||||
e_d = epoch & mask; |
|
||||
s_d = step & mask; |
|
||||
result |= ((e_d < s_d || e_d == mask) ? mask : e_d - s_d); |
|
||||
} |
|
||||
return result; |
|
||||
} |
|
||||
|
|
||||
std::vector<typename EpochManager::Epoch> EpochManager::getPredecessorEpochs(Epoch const& epoch, Epoch const& step) const { |
|
||||
STORM_LOG_ASSERT(dimensionCount > 0, "Invoked EpochManager with zero dimension count."); |
|
||||
STORM_LOG_ASSERT(!hasBottomDimension(step), "The given step has at least one bottom dimension."); |
|
||||
std::set<Epoch> resultAsSet; |
|
||||
gatherPredecessorEpochs(resultAsSet, epoch, step); |
|
||||
return std::vector<Epoch>(resultAsSet.begin(), resultAsSet.end()); |
|
||||
} |
|
||||
|
|
||||
void EpochManager::gatherPredecessorEpochs(std::set<Epoch>& gatheredPredecessorEpochs, Epoch const& epoch, Epoch const& step) const { |
|
||||
STORM_LOG_ASSERT(dimensionCount > 0, "Invoked EpochManager with zero dimension count."); |
|
||||
STORM_LOG_ASSERT(!hasBottomDimension(step), "The given step has at least one bottom dimension."); |
|
||||
Epoch currStep = step; |
|
||||
uint64_t d = 0; |
|
||||
while (d < dimensionCount) { |
|
||||
Epoch predecessor = epoch; |
|
||||
for (uint64_t dPrime = 0; dPrime < dimensionCount; ++dPrime) { |
|
||||
uint64_t step_dPrime = getDimensionOfEpoch(currStep, dPrime); |
|
||||
if (isBottomDimension(predecessor, dPrime)) { |
|
||||
if (step_dPrime != 0) { |
|
||||
setDimensionOfEpoch(predecessor, dPrime, step_dPrime - 1); |
|
||||
} |
|
||||
} else { |
|
||||
setDimensionOfEpoch(predecessor, dPrime, getDimensionOfEpoch(predecessor, dPrime) + step_dPrime); |
|
||||
} |
|
||||
} |
|
||||
assert(epoch == getSuccessorEpoch(predecessor, step)); |
|
||||
gatheredPredecessorEpochs.insert(predecessor); |
|
||||
|
|
||||
do { |
|
||||
if (isBottomDimension(epoch, d)) { |
|
||||
uint64_t step_d = getDimensionOfEpoch(currStep, d); |
|
||||
if (step_d == 0) { |
|
||||
setDimensionOfEpoch(currStep, d, getDimensionOfEpoch(step, d)); |
|
||||
} else { |
|
||||
setDimensionOfEpoch(currStep, d, step_d - 1); |
|
||||
d = 0; |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
++d; |
|
||||
} while(d < dimensionCount); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
bool EpochManager::isValidDimensionValue(uint64_t const& value) const { |
|
||||
STORM_LOG_ASSERT(dimensionCount > 0, "Invoked EpochManager with zero dimension count."); |
|
||||
return ((value & dimensionBitMask) == value) && value != dimensionBitMask; |
|
||||
} |
|
||||
|
|
||||
bool EpochManager::isZeroEpoch(Epoch const& epoch) const { |
|
||||
STORM_LOG_ASSERT(dimensionCount > 0, "Invoked EpochManager with zero dimension count."); |
|
||||
return (epoch & relevantBitsMask) == 0; |
|
||||
} |
|
||||
|
|
||||
bool EpochManager::isBottomEpoch(Epoch const& epoch) const { |
|
||||
STORM_LOG_ASSERT(dimensionCount > 0, "Invoked EpochManager with zero dimension count."); |
|
||||
return (epoch & relevantBitsMask) == relevantBitsMask; |
|
||||
} |
|
||||
|
|
||||
bool EpochManager::hasBottomDimension(Epoch const& epoch) const { |
|
||||
STORM_LOG_ASSERT(dimensionCount > 0, "Invoked EpochManager with zero dimension count."); |
|
||||
uint64_t mask = dimensionBitMask; |
|
||||
for (uint64_t d = 0; d < dimensionCount; ++d) { |
|
||||
if ((epoch | mask) == epoch) { |
|
||||
return true; |
|
||||
} |
|
||||
mask = mask << bitsPerDimension; |
|
||||
} |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
bool EpochManager::hasBottomDimensionEpochClass(EpochClass const& epochClass) const { |
|
||||
STORM_LOG_ASSERT(dimensionCount > 0, "Invoked EpochManager with zero dimension count."); |
|
||||
uint64_t mask = (1 << dimensionCount) - 1; |
|
||||
return (epochClass & mask) != 0; |
|
||||
} |
|
||||
|
|
||||
bool EpochManager::isPredecessorEpochClass(EpochClass const& epochClass1, EpochClass const& epochClass2) const { |
|
||||
return (epochClass1 & epochClass2) == epochClass1; |
|
||||
} |
|
||||
|
|
||||
void EpochManager::setBottomDimension(Epoch& epoch, uint64_t const& dimension) const { |
|
||||
STORM_LOG_ASSERT(dimensionCount > 0, "Invoked EpochManager with zero dimension count."); |
|
||||
epoch |= (dimensionBitMask << (dimension * bitsPerDimension)); |
|
||||
} |
|
||||
|
|
||||
void EpochManager::setDimensionOfEpoch(Epoch& epoch, uint64_t const& dimension, uint64_t const& value) const { |
|
||||
STORM_LOG_ASSERT(dimensionCount > 0, "Invoked EpochManager with zero dimension count."); |
|
||||
STORM_LOG_ASSERT(isValidDimensionValue(value), "The dimension value " << value << " is too high."); |
|
||||
epoch &= ~(dimensionBitMask << (dimension * bitsPerDimension)); |
|
||||
epoch |= (value << (dimension * bitsPerDimension)); |
|
||||
} |
|
||||
|
|
||||
bool EpochManager::isBottomDimension(Epoch const& epoch, uint64_t const& dimension) const { |
|
||||
STORM_LOG_ASSERT(dimensionCount > 0, "Invoked EpochManager with zero dimension count."); |
|
||||
return (epoch | (dimensionBitMask << (dimension * bitsPerDimension))) == epoch; |
|
||||
} |
|
||||
|
|
||||
bool EpochManager::isBottomDimensionEpochClass(EpochClass const& epochClass, uint64_t const& dimension) const { |
|
||||
STORM_LOG_ASSERT(dimensionCount > 0, "Invoked EpochManager with zero dimension count."); |
|
||||
return (epochClass | (1 << dimension)) == epochClass; |
|
||||
} |
|
||||
|
|
||||
uint64_t EpochManager::getDimensionOfEpoch(Epoch const& epoch, uint64_t const& dimension) const { |
|
||||
STORM_LOG_ASSERT(dimensionCount > 0, "Invoked EpochManager with zero dimension count."); |
|
||||
return (epoch >> (dimension * bitsPerDimension)) & dimensionBitMask; |
|
||||
} |
|
||||
|
|
||||
std::string EpochManager::toString(Epoch const& epoch) const { |
|
||||
STORM_LOG_ASSERT(dimensionCount > 0, "Invoked EpochManager with zero dimension count."); |
|
||||
std::string res = "<" + (isBottomDimension(epoch, 0) ? "_" : std::to_string(getDimensionOfEpoch(epoch, 0))); |
|
||||
for (uint64_t d = 1; d < dimensionCount; ++d) { |
|
||||
res += ", "; |
|
||||
res += (isBottomDimension(epoch, d) ? "_" : std::to_string(getDimensionOfEpoch(epoch, d))); |
|
||||
} |
|
||||
res += ">"; |
|
||||
return res; |
|
||||
} |
|
||||
|
|
||||
bool EpochManager::epochClassZigZagOrder(Epoch const& epoch1, Epoch const& epoch2) const { |
|
||||
STORM_LOG_ASSERT(dimensionCount > 0, "Invoked EpochManager with zero dimension count."); |
|
||||
|
|
||||
// Return true iff epoch 1 has to be computed before epoch 2
|
|
||||
|
|
||||
if (!compareEpochClass(epoch1, epoch2)) { |
|
||||
return epochClassOrder(getEpochClass(epoch1), getEpochClass(epoch2)); |
|
||||
} |
|
||||
|
|
||||
// check whether the sum of dimensions is the same
|
|
||||
uint64_t e1Sum = 0; |
|
||||
uint64_t e2Sum = 0; |
|
||||
for (uint64_t dim = 0; dim < dimensionCount; ++dim) { |
|
||||
if (!isBottomDimension(epoch1, dim)) { |
|
||||
assert(!isBottomDimension(epoch2, dim)); |
|
||||
e1Sum += getDimensionOfEpoch(epoch1, dim); |
|
||||
e2Sum += getDimensionOfEpoch(epoch2, dim); |
|
||||
} |
|
||||
} |
|
||||
if (e1Sum < e2Sum) { |
|
||||
return true; |
|
||||
} else if (e1Sum > e2Sum) { |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
// find the first dimension where the epochs do not match.
|
|
||||
// if the sum is even, we search from left to right, otherwise from right to left
|
|
||||
bool sumEven = (e1Sum % 2) == 0; |
|
||||
if (sumEven) { |
|
||||
for (uint64_t dim = 0; dim < dimensionCount; ++dim) { |
|
||||
uint64_t e1Value = getDimensionOfEpoch(epoch1, dim); |
|
||||
uint64_t e2Value = getDimensionOfEpoch(epoch2, dim); |
|
||||
if (e1Value < e2Value) { |
|
||||
return true; |
|
||||
} else if (e1Value > e2Value) { |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
} else { |
|
||||
uint64_t dim = dimensionCount; |
|
||||
while (dim > 0) { |
|
||||
--dim; |
|
||||
uint64_t e1Value = getDimensionOfEpoch(epoch1, dim); |
|
||||
uint64_t e2Value = getDimensionOfEpoch(epoch2, dim); |
|
||||
if (e1Value < e2Value) { |
|
||||
return true; |
|
||||
} else if (e1Value > e2Value) { |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// reaching this point means that the epochs are equal
|
|
||||
assert(epoch1 == epoch2); |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
bool EpochManager::epochClassOrder(EpochClass const& epochClass1, EpochClass const& epochClass2) const { |
|
||||
STORM_LOG_ASSERT(dimensionCount > 0, "Invoked EpochManager with zero dimension count."); |
|
||||
|
|
||||
if (epochClass1 == epochClass2) { |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
// Return true iff epochClass 1 is a successor class of epochClass 2
|
|
||||
|
|
||||
// Check whether the number of bottom dimensions is not equal
|
|
||||
int64_t count; |
|
||||
EpochClass ec = epochClass1; |
|
||||
for (count = 0; ec; ++count) { |
|
||||
ec &= ec - 1; |
|
||||
} |
|
||||
ec = epochClass2; |
|
||||
for (; ec; --count) { |
|
||||
ec &= ec - 1; |
|
||||
} |
|
||||
if (count > 0) { |
|
||||
return true; |
|
||||
} else if (count < 0) { |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
return epochClass1 < epochClass2; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,62 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include <vector> |
|
||||
#include <set> |
|
||||
#include <string> |
|
||||
|
|
||||
namespace storm { |
|
||||
namespace modelchecker { |
|
||||
namespace multiobjective { |
|
||||
|
|
||||
|
|
||||
class EpochManager { |
|
||||
public: |
|
||||
|
|
||||
typedef uint64_t Epoch; // The number of reward steps that are "left" for each dimension |
|
||||
typedef uint64_t EpochClass; // The number of reward steps that are "left" for each dimension |
|
||||
|
|
||||
EpochManager(); |
|
||||
EpochManager(uint64_t dimensionCount); |
|
||||
|
|
||||
uint64_t const& getDimensionCount() const; |
|
||||
|
|
||||
Epoch getBottomEpoch() const; |
|
||||
Epoch getZeroEpoch() const; |
|
||||
|
|
||||
bool compareEpochClass(Epoch const& epoch1, Epoch const& epoch2) const; |
|
||||
EpochClass getEpochClass(Epoch const& epoch) const; |
|
||||
|
|
||||
Epoch getSuccessorEpoch(Epoch const& epoch, Epoch const& step) const; |
|
||||
std::vector<Epoch> getPredecessorEpochs(Epoch const& epoch, Epoch const& step) const; |
|
||||
void gatherPredecessorEpochs(std::set<Epoch>& gatheredPredecessorEpochs, Epoch const& epoch, Epoch const& step) const; |
|
||||
|
|
||||
bool isZeroEpoch(Epoch const& epoch) const; |
|
||||
bool isBottomEpoch(Epoch const& epoch) const; |
|
||||
bool hasBottomDimension(Epoch const& epoch) const; |
|
||||
bool hasBottomDimensionEpochClass(EpochClass const& epochClass) const; |
|
||||
bool isPredecessorEpochClass(EpochClass const& epochClass1, EpochClass const& epochClass2) const; |
|
||||
|
|
||||
bool isValidDimensionValue(uint64_t const& value) const; |
|
||||
|
|
||||
void setBottomDimension(Epoch& epoch, uint64_t const& dimension) const; |
|
||||
void setDimensionOfEpoch(Epoch& epoch, uint64_t const& dimension, uint64_t const& value) const; // assumes that the value is valid, i.e., small enough |
|
||||
|
|
||||
bool isBottomDimension(Epoch const& epoch, uint64_t const& dimension) const; |
|
||||
bool isBottomDimensionEpochClass(EpochClass const& epochClass, uint64_t const& dimension) const; |
|
||||
uint64_t getDimensionOfEpoch(Epoch const& epoch, uint64_t const& dimension) const; // assumes that the dimension is not bottom |
|
||||
|
|
||||
std::string toString(Epoch const& epoch) const; |
|
||||
|
|
||||
bool epochClassZigZagOrder(Epoch const& epoch1, Epoch const& epoch2) const; |
|
||||
bool epochClassOrder(EpochClass const& epochClass1, EpochClass const& epochClass2) const; |
|
||||
|
|
||||
private: |
|
||||
|
|
||||
uint64_t dimensionCount; |
|
||||
uint64_t bitsPerDimension; |
|
||||
uint64_t dimensionBitMask; |
|
||||
uint64_t relevantBitsMask; |
|
||||
}; |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,89 +0,0 @@ |
|||||
#include "storm/modelchecker/multiobjective/rewardbounded/MemoryStateManager.h"
|
|
||||
|
|
||||
#include "storm/utility/macros.h"
|
|
||||
|
|
||||
#include "storm/exceptions/IllegalArgumentException.h"
|
|
||||
|
|
||||
namespace storm { |
|
||||
namespace modelchecker { |
|
||||
namespace multiobjective { |
|
||||
|
|
||||
MemoryStateManager::MemoryStateManager(uint64_t dimensionCount) : dimensionCount(dimensionCount), dimensionBitMask(1ull), relevantBitsMask((1ull << dimensionCount) - 1), stateCount(dimensionBitMask << dimensionCount), dimensionsWithoutMemoryMask(0), upperMemoryStateBound(stateCount) { |
|
||||
STORM_LOG_THROW(dimensionCount > 0, storm::exceptions::IllegalArgumentException, "Invoked MemoryStateManager with zero dimension count."); |
|
||||
STORM_LOG_THROW(dimensionCount <= 64, storm::exceptions::IllegalArgumentException, "Invoked MemoryStateManager with too many dimensions."); |
|
||||
} |
|
||||
|
|
||||
uint64_t const& MemoryStateManager::getDimensionCount() const { |
|
||||
return dimensionCount; |
|
||||
} |
|
||||
|
|
||||
uint64_t const& MemoryStateManager::getMemoryStateCount() const { |
|
||||
return stateCount; |
|
||||
} |
|
||||
|
|
||||
MemoryStateManager::MemoryState const& MemoryStateManager::getUpperMemoryStateBound() const { |
|
||||
return upperMemoryStateBound; |
|
||||
} |
|
||||
|
|
||||
void MemoryStateManager::setDimensionWithoutMemory(uint64_t dimension) { |
|
||||
STORM_LOG_ASSERT(dimensionCount > 0, "Invoked MemoryStateManager with zero dimension count."); |
|
||||
STORM_LOG_ASSERT(dimension < dimensionCount, "Tried to set a dimension that is larger then the number of considered dimensions"); |
|
||||
if (((dimensionBitMask << dimension) & dimensionsWithoutMemoryMask) == 0) { |
|
||||
stateCount = (stateCount >> 1); |
|
||||
} |
|
||||
dimensionsWithoutMemoryMask |= (dimensionBitMask << dimension); |
|
||||
} |
|
||||
|
|
||||
MemoryStateManager::MemoryState MemoryStateManager::getInitialMemoryState() const { |
|
||||
STORM_LOG_ASSERT(dimensionCount > 0, "Invoked MemoryStateManager with zero dimension count."); |
|
||||
return relevantBitsMask; |
|
||||
} |
|
||||
|
|
||||
bool MemoryStateManager::isRelevantDimension(MemoryState const& state, uint64_t dimension) const { |
|
||||
STORM_LOG_ASSERT(dimensionCount > 0, "Invoked MemoryStateManager with zero dimension count."); |
|
||||
STORM_LOG_ASSERT((state & dimensionsWithoutMemoryMask) == dimensionsWithoutMemoryMask, "Invalid memory state found."); |
|
||||
return (state & (dimensionBitMask << dimension)) != 0; |
|
||||
} |
|
||||
|
|
||||
void MemoryStateManager::setRelevantDimension(MemoryState& state, uint64_t dimension, bool value) const { |
|
||||
STORM_LOG_ASSERT(dimensionCount > 0, "Invoked MemoryStateManager with zero dimension count."); |
|
||||
STORM_LOG_ASSERT(dimension < dimensionCount, "Tried to set a dimension that is larger then the number of considered dimensions"); |
|
||||
STORM_LOG_ASSERT(((dimensionBitMask << dimension) & dimensionsWithoutMemoryMask) == 0, "Tried to change a memory state for a dimension but the dimension is assumed to have no memory."); |
|
||||
if (value) { |
|
||||
state |= (dimensionBitMask << dimension); |
|
||||
} else { |
|
||||
state &= ~(dimensionBitMask << dimension); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void MemoryStateManager::setRelevantDimensions(MemoryState& state, storm::storage::BitVector const& dimensions, bool value) const { |
|
||||
STORM_LOG_ASSERT(dimensionCount > 0, "Invoked MemoryStateManager with zero dimension count."); |
|
||||
STORM_LOG_ASSERT(dimensions.size() == dimensionCount, "Invalid size of given bitset."); |
|
||||
if (value) { |
|
||||
for (auto const& d : dimensions) { |
|
||||
STORM_LOG_ASSERT(((dimensionBitMask << d) & dimensionsWithoutMemoryMask) == 0, "Tried to set a dimension to 'relevant'-memory state but the dimension is assumed to have no memory."); |
|
||||
state |= (dimensionBitMask << d); |
|
||||
} |
|
||||
} else { |
|
||||
for (auto const& d : dimensions) { |
|
||||
STORM_LOG_ASSERT(((dimensionBitMask << d) & dimensionsWithoutMemoryMask) == 0, "Tried to set a dimension to 'unrelevant'-memory state but the dimension is assumed to have no memory."); |
|
||||
state &= ~(dimensionBitMask << d); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
std::string MemoryStateManager::toString(MemoryState const& state) const { |
|
||||
STORM_LOG_ASSERT(dimensionCount > 0, "Invoked EpochManager with zero dimension count."); |
|
||||
std::string res = "["; |
|
||||
res += (isRelevantDimension(state, 0) ? "1" : "0"); |
|
||||
for (uint64_t d = 1; d < dimensionCount; ++d) { |
|
||||
res += ", "; |
|
||||
res += (isRelevantDimension(state, d) ? "1" : "0"); |
|
||||
} |
|
||||
res += "]"; |
|
||||
return res; |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,45 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include <vector> |
|
||||
#include <set> |
|
||||
#include <string> |
|
||||
|
|
||||
#include "storm/storage/BitVector.h" |
|
||||
|
|
||||
namespace storm { |
|
||||
namespace modelchecker { |
|
||||
namespace multiobjective { |
|
||||
|
|
||||
|
|
||||
class MemoryStateManager { |
|
||||
public: |
|
||||
|
|
||||
typedef uint64_t MemoryState; |
|
||||
|
|
||||
MemoryStateManager(uint64_t dimensionCount); |
|
||||
|
|
||||
void setDimensionWithoutMemory(uint64_t dimension); |
|
||||
|
|
||||
uint64_t const& getDimensionCount() const; |
|
||||
uint64_t const& getMemoryStateCount() const; |
|
||||
MemoryState const& getUpperMemoryStateBound() const; // is larger then every valid memory state m, i.e., m < getUpperMemoryStateBound() holds for all m |
|
||||
|
|
||||
MemoryState getInitialMemoryState() const; |
|
||||
bool isRelevantDimension(MemoryState const& state, uint64_t dimension) const; |
|
||||
void setRelevantDimension(MemoryState& state, uint64_t dimension, bool value = true) const; |
|
||||
void setRelevantDimensions(MemoryState& state, storm::storage::BitVector const& dimensions, bool value = true) const; |
|
||||
|
|
||||
std::string toString(MemoryState const& state) const; |
|
||||
|
|
||||
private: |
|
||||
uint64_t const dimensionCount; |
|
||||
uint64_t const dimensionBitMask; |
|
||||
uint64_t const relevantBitsMask; |
|
||||
uint64_t stateCount; |
|
||||
uint64_t dimensionsWithoutMemoryMask; |
|
||||
MemoryState const upperMemoryStateBound; |
|
||||
|
|
||||
}; |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,639 +0,0 @@ |
|||||
#include "storm/modelchecker/multiobjective/rewardbounded/MultiDimensionalRewardUnfolding.h"
|
|
||||
|
|
||||
#include <string>
|
|
||||
#include <set>
|
|
||||
#include <functional>
|
|
||||
|
|
||||
#include "storm/utility/macros.h"
|
|
||||
#include "storm/logic/Formulas.h"
|
|
||||
|
|
||||
#include "storm/modelchecker/propositional/SparsePropositionalModelChecker.h"
|
|
||||
#include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h"
|
|
||||
#include "storm/transformer/EndComponentEliminator.h"
|
|
||||
|
|
||||
#include "storm/exceptions/UnexpectedException.h"
|
|
||||
#include "storm/exceptions/IllegalArgumentException.h"
|
|
||||
#include "storm/exceptions/NotSupportedException.h"
|
|
||||
#include "storm/exceptions/InvalidPropertyException.h"
|
|
||||
|
|
||||
namespace storm { |
|
||||
namespace modelchecker { |
|
||||
namespace multiobjective { |
|
||||
|
|
||||
template<typename ValueType, bool SingleObjectiveMode> |
|
||||
MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::MultiDimensionalRewardUnfolding(storm::models::sparse::Mdp<ValueType> const& model, std::vector<storm::modelchecker::multiobjective::Objective<ValueType>> const& objectives) : model(model), objectives(objectives) { |
|
||||
initialize(); |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType, bool SingleObjectiveMode> |
|
||||
MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::MultiDimensionalRewardUnfolding(storm::models::sparse::Mdp<ValueType> const& model, std::shared_ptr<storm::logic::OperatorFormula const> objectiveFormula) : model(model) { |
|
||||
|
|
||||
STORM_LOG_THROW(objectiveFormula->hasOptimalityType(), storm::exceptions::InvalidPropertyException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model."); |
|
||||
if (objectiveFormula->isProbabilityOperatorFormula()) { |
|
||||
if (objectiveFormula->getSubformula().isMultiObjectiveFormula()) { |
|
||||
for (auto const& subFormula : objectiveFormula->getSubformula().asMultiObjectiveFormula().getSubformulas()) { |
|
||||
STORM_LOG_THROW(subFormula->isBoundedUntilFormula(), storm::exceptions::InvalidPropertyException, "Formula " << objectiveFormula << " is not supported. Invalid subformula " << *subFormula << "."); |
|
||||
} |
|
||||
} else { |
|
||||
STORM_LOG_THROW(objectiveFormula->getSubformula().isBoundedUntilFormula(), storm::exceptions::InvalidPropertyException, "Formula " << objectiveFormula << " is not supported. Invalid subformula " << objectiveFormula->getSubformula() << "."); |
|
||||
} |
|
||||
} else { |
|
||||
STORM_LOG_THROW(objectiveFormula->isRewardOperatorFormula() && objectiveFormula->getSubformula().isCumulativeRewardFormula(), storm::exceptions::InvalidPropertyException, "Formula " << objectiveFormula << " is not supported."); |
|
||||
|
|
||||
} |
|
||||
|
|
||||
// Build an objective from the formula.
|
|
||||
storm::modelchecker::multiobjective::Objective<ValueType> objective; |
|
||||
objective.formula = objectiveFormula; |
|
||||
objective.originalFormula = objective.formula; |
|
||||
objective.considersComplementaryEvent = false; |
|
||||
objectives.push_back(std::move(objective)); |
|
||||
|
|
||||
initialize(); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
template<typename ValueType, bool SingleObjectiveMode> |
|
||||
void MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::initialize() { |
|
||||
|
|
||||
maxSolutionsStored = 0; |
|
||||
|
|
||||
STORM_LOG_ASSERT(!SingleObjectiveMode || (this->objectives.size() == 1), "Enabled single objective mode but there are multiple objectives."); |
|
||||
std::vector<Epoch> epochSteps; |
|
||||
initializeObjectives(epochSteps); |
|
||||
initializeMemoryProduct(epochSteps); |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType, bool SingleObjectiveMode> |
|
||||
void MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::initializeObjectives(std::vector<Epoch>& epochSteps) { |
|
||||
std::vector<std::vector<uint64_t>> dimensionWiseEpochSteps; |
|
||||
// collect the time-bounded subobjectives
|
|
||||
for (uint64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) { |
|
||||
auto const& formula = *this->objectives[objIndex].formula; |
|
||||
if (formula.isProbabilityOperatorFormula()) { |
|
||||
STORM_LOG_THROW(formula.getSubformula().isBoundedUntilFormula(), storm::exceptions::NotSupportedException, "Unexpected type of subformula for formula " << formula); |
|
||||
auto const& subformula = formula.getSubformula().asBoundedUntilFormula(); |
|
||||
for (uint64_t dim = 0; dim < subformula.getDimension(); ++dim) { |
|
||||
Dimension<ValueType> dimension; |
|
||||
dimension.formula = subformula.restrictToDimension(dim); |
|
||||
dimension.objectiveIndex = objIndex; |
|
||||
std::string memLabel = "dim" + std::to_string(dimensions.size()) + "_maybe"; |
|
||||
while (model.getStateLabeling().containsLabel(memLabel)) { |
|
||||
memLabel = "_" + memLabel; |
|
||||
} |
|
||||
dimension.memoryLabel = memLabel; |
|
||||
dimension.isUpperBounded = subformula.hasUpperBound(dim); |
|
||||
// for simplicity we do not allow intervals or unbounded formulas.
|
|
||||
STORM_LOG_THROW(subformula.hasLowerBound(dim) != dimension.isUpperBounded, storm::exceptions::NotSupportedException, "Bounded until formulas are only supported by this method if they consider either an upper bound or a lower bound. Got " << subformula << " instead."); |
|
||||
// lower bounded until formulas with non-trivial left hand side are excluded as this would require some additional effort (in particular the ProductModel::transformMemoryState method).
|
|
||||
STORM_LOG_THROW(dimension.isUpperBounded || subformula.getLeftSubformula(dim).isTrueFormula(), storm::exceptions::NotSupportedException, "Lower bounded until formulas are only supported by this method if the left subformula is 'true'. Got " << subformula << " instead."); |
|
||||
if (subformula.getTimeBoundReference(dim).isTimeBound() || subformula.getTimeBoundReference(dim).isStepBound()) { |
|
||||
dimensionWiseEpochSteps.push_back(std::vector<uint64_t>(model.getNumberOfChoices(), 1)); |
|
||||
dimension.scalingFactor = storm::utility::one<ValueType>(); |
|
||||
} else { |
|
||||
STORM_LOG_ASSERT(subformula.getTimeBoundReference(dim).isRewardBound(), "Unexpected type of time bound."); |
|
||||
std::string const& rewardName = subformula.getTimeBoundReference(dim).getRewardName(); |
|
||||
STORM_LOG_THROW(this->model.hasRewardModel(rewardName), storm::exceptions::IllegalArgumentException, "No reward model with name '" << rewardName << "' found."); |
|
||||
auto const& rewardModel = this->model.getRewardModel(rewardName); |
|
||||
STORM_LOG_THROW(!rewardModel.hasTransitionRewards(), storm::exceptions::NotSupportedException, "Transition rewards are currently not supported as reward bounds."); |
|
||||
std::vector<ValueType> actionRewards = rewardModel.getTotalRewardVector(this->model.getTransitionMatrix()); |
|
||||
auto discretizedRewardsAndFactor = storm::utility::vector::toIntegralVector<ValueType, uint64_t>(actionRewards); |
|
||||
dimensionWiseEpochSteps.push_back(std::move(discretizedRewardsAndFactor.first)); |
|
||||
dimension.scalingFactor = std::move(discretizedRewardsAndFactor.second); |
|
||||
} |
|
||||
dimensions.emplace_back(std::move(dimension)); |
|
||||
} |
|
||||
} else if (formula.isRewardOperatorFormula() && formula.getSubformula().isCumulativeRewardFormula()) { |
|
||||
auto const& subformula = formula.getSubformula().asCumulativeRewardFormula(); |
|
||||
Dimension<ValueType> dimension; |
|
||||
dimension.formula = formula.getSubformula().asSharedPointer(); |
|
||||
dimension.objectiveIndex = objIndex; |
|
||||
dimension.isUpperBounded = true; |
|
||||
if (subformula.getTimeBoundReference().isTimeBound() || subformula.getTimeBoundReference().isStepBound()) { |
|
||||
dimensionWiseEpochSteps.push_back(std::vector<uint64_t>(model.getNumberOfChoices(), 1)); |
|
||||
dimension.scalingFactor = storm::utility::one<ValueType>(); |
|
||||
} else { |
|
||||
STORM_LOG_ASSERT(subformula.getTimeBoundReference().isRewardBound(), "Unexpected type of time bound."); |
|
||||
std::string const& rewardName = subformula.getTimeBoundReference().getRewardName(); |
|
||||
STORM_LOG_THROW(this->model.hasRewardModel(rewardName), storm::exceptions::IllegalArgumentException, "No reward model with name '" << rewardName << "' found."); |
|
||||
auto const& rewardModel = this->model.getRewardModel(rewardName); |
|
||||
STORM_LOG_THROW(!rewardModel.hasTransitionRewards(), storm::exceptions::NotSupportedException, "Transition rewards are currently not supported as reward bounds."); |
|
||||
std::vector<ValueType> actionRewards = rewardModel.getTotalRewardVector(this->model.getTransitionMatrix()); |
|
||||
auto discretizedRewardsAndFactor = storm::utility::vector::toIntegralVector<ValueType, uint64_t>(actionRewards); |
|
||||
dimensionWiseEpochSteps.push_back(std::move(discretizedRewardsAndFactor.first)); |
|
||||
dimension.scalingFactor = std::move(discretizedRewardsAndFactor.second); |
|
||||
} |
|
||||
dimensions.emplace_back(std::move(dimension)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// Compute a mapping for each objective to the set of dimensions it considers
|
|
||||
// Also store for each dimension dim, which dimensions should be unsatisfiable whenever the bound of dim is violated
|
|
||||
uint64_t dim = 0; |
|
||||
for (uint64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) { |
|
||||
storm::storage::BitVector objDimensions(dimensions.size(), false); |
|
||||
if (objectives[objIndex].formula->isProbabilityOperatorFormula() && objectives[objIndex].formula->getSubformula().isBoundedUntilFormula()) { |
|
||||
auto const& boundedUntilFormula = objectives[objIndex].formula->getSubformula().asBoundedUntilFormula(); |
|
||||
for (uint64_t currDim = dim; currDim < dim + boundedUntilFormula.getDimension(); ++currDim ) { |
|
||||
objDimensions.set(currDim); |
|
||||
} |
|
||||
for (uint64_t currDim = dim; currDim < dim + boundedUntilFormula.getDimension(); ++currDim ) { |
|
||||
if (!boundedUntilFormula.hasMultiDimensionalSubformulas() || dimensions[currDim].isUpperBounded) { |
|
||||
dimensions[currDim].dependentDimensions = objDimensions; |
|
||||
} else { |
|
||||
dimensions[currDim].dependentDimensions = storm::storage::BitVector(dimensions.size(), false); |
|
||||
dimensions[currDim].dependentDimensions.set(currDim, true); |
|
||||
} |
|
||||
// std::cout << "dimension " << currDim << " has depDims: " << dimensions[currDim].dependentDimensions << std::endl;
|
|
||||
} |
|
||||
dim += boundedUntilFormula.getDimension(); |
|
||||
} else if (objectives[objIndex].formula->isRewardOperatorFormula() && objectives[objIndex].formula->getSubformula().isCumulativeRewardFormula()) { |
|
||||
objDimensions.set(dim, true); |
|
||||
dimensions[dim].dependentDimensions = objDimensions; |
|
||||
++dim; |
|
||||
} |
|
||||
|
|
||||
objectiveDimensions.push_back(std::move(objDimensions)); |
|
||||
} |
|
||||
assert(dim == dimensions.size()); |
|
||||
|
|
||||
|
|
||||
// Initialize the epoch manager
|
|
||||
epochManager = EpochManager(dimensions.size()); |
|
||||
|
|
||||
// Convert the epoch steps to a choice-wise representation
|
|
||||
epochSteps.reserve(model.getNumberOfChoices()); |
|
||||
for (uint64_t choice = 0; choice < model.getNumberOfChoices(); ++choice) { |
|
||||
Epoch step; |
|
||||
uint64_t dim = 0; |
|
||||
for (auto const& dimensionSteps : dimensionWiseEpochSteps) { |
|
||||
epochManager.setDimensionOfEpoch(step, dim, dimensionSteps[choice]); |
|
||||
++dim; |
|
||||
} |
|
||||
epochSteps.push_back(step); |
|
||||
} |
|
||||
|
|
||||
// collect which epoch steps are possible
|
|
||||
possibleEpochSteps.clear(); |
|
||||
for (auto const& step : epochSteps) { |
|
||||
possibleEpochSteps.insert(step); |
|
||||
} |
|
||||
|
|
||||
// Set the maximal values we need to consider for each dimension
|
|
||||
computeMaxDimensionValues(); |
|
||||
|
|
||||
} |
|
||||
|
|
||||
template<typename ValueType, bool SingleObjectiveMode> |
|
||||
void MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::initializeMemoryProduct(std::vector<Epoch> const& epochSteps) { |
|
||||
productModel = std::make_unique<ProductModel<ValueType>>(model, objectives, dimensions, objectiveDimensions, epochManager, epochSteps); |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType, bool SingleObjectiveMode> |
|
||||
void MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::computeMaxDimensionValues() { |
|
||||
for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) { |
|
||||
storm::expressions::Expression bound; |
|
||||
bool isStrict = false; |
|
||||
storm::logic::Formula const& dimFormula = *dimensions[dim].formula; |
|
||||
if (dimFormula.isBoundedUntilFormula()) { |
|
||||
assert(!dimFormula.asBoundedUntilFormula().isMultiDimensional()); |
|
||||
if (dimFormula.asBoundedUntilFormula().hasUpperBound()) { |
|
||||
STORM_LOG_ASSERT(!dimFormula.asBoundedUntilFormula().hasLowerBound(), "Bounded until formulas with interval bounds are not supported."); |
|
||||
bound = dimFormula.asBoundedUntilFormula().getUpperBound(); |
|
||||
isStrict = dimFormula.asBoundedUntilFormula().isUpperBoundStrict(); |
|
||||
} else { |
|
||||
STORM_LOG_ASSERT(dimFormula.asBoundedUntilFormula().hasLowerBound(), "Bounded until formulas without any bounds are not supported."); |
|
||||
bound = dimFormula.asBoundedUntilFormula().getLowerBound(); |
|
||||
isStrict = dimFormula.asBoundedUntilFormula().isLowerBoundStrict(); |
|
||||
} |
|
||||
} else if (dimFormula.isCumulativeRewardFormula()) { |
|
||||
bound = dimFormula.asCumulativeRewardFormula().getBound(); |
|
||||
isStrict = dimFormula.asCumulativeRewardFormula().isBoundStrict(); |
|
||||
} |
|
||||
STORM_LOG_THROW(!bound.containsVariables(), storm::exceptions::NotSupportedException, "The bound " << bound << " contains undefined constants."); |
|
||||
ValueType discretizedBound = storm::utility::convertNumber<ValueType>(bound.evaluateAsRational()); |
|
||||
STORM_LOG_THROW(dimensions[dim].isUpperBounded || isStrict || !storm::utility::isZero(discretizedBound), storm::exceptions::NotSupportedException, "Lower bounds need to be either strict or greater than zero."); |
|
||||
discretizedBound /= dimensions[dim].scalingFactor; |
|
||||
if (storm::utility::isInteger(discretizedBound)) { |
|
||||
if (isStrict == dimensions[dim].isUpperBounded) { |
|
||||
discretizedBound -= storm::utility::one<ValueType>(); |
|
||||
} |
|
||||
} else { |
|
||||
discretizedBound = storm::utility::floor(discretizedBound); |
|
||||
} |
|
||||
uint64_t dimensionValue = storm::utility::convertNumber<uint64_t>(discretizedBound); |
|
||||
STORM_LOG_THROW(epochManager.isValidDimensionValue(dimensionValue), storm::exceptions::NotSupportedException, "The bound " << bound << " is too high for the considered number of dimensions."); |
|
||||
dimensions[dim].maxValue = dimensionValue; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType, bool SingleObjectiveMode> |
|
||||
typename MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::Epoch MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::getStartEpoch() { |
|
||||
Epoch startEpoch = epochManager.getZeroEpoch(); |
|
||||
for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) { |
|
||||
STORM_LOG_ASSERT(dimensions[dim].maxValue, "No max-value for dimension " << dim << " was given."); |
|
||||
epochManager.setDimensionOfEpoch(startEpoch, dim, dimensions[dim].maxValue.get()); |
|
||||
} |
|
||||
STORM_LOG_TRACE("Start epoch is " << epochManager.toString(startEpoch)); |
|
||||
return startEpoch; |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType, bool SingleObjectiveMode> |
|
||||
std::vector<typename MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::Epoch> MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::getEpochComputationOrder(Epoch const& startEpoch) { |
|
||||
// Perform a DFS to find all the reachable epochs
|
|
||||
std::vector<Epoch> dfsStack; |
|
||||
std::set<Epoch, std::function<bool(Epoch const&, Epoch const&)>> collectedEpochs(std::bind(&EpochManager::epochClassZigZagOrder, &epochManager, std::placeholders::_1, std::placeholders::_2)); |
|
||||
|
|
||||
collectedEpochs.insert(startEpoch); |
|
||||
dfsStack.push_back(startEpoch); |
|
||||
while (!dfsStack.empty()) { |
|
||||
Epoch currentEpoch = dfsStack.back(); |
|
||||
dfsStack.pop_back(); |
|
||||
for (auto const& step : possibleEpochSteps) { |
|
||||
Epoch successorEpoch = epochManager.getSuccessorEpoch(currentEpoch, step); |
|
||||
/*
|
|
||||
for (auto const& e : collectedEpochs) { |
|
||||
std::cout << "Comparing " << epochManager.toString(e) << " and " << epochManager.toString(successorEpoch) << std::endl; |
|
||||
if (epochManager.epochClassZigZagOrder(e, successorEpoch)) { |
|
||||
std::cout << " " << epochManager.toString(e) << " < " << epochManager.toString(successorEpoch) << std::endl; |
|
||||
} |
|
||||
if (epochManager.epochClassZigZagOrder(successorEpoch, e)) { |
|
||||
std::cout << " " << epochManager.toString(e) << " > " << epochManager.toString(successorEpoch) << std::endl; |
|
||||
} |
|
||||
} |
|
||||
*/ |
|
||||
if (collectedEpochs.insert(successorEpoch).second) { |
|
||||
dfsStack.push_back(std::move(successorEpoch)); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
/*
|
|
||||
std::cout << "Resulting order: "; |
|
||||
for (auto const& e : collectedEpochs) { |
|
||||
std::cout << epochManager.toString(e) << ", "; |
|
||||
} |
|
||||
std::cout << std::endl; |
|
||||
*/ |
|
||||
return std::vector<Epoch>(collectedEpochs.begin(), collectedEpochs.end()); |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType, bool SingleObjectiveMode> |
|
||||
typename MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::EpochModel& MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::setCurrentEpoch(Epoch const& epoch) { |
|
||||
STORM_LOG_DEBUG("Setting model for epoch " << epochManager.toString(epoch)); |
|
||||
|
|
||||
// Check if we need to update the current epoch class
|
|
||||
if (!currentEpoch || !epochManager.compareEpochClass(epoch, currentEpoch.get())) { |
|
||||
setCurrentEpochClass(epoch); |
|
||||
epochModel.epochMatrixChanged = true; |
|
||||
} else { |
|
||||
epochModel.epochMatrixChanged = false; |
|
||||
} |
|
||||
|
|
||||
bool containsLowerBoundedObjective = false; |
|
||||
for (auto const& dimension : dimensions) { |
|
||||
if (!dimension.isUpperBounded) { |
|
||||
containsLowerBoundedObjective = true; |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
std::map<Epoch, EpochSolution const*> subSolutions; |
|
||||
for (auto const& step : possibleEpochSteps) { |
|
||||
Epoch successorEpoch = epochManager.getSuccessorEpoch(epoch, step); |
|
||||
if (successorEpoch != epoch) { |
|
||||
auto successorSolIt = epochSolutions.find(successorEpoch); |
|
||||
STORM_LOG_ASSERT(successorSolIt != epochSolutions.end(), "Solution for successor epoch does not exist (anymore)."); |
|
||||
subSolutions.emplace(successorEpoch, &successorSolIt->second); |
|
||||
} |
|
||||
} |
|
||||
epochModel.stepSolutions.resize(epochModel.stepChoices.getNumberOfSetBits()); |
|
||||
auto stepSolIt = epochModel.stepSolutions.begin(); |
|
||||
for (auto const& reducedChoice : epochModel.stepChoices) { |
|
||||
uint64_t productChoice = epochModelToProductChoiceMap[reducedChoice]; |
|
||||
uint64_t productState = productModel->getProductStateFromChoice(productChoice); |
|
||||
auto const& memoryState = productModel->getMemoryState(productState); |
|
||||
Epoch successorEpoch = epochManager.getSuccessorEpoch(epoch, productModel->getSteps()[productChoice]); |
|
||||
EpochClass successorEpochClass = epochManager.getEpochClass(successorEpoch); |
|
||||
// Find out whether objective reward is earned for the current choice
|
|
||||
// Objective reward is not earned if
|
|
||||
// a) there is an upper bounded subObjective that is __still_relevant__ but the corresponding reward bound is passed after taking the choice
|
|
||||
// b) there is a lower bounded subObjective and the corresponding reward bound is not passed yet.
|
|
||||
for (uint64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) { |
|
||||
bool rewardEarned = !storm::utility::isZero(epochModel.objectiveRewards[objIndex][reducedChoice]); |
|
||||
if (rewardEarned) { |
|
||||
for (auto const& dim : objectiveDimensions[objIndex]) { |
|
||||
if (dimensions[dim].isUpperBounded == epochManager.isBottomDimension(successorEpoch, dim) && productModel->getMemoryStateManager().isRelevantDimension(memoryState, dim)) { |
|
||||
rewardEarned = false; |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
epochModel.objectiveRewardFilter[objIndex].set(reducedChoice, rewardEarned); |
|
||||
} |
|
||||
// compute the solution for the stepChoices
|
|
||||
// For optimization purposes, we distinguish the case where the memory state does not have to be transformed
|
|
||||
EpochSolution const& successorEpochSolution = getEpochSolution(subSolutions, successorEpoch); |
|
||||
SolutionType choiceSolution; |
|
||||
bool firstSuccessor = true; |
|
||||
if (!containsLowerBoundedObjective && epochManager.compareEpochClass(epoch, successorEpoch)) { |
|
||||
for (auto const& successor : productModel->getProduct().getTransitionMatrix().getRow(productChoice)) { |
|
||||
if (firstSuccessor) { |
|
||||
choiceSolution = getScaledSolution(getStateSolution(successorEpochSolution, successor.getColumn()), successor.getValue()); |
|
||||
firstSuccessor = false; |
|
||||
} else { |
|
||||
addScaledSolution(choiceSolution, getStateSolution(successorEpochSolution, successor.getColumn()), successor.getValue()); |
|
||||
} |
|
||||
} |
|
||||
} else { |
|
||||
for (auto const& successor : productModel->getProduct().getTransitionMatrix().getRow(productChoice)) { |
|
||||
uint64_t successorProductState = productModel->transformProductState(successor.getColumn(), successorEpochClass, memoryState); |
|
||||
SolutionType const& successorSolution = getStateSolution(successorEpochSolution, successorProductState); |
|
||||
if (firstSuccessor) { |
|
||||
choiceSolution = getScaledSolution(successorSolution, successor.getValue()); |
|
||||
firstSuccessor = false; |
|
||||
} else { |
|
||||
addScaledSolution(choiceSolution, successorSolution, successor.getValue()); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
assert(!firstSuccessor); |
|
||||
*stepSolIt = std::move(choiceSolution); |
|
||||
++stepSolIt; |
|
||||
} |
|
||||
|
|
||||
assert(epochModel.objectiveRewards.size() == objectives.size()); |
|
||||
assert(epochModel.objectiveRewardFilter.size() == objectives.size()); |
|
||||
assert(epochModel.epochMatrix.getRowCount() == epochModel.stepChoices.size()); |
|
||||
assert(epochModel.stepChoices.size() == epochModel.objectiveRewards.front().size()); |
|
||||
assert(epochModel.objectiveRewards.front().size() == epochModel.objectiveRewards.back().size()); |
|
||||
assert(epochModel.objectiveRewards.front().size() == epochModel.objectiveRewardFilter.front().size()); |
|
||||
assert(epochModel.objectiveRewards.back().size() == epochModel.objectiveRewardFilter.back().size()); |
|
||||
assert(epochModel.stepChoices.getNumberOfSetBits() == epochModel.stepSolutions.size()); |
|
||||
|
|
||||
currentEpoch = epoch; |
|
||||
/*
|
|
||||
std::cout << "Epoch model for epoch " << storm::utility::vector::toString(epoch) << std::endl; |
|
||||
std::cout << "Matrix: " << std::endl << epochModel.epochMatrix << std::endl; |
|
||||
std::cout << "ObjectiveRewards: " << storm::utility::vector::toString(epochModel.objectiveRewards[0]) << std::endl; |
|
||||
std::cout << "steps: " << epochModel.stepChoices << std::endl; |
|
||||
std::cout << "step solutions: "; |
|
||||
for (int i = 0; i < epochModel.stepSolutions.size(); ++i) { |
|
||||
std::cout << " " << epochModel.stepSolutions[i].weightedValue; |
|
||||
} |
|
||||
std::cout << std::endl; |
|
||||
*/ |
|
||||
return epochModel; |
|
||||
|
|
||||
} |
|
||||
|
|
||||
template<typename ValueType, bool SingleObjectiveMode> |
|
||||
void MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::setCurrentEpochClass(Epoch const& epoch) { |
|
||||
EpochClass epochClass = epochManager.getEpochClass(epoch); |
|
||||
// std::cout << "Setting epoch class for epoch " << epochManager.toString(epoch) << std::endl;
|
|
||||
auto productObjectiveRewards = productModel->computeObjectiveRewards(epochClass, objectives); |
|
||||
|
|
||||
storm::storage::BitVector stepChoices(productModel->getProduct().getNumberOfChoices(), false); |
|
||||
uint64_t choice = 0; |
|
||||
for (auto const& step : productModel->getSteps()) { |
|
||||
if (!epochManager.isZeroEpoch(step) && epochManager.getSuccessorEpoch(epoch, step) != epoch) { |
|
||||
stepChoices.set(choice, true); |
|
||||
} |
|
||||
++choice; |
|
||||
} |
|
||||
epochModel.epochMatrix = productModel->getProduct().getTransitionMatrix().filterEntries(~stepChoices); |
|
||||
// redirect transitions for the case where the lower reward bounds are not met yet
|
|
||||
storm::storage::BitVector violatedLowerBoundedDimensions(dimensions.size(), false); |
|
||||
for (uint64_t dim = 0; dim < dimensions.size(); ++dim) { |
|
||||
if (!dimensions[dim].isUpperBounded && !epochManager.isBottomDimensionEpochClass(epochClass, dim)) { |
|
||||
violatedLowerBoundedDimensions.set(dim); |
|
||||
} |
|
||||
} |
|
||||
if (!violatedLowerBoundedDimensions.empty()) { |
|
||||
for (uint64_t state = 0; state < epochModel.epochMatrix.getRowGroupCount(); ++state) { |
|
||||
auto const& memoryState = productModel->getMemoryState(state); |
|
||||
for (auto& entry : epochModel.epochMatrix.getRowGroup(state)) { |
|
||||
entry.setColumn(productModel->transformProductState(entry.getColumn(), epochClass, memoryState)); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
storm::storage::BitVector zeroObjRewardChoices(productModel->getProduct().getNumberOfChoices(), true); |
|
||||
for (uint64_t objIndex = 0; objIndex < objectives.size(); ++objIndex) { |
|
||||
if (violatedLowerBoundedDimensions.isDisjointFrom(objectiveDimensions[objIndex])) { |
|
||||
zeroObjRewardChoices &= storm::utility::vector::filterZero(productObjectiveRewards[objIndex]); |
|
||||
} |
|
||||
} |
|
||||
storm::storage::BitVector allProductStates(productModel->getProduct().getNumberOfStates(), true); |
|
||||
|
|
||||
// Get the relevant states for this epoch.
|
|
||||
storm::storage::BitVector productInStates = productModel->getInStates(epochClass); |
|
||||
// The epoch model only needs to consider the states that are reachable from a relevant state
|
|
||||
storm::storage::BitVector consideredStates = storm::utility::graph::getReachableStates(epochModel.epochMatrix, productInStates, allProductStates, ~allProductStates); |
|
||||
// std::cout << "numInStates = " << productInStates.getNumberOfSetBits() << std::endl;
|
|
||||
// std::cout << "numConsideredStates = " << consideredStates.getNumberOfSetBits() << std::endl;
|
|
||||
|
|
||||
// We assume that there is no end component in which objective reward is earned
|
|
||||
STORM_LOG_ASSERT(!storm::utility::graph::checkIfECWithChoiceExists(epochModel.epochMatrix, epochModel.epochMatrix.transpose(true), allProductStates, ~zeroObjRewardChoices & ~stepChoices), "There is a scheduler that yields infinite reward for one objective. This case should be excluded"); |
|
||||
auto ecElimResult = storm::transformer::EndComponentEliminator<ValueType>::transform(epochModel.epochMatrix, consideredStates, zeroObjRewardChoices & ~stepChoices, consideredStates); |
|
||||
epochModel.epochMatrix = std::move(ecElimResult.matrix); |
|
||||
epochModelToProductChoiceMap = std::move(ecElimResult.newToOldRowMapping); |
|
||||
|
|
||||
epochModel.stepChoices = storm::storage::BitVector(epochModel.epochMatrix.getRowCount(), false); |
|
||||
for (uint64_t choice = 0; choice < epochModel.epochMatrix.getRowCount(); ++choice) { |
|
||||
if (stepChoices.get(epochModelToProductChoiceMap[choice])) { |
|
||||
epochModel.stepChoices.set(choice, true); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
epochModel.objectiveRewards.clear(); |
|
||||
for (uint64_t objIndex = 0; objIndex < objectives.size(); ++objIndex) { |
|
||||
std::vector<ValueType> const& productObjRew = productObjectiveRewards[objIndex]; |
|
||||
std::vector<ValueType> reducedModelObjRewards; |
|
||||
reducedModelObjRewards.reserve(epochModel.epochMatrix.getRowCount()); |
|
||||
for (auto const& productChoice : epochModelToProductChoiceMap) { |
|
||||
reducedModelObjRewards.push_back(productObjRew[productChoice]); |
|
||||
} |
|
||||
// Check if the objective is violated in the current epoch
|
|
||||
if (!violatedLowerBoundedDimensions.isDisjointFrom(objectiveDimensions[objIndex])) { |
|
||||
storm::utility::vector::setVectorValues(reducedModelObjRewards, ~epochModel.stepChoices, storm::utility::zero<ValueType>()); |
|
||||
} |
|
||||
epochModel.objectiveRewards.push_back(std::move(reducedModelObjRewards)); |
|
||||
} |
|
||||
|
|
||||
epochModel.epochInStates = storm::storage::BitVector(epochModel.epochMatrix.getRowGroupCount(), false); |
|
||||
for (auto const& productState : productInStates) { |
|
||||
STORM_LOG_ASSERT(ecElimResult.oldToNewStateMapping[productState] < epochModel.epochMatrix.getRowGroupCount(), "Selected product state does not exist in the epoch model."); |
|
||||
epochModel.epochInStates.set(ecElimResult.oldToNewStateMapping[productState], true); |
|
||||
} |
|
||||
|
|
||||
std::vector<uint64_t> toEpochModelInStatesMap(productModel->getProduct().getNumberOfStates(), std::numeric_limits<uint64_t>::max()); |
|
||||
std::vector<uint64_t> epochModelStateToInStateMap = epochModel.epochInStates.getNumberOfSetBitsBeforeIndices(); |
|
||||
for (auto const& productState : productInStates) { |
|
||||
toEpochModelInStatesMap[productState] = epochModelStateToInStateMap[ecElimResult.oldToNewStateMapping[productState]]; |
|
||||
} |
|
||||
productStateToEpochModelInStateMap = std::make_shared<std::vector<uint64_t> const>(std::move(toEpochModelInStatesMap)); |
|
||||
|
|
||||
epochModel.objectiveRewardFilter.clear(); |
|
||||
for (auto const& objRewards : epochModel.objectiveRewards) { |
|
||||
epochModel.objectiveRewardFilter.push_back(storm::utility::vector::filterZero(objRewards)); |
|
||||
epochModel.objectiveRewardFilter.back().complement(); |
|
||||
} |
|
||||
|
|
||||
epochModelSizes.push_back(epochModel.epochMatrix.getRowGroupCount()); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
template<typename ValueType, bool SingleObjectiveMode> |
|
||||
template<bool SO, typename std::enable_if<SO, int>::type> |
|
||||
typename MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::SolutionType MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::getScaledSolution(SolutionType const& solution, ValueType const& scalingFactor) const { |
|
||||
return solution * scalingFactor; |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType, bool SingleObjectiveMode> |
|
||||
template<bool SO, typename std::enable_if<!SO, int>::type> |
|
||||
typename MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::SolutionType MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::getScaledSolution(SolutionType const& solution, ValueType const& scalingFactor) const { |
|
||||
SolutionType res; |
|
||||
res.reserve(solution.size()); |
|
||||
for (auto const& sol : solution) { |
|
||||
res.push_back(sol * scalingFactor); |
|
||||
} |
|
||||
return res; |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType, bool SingleObjectiveMode> |
|
||||
template<bool SO, typename std::enable_if<SO, int>::type> |
|
||||
void MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::addScaledSolution(SolutionType& solution, SolutionType const& solutionToAdd, ValueType const& scalingFactor) const { |
|
||||
solution += solutionToAdd * scalingFactor; |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType, bool SingleObjectiveMode> |
|
||||
template<bool SO, typename std::enable_if<!SO, int>::type> |
|
||||
void MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::addScaledSolution(SolutionType& solution, SolutionType const& solutionToAdd, ValueType const& scalingFactor) const { |
|
||||
storm::utility::vector::addScaledVector(solution, solutionToAdd, scalingFactor); |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType, bool SingleObjectiveMode> |
|
||||
template<bool SO, typename std::enable_if<SO, int>::type> |
|
||||
std::string MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::solutionToString(SolutionType const& solution) const { |
|
||||
std::stringstream stringstream; |
|
||||
stringstream << solution; |
|
||||
return stringstream.str(); |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType, bool SingleObjectiveMode> |
|
||||
template<bool SO, typename std::enable_if<!SO, int>::type> |
|
||||
std::string MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::solutionToString(SolutionType const& solution) const { |
|
||||
std::stringstream stringstream; |
|
||||
stringstream << "("; |
|
||||
bool first = true; |
|
||||
for (auto const& s : solution) { |
|
||||
if (first) { |
|
||||
first = false; |
|
||||
} else { |
|
||||
stringstream << ", "; |
|
||||
} |
|
||||
stringstream << s; |
|
||||
} |
|
||||
stringstream << ")"; |
|
||||
return stringstream.str(); |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType, bool SingleObjectiveMode> |
|
||||
ValueType MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::getRequiredEpochModelPrecision(Epoch const& startEpoch, ValueType const& precision) { |
|
||||
// if (storm::settings::getModule<storm::settings::modules::GeneralSettings>().isSoundSet()) {
|
|
||||
uint64_t sumOfDimensions = 0; |
|
||||
for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) { |
|
||||
sumOfDimensions += epochManager.getDimensionOfEpoch(startEpoch, dim) + 1; |
|
||||
} |
|
||||
return precision / storm::utility::convertNumber<ValueType>(sumOfDimensions); |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType, bool SingleObjectiveMode> |
|
||||
void MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::setSolutionForCurrentEpoch(std::vector<SolutionType>&& inStateSolutions) { |
|
||||
STORM_LOG_ASSERT(currentEpoch, "Tried to set a solution for the current epoch, but no epoch was specified before."); |
|
||||
STORM_LOG_ASSERT(inStateSolutions.size() == epochModel.epochInStates.getNumberOfSetBits(), "Invalid number of solutions."); |
|
||||
|
|
||||
std::set<Epoch> predecessorEpochs, successorEpochs; |
|
||||
for (auto const& step : possibleEpochSteps) { |
|
||||
epochManager.gatherPredecessorEpochs(predecessorEpochs, currentEpoch.get(), step); |
|
||||
successorEpochs.insert(epochManager.getSuccessorEpoch(currentEpoch.get(), step)); |
|
||||
} |
|
||||
predecessorEpochs.erase(currentEpoch.get()); |
|
||||
successorEpochs.erase(currentEpoch.get()); |
|
||||
STORM_LOG_ASSERT(!predecessorEpochs.empty(), "There are no predecessors for the epoch " << epochManager.toString(currentEpoch.get())); |
|
||||
|
|
||||
// clean up solutions that are not needed anymore
|
|
||||
for (auto const& successorEpoch : successorEpochs) { |
|
||||
auto successorEpochSolutionIt = epochSolutions.find(successorEpoch); |
|
||||
STORM_LOG_ASSERT(successorEpochSolutionIt != epochSolutions.end(), "Solution for successor epoch does not exist (anymore)."); |
|
||||
--successorEpochSolutionIt->second.count; |
|
||||
if (successorEpochSolutionIt->second.count == 0) { |
|
||||
epochSolutions.erase(successorEpochSolutionIt); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// add the new solution
|
|
||||
EpochSolution solution; |
|
||||
solution.count = predecessorEpochs.size(); |
|
||||
solution.productStateToSolutionVectorMap = productStateToEpochModelInStateMap; |
|
||||
solution.solutions = std::move(inStateSolutions); |
|
||||
epochSolutions[currentEpoch.get()] = std::move(solution); |
|
||||
|
|
||||
maxSolutionsStored = std::max((uint64_t) epochSolutions.size(), maxSolutionsStored); |
|
||||
|
|
||||
} |
|
||||
|
|
||||
template<typename ValueType, bool SingleObjectiveMode> |
|
||||
typename MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::SolutionType const& MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::getStateSolution(Epoch const& epoch, uint64_t const& productState) { |
|
||||
auto epochSolutionIt = epochSolutions.find(epoch); |
|
||||
STORM_LOG_ASSERT(epochSolutionIt != epochSolutions.end(), "Requested unexisting solution for epoch " << epochManager.toString(epoch) << "."); |
|
||||
auto const& epochSolution = epochSolutionIt->second; |
|
||||
STORM_LOG_ASSERT(productState < epochSolution.productStateToSolutionVectorMap->size(), "Requested solution for epoch " << epochManager.toString(epoch) << " at an unexisting product state."); |
|
||||
STORM_LOG_ASSERT((*epochSolution.productStateToSolutionVectorMap)[productState] < epochSolution.solutions.size(), "Requested solution for epoch " << epochManager.toString(epoch) << " at a state for which no solution was stored."); |
|
||||
return epochSolution.solutions[(*epochSolution.productStateToSolutionVectorMap)[productState]]; |
|
||||
} |
|
||||
template<typename ValueType, bool SingleObjectiveMode> |
|
||||
typename MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::EpochSolution const& MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::getEpochSolution(std::map<Epoch, EpochSolution const*> const& solutions, Epoch const& epoch) { |
|
||||
auto epochSolutionIt = solutions.find(epoch); |
|
||||
STORM_LOG_ASSERT(epochSolutionIt != solutions.end(), "Requested unexisting solution for epoch " << epochManager.toString(epoch) << "."); |
|
||||
return *epochSolutionIt->second; |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType, bool SingleObjectiveMode> |
|
||||
typename MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::SolutionType const& MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::getStateSolution(EpochSolution const& epochSolution, uint64_t const& productState) { |
|
||||
STORM_LOG_ASSERT(productState < epochSolution.productStateToSolutionVectorMap->size(), "Requested solution at an unexisting product state."); |
|
||||
STORM_LOG_ASSERT((*epochSolution.productStateToSolutionVectorMap)[productState] < epochSolution.solutions.size(), "Requested solution for epoch at a state for which no solution was stored."); |
|
||||
return epochSolution.solutions[(*epochSolution.productStateToSolutionVectorMap)[productState]]; |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType, bool SingleObjectiveMode> |
|
||||
typename MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::SolutionType const& MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::getInitialStateResult(Epoch const& epoch) { |
|
||||
STORM_LOG_ASSERT(model.getInitialStates().getNumberOfSetBits() == 1, "The model has multiple initial states."); |
|
||||
STORM_LOG_ASSERT(!epochManager.hasBottomDimension(epoch), "Tried to get the initial state result in an epoch that still contains at least one bottom dimension."); |
|
||||
return getStateSolution(epoch, productModel->getInitialProductState(*model.getInitialStates().begin(), model.getInitialStates())); |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType, bool SingleObjectiveMode> |
|
||||
typename MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::SolutionType const& MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::getInitialStateResult(Epoch const& epoch, uint64_t initialStateIndex) { |
|
||||
STORM_LOG_ASSERT(model.getInitialStates().get(initialStateIndex), "The given model state is not an initial state."); |
|
||||
STORM_LOG_ASSERT(!epochManager.hasBottomDimension(epoch), "Tried to get the initial state result in an epoch that still contains at least one bottom dimension."); |
|
||||
return getStateSolution(epoch, productModel->getInitialProductState(initialStateIndex, model.getInitialStates())); |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType, bool SingleObjectiveMode> |
|
||||
EpochManager const& MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::getEpochManager() const { |
|
||||
return epochManager; |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType, bool SingleObjectiveMode> |
|
||||
Dimension<ValueType> const& MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::getDimension(uint64_t dim) const { |
|
||||
return dimensions.at(dim); |
|
||||
} |
|
||||
|
|
||||
template class MultiDimensionalRewardUnfolding<double, true>; |
|
||||
template class MultiDimensionalRewardUnfolding<double, false>; |
|
||||
template class MultiDimensionalRewardUnfolding<storm::RationalNumber, true>; |
|
||||
template class MultiDimensionalRewardUnfolding<storm::RationalNumber, false>; |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,127 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include <boost/optional.hpp> |
|
||||
|
|
||||
#include "storm/storage/BitVector.h" |
|
||||
#include "storm/storage/SparseMatrix.h" |
|
||||
#include "storm/modelchecker/multiobjective/Objective.h" |
|
||||
#include "storm/modelchecker/multiobjective/rewardbounded/EpochManager.h" |
|
||||
#include "storm/modelchecker/multiobjective/rewardbounded/ProductModel.h" |
|
||||
#include "storm/modelchecker/multiobjective/rewardbounded/Dimension.h" |
|
||||
#include "storm/models/sparse/Mdp.h" |
|
||||
#include "storm/utility/vector.h" |
|
||||
#include "storm/storage/memorystructure/MemoryStructure.h" |
|
||||
#include "storm/utility/Stopwatch.h" |
|
||||
|
|
||||
namespace storm { |
|
||||
namespace modelchecker { |
|
||||
namespace multiobjective { |
|
||||
|
|
||||
|
|
||||
template<typename ValueType, bool SingleObjectiveMode> |
|
||||
class MultiDimensionalRewardUnfolding { |
|
||||
public: |
|
||||
|
|
||||
typedef typename EpochManager::Epoch Epoch; // The number of reward steps that are "left" for each dimension |
|
||||
typedef typename EpochManager::EpochClass EpochClass; |
|
||||
|
|
||||
typedef typename std::conditional<SingleObjectiveMode, ValueType, std::vector<ValueType>>::type SolutionType; |
|
||||
|
|
||||
struct EpochModel { |
|
||||
bool epochMatrixChanged; |
|
||||
storm::storage::SparseMatrix<ValueType> epochMatrix; |
|
||||
storm::storage::BitVector stepChoices; |
|
||||
std::vector<SolutionType> stepSolutions; |
|
||||
std::vector<std::vector<ValueType>> objectiveRewards; |
|
||||
std::vector<storm::storage::BitVector> objectiveRewardFilter; |
|
||||
storm::storage::BitVector epochInStates; |
|
||||
}; |
|
||||
|
|
||||
/* |
|
||||
* |
|
||||
* @param model The (preprocessed) model |
|
||||
* @param objectives The (preprocessed) objectives |
|
||||
* |
|
||||
*/ |
|
||||
MultiDimensionalRewardUnfolding(storm::models::sparse::Mdp<ValueType> const& model, std::vector<storm::modelchecker::multiobjective::Objective<ValueType>> const& objectives); |
|
||||
MultiDimensionalRewardUnfolding(storm::models::sparse::Mdp<ValueType> const& model, std::shared_ptr<storm::logic::OperatorFormula const> objectiveFormula); |
|
||||
|
|
||||
~MultiDimensionalRewardUnfolding() = default; |
|
||||
|
|
||||
Epoch getStartEpoch(); |
|
||||
std::vector<Epoch> getEpochComputationOrder(Epoch const& startEpoch); |
|
||||
|
|
||||
EpochModel& setCurrentEpoch(Epoch const& epoch); |
|
||||
|
|
||||
/*! |
|
||||
* Returns the precision required for the analyzis of each epoch model in order to achieve the given overall precision |
|
||||
*/ |
|
||||
ValueType getRequiredEpochModelPrecision(Epoch const& startEpoch, ValueType const& precision); |
|
||||
|
|
||||
void setSolutionForCurrentEpoch(std::vector<SolutionType>&& inStateSolutions); |
|
||||
SolutionType const& getInitialStateResult(Epoch const& epoch); // Assumes that the initial state is unique |
|
||||
SolutionType const& getInitialStateResult(Epoch const& epoch, uint64_t initialStateIndex); |
|
||||
|
|
||||
EpochManager const& getEpochManager() const; |
|
||||
Dimension<ValueType> const& getDimension(uint64_t dim) const; |
|
||||
|
|
||||
private: |
|
||||
|
|
||||
void setCurrentEpochClass(Epoch const& epoch); |
|
||||
void initialize(); |
|
||||
|
|
||||
void initializeObjectives(std::vector<Epoch>& epochSteps); |
|
||||
void computeMaxDimensionValues(); |
|
||||
|
|
||||
void initializeMemoryProduct(std::vector<Epoch> const& epochSteps); |
|
||||
|
|
||||
template<bool SO = SingleObjectiveMode, typename std::enable_if<SO, int>::type = 0> |
|
||||
SolutionType getScaledSolution(SolutionType const& solution, ValueType const& scalingFactor) const; |
|
||||
template<bool SO = SingleObjectiveMode, typename std::enable_if<!SO, int>::type = 0> |
|
||||
SolutionType getScaledSolution(SolutionType const& solution, ValueType const& scalingFactor) const; |
|
||||
|
|
||||
template<bool SO = SingleObjectiveMode, typename std::enable_if<SO, int>::type = 0> |
|
||||
void addScaledSolution(SolutionType& solution, SolutionType const& solutionToAdd, ValueType const& scalingFactor) const; |
|
||||
template<bool SO = SingleObjectiveMode, typename std::enable_if<!SO, int>::type = 0> |
|
||||
void addScaledSolution(SolutionType& solution, SolutionType const& solutionToAdd, ValueType const& scalingFactor) const; |
|
||||
|
|
||||
template<bool SO = SingleObjectiveMode, typename std::enable_if<SO, int>::type = 0> |
|
||||
std::string solutionToString(SolutionType const& solution) const; |
|
||||
template<bool SO = SingleObjectiveMode, typename std::enable_if<!SO, int>::type = 0> |
|
||||
std::string solutionToString(SolutionType const& solution) const; |
|
||||
|
|
||||
SolutionType const& getStateSolution(Epoch const& epoch, uint64_t const& productState); |
|
||||
struct EpochSolution { |
|
||||
uint64_t count; |
|
||||
std::shared_ptr<std::vector<uint64_t> const> productStateToSolutionVectorMap; |
|
||||
std::vector<SolutionType> solutions; |
|
||||
}; |
|
||||
std::map<Epoch, EpochSolution> epochSolutions; |
|
||||
EpochSolution const& getEpochSolution(std::map<Epoch, EpochSolution const*> const& solutions, Epoch const& epoch); |
|
||||
SolutionType const& getStateSolution(EpochSolution const& epochSolution, uint64_t const& productState); |
|
||||
|
|
||||
storm::models::sparse::Mdp<ValueType> const& model; |
|
||||
std::vector<storm::modelchecker::multiobjective::Objective<ValueType>> objectives; |
|
||||
|
|
||||
std::unique_ptr<ProductModel<ValueType>> productModel; |
|
||||
|
|
||||
std::vector<uint64_t> epochModelToProductChoiceMap; |
|
||||
std::shared_ptr<std::vector<uint64_t> const> productStateToEpochModelInStateMap; |
|
||||
std::set<Epoch> possibleEpochSteps; |
|
||||
|
|
||||
EpochModel epochModel; |
|
||||
boost::optional<Epoch> currentEpoch; |
|
||||
|
|
||||
EpochManager epochManager; |
|
||||
|
|
||||
std::vector<Dimension<ValueType>> dimensions; |
|
||||
std::vector<storm::storage::BitVector> objectiveDimensions; |
|
||||
|
|
||||
|
|
||||
storm::utility::Stopwatch swInit, swFindSol, swInsertSol, swSetEpoch, swSetEpochClass, swAux1, swAux2, swAux3, swAux4; |
|
||||
std::vector<uint64_t> epochModelSizes; |
|
||||
uint64_t maxSolutionsStored; |
|
||||
}; |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,608 +0,0 @@ |
|||||
#include "storm/modelchecker/multiobjective/rewardbounded/ProductModel.h"
|
|
||||
|
|
||||
|
|
||||
#include "storm/utility/macros.h"
|
|
||||
#include "storm/logic/Formulas.h"
|
|
||||
#include "storm/logic/CloneVisitor.h"
|
|
||||
#include "storm/storage/memorystructure/SparseModelMemoryProduct.h"
|
|
||||
#include "storm/storage/memorystructure/MemoryStructureBuilder.h"
|
|
||||
|
|
||||
#include "storm/modelchecker/propositional/SparsePropositionalModelChecker.h"
|
|
||||
#include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h"
|
|
||||
|
|
||||
#include "storm/exceptions/UnexpectedException.h"
|
|
||||
#include "storm/exceptions/NotSupportedException.h"
|
|
||||
|
|
||||
namespace storm { |
|
||||
namespace modelchecker { |
|
||||
namespace multiobjective { |
|
||||
|
|
||||
template<typename ValueType> |
|
||||
ProductModel<ValueType>::ProductModel(storm::models::sparse::Mdp<ValueType> const& model, std::vector<storm::modelchecker::multiobjective::Objective<ValueType>> const& objectives, std::vector<Dimension<ValueType>> const& dimensions, std::vector<storm::storage::BitVector> const& objectiveDimensions, EpochManager const& epochManager, std::vector<Epoch> const& originalModelSteps) : dimensions(dimensions), objectiveDimensions(objectiveDimensions), epochManager(epochManager), memoryStateManager(dimensions.size()) { |
|
||||
|
|
||||
for (uint64_t dim = 0; dim < dimensions.size(); ++dim) { |
|
||||
if (!dimensions[dim].memoryLabel) { |
|
||||
memoryStateManager.setDimensionWithoutMemory(dim); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
storm::storage::MemoryStructure memory = computeMemoryStructure(model, objectives); |
|
||||
assert(memoryStateManager.getMemoryStateCount() == memory.getNumberOfStates()); |
|
||||
std::vector<MemoryState> memoryStateMap = computeMemoryStateMap(memory); |
|
||||
|
|
||||
storm::storage::SparseModelMemoryProduct<ValueType> productBuilder(memory.product(model)); |
|
||||
|
|
||||
setReachableProductStates(productBuilder, originalModelSteps, memoryStateMap); |
|
||||
product = productBuilder.build()->template as<storm::models::sparse::Mdp<ValueType>>(); |
|
||||
|
|
||||
uint64_t numModelStates = productBuilder.getOriginalModel().getNumberOfStates(); |
|
||||
MemoryState upperMemStateBound = memoryStateManager.getUpperMemoryStateBound(); |
|
||||
uint64_t numMemoryStates = memoryStateManager.getMemoryStateCount(); |
|
||||
uint64_t numProductStates = getProduct().getNumberOfStates(); |
|
||||
|
|
||||
// Compute a mappings from product states to model/memory states and back
|
|
||||
modelMemoryToProductStateMap.resize(upperMemStateBound * numModelStates, std::numeric_limits<uint64_t>::max()); |
|
||||
productToModelStateMap.resize(numProductStates, std::numeric_limits<uint64_t>::max()); |
|
||||
productToMemoryStateMap.resize(numProductStates, std::numeric_limits<uint64_t>::max()); |
|
||||
for (uint64_t modelState = 0; modelState < numModelStates; ++modelState) { |
|
||||
for (uint64_t memoryStateIndex = 0; memoryStateIndex < numMemoryStates; ++memoryStateIndex) { |
|
||||
if (productBuilder.isStateReachable(modelState, memoryStateIndex)) { |
|
||||
uint64_t productState = productBuilder.getResultState(modelState, memoryStateIndex); |
|
||||
modelMemoryToProductStateMap[modelState * upperMemStateBound + memoryStateMap[memoryStateIndex]] = productState; |
|
||||
productToModelStateMap[productState] = modelState; |
|
||||
productToMemoryStateMap[productState] = memoryStateMap[memoryStateIndex]; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// Map choice indices of the product to the state where it origins
|
|
||||
choiceToStateMap.reserve(getProduct().getNumberOfChoices()); |
|
||||
for (uint64_t productState = 0; productState < numProductStates; ++productState) { |
|
||||
uint64_t groupSize = getProduct().getTransitionMatrix().getRowGroupSize(productState); |
|
||||
for (uint64_t i = 0; i < groupSize; ++i) { |
|
||||
choiceToStateMap.push_back(productState); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// Compute the epoch steps for the product
|
|
||||
steps.resize(getProduct().getNumberOfChoices(), 0); |
|
||||
for (uint64_t modelState = 0; modelState < numModelStates; ++modelState) { |
|
||||
uint64_t numChoices = productBuilder.getOriginalModel().getTransitionMatrix().getRowGroupSize(modelState); |
|
||||
uint64_t firstChoice = productBuilder.getOriginalModel().getTransitionMatrix().getRowGroupIndices()[modelState]; |
|
||||
for (uint64_t choiceOffset = 0; choiceOffset < numChoices; ++choiceOffset) { |
|
||||
Epoch const& step = originalModelSteps[firstChoice + choiceOffset]; |
|
||||
if (step != 0) { |
|
||||
for (MemoryState const& memoryState : memoryStateMap) { |
|
||||
if (productStateExists(modelState, memoryState)) { |
|
||||
uint64_t productState = getProductState(modelState, memoryState); |
|
||||
uint64_t productChoice = getProduct().getTransitionMatrix().getRowGroupIndices()[productState] + choiceOffset; |
|
||||
assert(productChoice < getProduct().getTransitionMatrix().getRowGroupIndices()[productState + 1]); |
|
||||
steps[productChoice] = step; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// getProduct().writeDotToStream(std::cout);
|
|
||||
|
|
||||
computeReachableStatesInEpochClasses(); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
template<typename ValueType> |
|
||||
storm::storage::MemoryStructure ProductModel<ValueType>::computeMemoryStructure(storm::models::sparse::Mdp<ValueType> const& model, std::vector<storm::modelchecker::multiobjective::Objective<ValueType>> const& objectives) const { |
|
||||
|
|
||||
storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Mdp<ValueType>> mc(model); |
|
||||
|
|
||||
// Create a memory structure that remembers whether (sub)objectives are satisfied
|
|
||||
storm::storage::MemoryStructure memory = storm::storage::MemoryStructureBuilder<ValueType>::buildTrivialMemoryStructure(model); |
|
||||
for (uint64_t objIndex = 0; objIndex < objectives.size(); ++objIndex) { |
|
||||
if (!objectives[objIndex].formula->isProbabilityOperatorFormula()) { |
|
||||
continue; |
|
||||
} |
|
||||
|
|
||||
std::vector<uint64_t> dimensionIndexMap; |
|
||||
for (auto const& globalDimensionIndex : objectiveDimensions[objIndex]) { |
|
||||
dimensionIndexMap.push_back(globalDimensionIndex); |
|
||||
} |
|
||||
|
|
||||
bool objectiveContainsLowerBound = false; |
|
||||
for (auto const& globalDimensionIndex : objectiveDimensions[objIndex]) { |
|
||||
if (!dimensions[globalDimensionIndex].isUpperBounded) { |
|
||||
objectiveContainsLowerBound = true; |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// collect the memory states for this objective
|
|
||||
std::vector<storm::storage::BitVector> objMemStates; |
|
||||
storm::storage::BitVector m(dimensionIndexMap.size(), false); |
|
||||
for (; !m.full(); m.increment()) { |
|
||||
objMemStates.push_back(~m); |
|
||||
} |
|
||||
objMemStates.push_back(~m); |
|
||||
assert(objMemStates.size() == 1ull << dimensionIndexMap.size()); |
|
||||
|
|
||||
// build objective memory
|
|
||||
auto objMemoryBuilder = storm::storage::MemoryStructureBuilder<ValueType>(objMemStates.size(), model); |
|
||||
|
|
||||
// Get the set of states that for all subobjectives satisfy either the left or the right subformula
|
|
||||
storm::storage::BitVector constraintStates(model.getNumberOfStates(), true); |
|
||||
for (auto const& dim : objectiveDimensions[objIndex]) { |
|
||||
auto const& dimension = dimensions[dim]; |
|
||||
STORM_LOG_ASSERT(dimension.formula->isBoundedUntilFormula(), "Unexpected Formula type"); |
|
||||
constraintStates &= |
|
||||
(mc.check(dimension.formula->asBoundedUntilFormula().getLeftSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector() | |
|
||||
mc.check(dimension.formula->asBoundedUntilFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); |
|
||||
} |
|
||||
|
|
||||
// Build the transitions between the memory states
|
|
||||
for (uint64_t memState = 0; memState < objMemStates.size(); ++memState) { |
|
||||
auto const& memStateBV = objMemStates[memState]; |
|
||||
for (uint64_t memStatePrime = 0; memStatePrime < objMemStates.size(); ++memStatePrime) { |
|
||||
auto const& memStatePrimeBV = objMemStates[memStatePrime]; |
|
||||
if (memStatePrimeBV.isSubsetOf(memStateBV)) { |
|
||||
|
|
||||
std::shared_ptr<storm::logic::Formula const> transitionFormula = storm::logic::Formula::getTrueFormula(); |
|
||||
for (auto const& subObjIndex : memStateBV) { |
|
||||
std::shared_ptr<storm::logic::Formula const> subObjFormula = dimensions[dimensionIndexMap[subObjIndex]].formula->asBoundedUntilFormula().getRightSubformula().asSharedPointer(); |
|
||||
if (memStatePrimeBV.get(subObjIndex)) { |
|
||||
subObjFormula = std::make_shared<storm::logic::UnaryBooleanStateFormula>(storm::logic::UnaryBooleanStateFormula::OperatorType::Not, subObjFormula); |
|
||||
} |
|
||||
transitionFormula = std::make_shared<storm::logic::BinaryBooleanStateFormula>(storm::logic::BinaryBooleanStateFormula::OperatorType::And, transitionFormula, subObjFormula); |
|
||||
} |
|
||||
|
|
||||
storm::storage::BitVector transitionStates = mc.check(*transitionFormula)->asExplicitQualitativeCheckResult().getTruthValuesVector(); |
|
||||
if (memStatePrimeBV.empty()) { |
|
||||
transitionStates |= ~constraintStates; |
|
||||
} else { |
|
||||
transitionStates &= constraintStates; |
|
||||
} |
|
||||
objMemoryBuilder.setTransition(memState, memStatePrime, transitionStates); |
|
||||
|
|
||||
// Set the initial states
|
|
||||
if (memStateBV.full()) { |
|
||||
storm::storage::BitVector initialTransitionStates = model.getInitialStates() & transitionStates; |
|
||||
// At this point we can check whether there is an initial state that already satisfies all subObjectives.
|
|
||||
// Such a situation is not supported as we can not reduce this (easily) to an expected reward computation.
|
|
||||
STORM_LOG_THROW(!memStatePrimeBV.empty() || initialTransitionStates.empty() || objectiveContainsLowerBound || initialTransitionStates.isDisjointFrom(constraintStates), storm::exceptions::NotSupportedException, "The objective " << *objectives[objIndex].formula << " is already satisfied in an initial state. This special case is not supported."); |
|
||||
for (auto const& initState : initialTransitionStates) { |
|
||||
objMemoryBuilder.setInitialMemoryState(initState, memStatePrime); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// Build the memory labels
|
|
||||
for (uint64_t memState = 0; memState < objMemStates.size(); ++memState) { |
|
||||
auto const& memStateBV = objMemStates[memState]; |
|
||||
for (auto const& subObjIndex : memStateBV) { |
|
||||
objMemoryBuilder.setLabel(memState, dimensions[dimensionIndexMap[subObjIndex]].memoryLabel.get()); |
|
||||
} |
|
||||
} |
|
||||
auto objMemory = objMemoryBuilder.build(); |
|
||||
memory = memory.product(objMemory); |
|
||||
} |
|
||||
return memory; |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType> |
|
||||
std::vector<typename ProductModel<ValueType>::MemoryState> ProductModel<ValueType>::computeMemoryStateMap(storm::storage::MemoryStructure const& memory) const { |
|
||||
// Compute a mapping between the different representations of memory states
|
|
||||
std::vector<MemoryState> result; |
|
||||
result.reserve(memory.getNumberOfStates()); |
|
||||
for (uint64_t memStateIndex = 0; memStateIndex < memory.getNumberOfStates(); ++memStateIndex) { |
|
||||
MemoryState memState = memoryStateManager.getInitialMemoryState(); |
|
||||
std::set<std::string> stateLabels = memory.getStateLabeling().getLabelsOfState(memStateIndex); |
|
||||
for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) { |
|
||||
if (dimensions[dim].memoryLabel) { |
|
||||
if (stateLabels.find(dimensions[dim].memoryLabel.get()) != stateLabels.end()) { |
|
||||
memoryStateManager.setRelevantDimension(memState, dim, true); |
|
||||
} else { |
|
||||
memoryStateManager.setRelevantDimension(memState, dim, false); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
result.push_back(std::move(memState)); |
|
||||
} |
|
||||
return result; |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType> |
|
||||
void ProductModel<ValueType>::setReachableProductStates(storm::storage::SparseModelMemoryProduct<ValueType>& productBuilder, std::vector<Epoch> const& originalModelSteps, std::vector<MemoryState> const& memoryStateMap) const { |
|
||||
|
|
||||
std::vector<uint64_t> inverseMemoryStateMap(memoryStateManager.getUpperMemoryStateBound(), std::numeric_limits<uint64_t>::max()); |
|
||||
for (uint64_t memStateIndex = 0; memStateIndex < memoryStateMap.size(); ++memStateIndex) { |
|
||||
inverseMemoryStateMap[memoryStateMap[memStateIndex]] = memStateIndex; |
|
||||
} |
|
||||
|
|
||||
auto const& memory = productBuilder.getMemory(); |
|
||||
auto const& model = productBuilder.getOriginalModel(); |
|
||||
auto const& modelTransitions = model.getTransitionMatrix(); |
|
||||
|
|
||||
std::vector<storm::storage::BitVector> reachableProductStates(memoryStateManager.getUpperMemoryStateBound()); |
|
||||
for (auto const& memState : memoryStateMap) { |
|
||||
reachableProductStates[memState] = storm::storage::BitVector(model.getNumberOfStates(), false); |
|
||||
} |
|
||||
|
|
||||
// Initialize the reachable states with the initial states
|
|
||||
EpochClass initEpochClass = epochManager.getEpochClass(epochManager.getZeroEpoch()); |
|
||||
auto memStateIt = memory.getInitialMemoryStates().begin(); |
|
||||
for (auto const& initState : model.getInitialStates()) { |
|
||||
uint64_t transformedMemoryState = transformMemoryState(memoryStateMap[*memStateIt], initEpochClass, memoryStateManager.getInitialMemoryState()); |
|
||||
reachableProductStates[transformedMemoryState].set(initState, true); |
|
||||
++memStateIt; |
|
||||
} |
|
||||
assert(memStateIt == memory.getInitialMemoryStates().end()); |
|
||||
|
|
||||
// Find the reachable epoch classes
|
|
||||
std::set<Epoch> possibleSteps(originalModelSteps.begin(), originalModelSteps.end()); |
|
||||
std::set<EpochClass, std::function<bool(EpochClass const&, EpochClass const&)>> reachableEpochClasses(std::bind(&EpochManager::epochClassOrder, &epochManager, std::placeholders::_1, std::placeholders::_2)); |
|
||||
collectReachableEpochClasses(reachableEpochClasses, possibleSteps); |
|
||||
|
|
||||
|
|
||||
// Iterate over all epoch classes starting from the initial one (i.e., no bottom dimension).
|
|
||||
for (auto epochClassIt = reachableEpochClasses.rbegin(); epochClassIt != reachableEpochClasses.rend(); ++epochClassIt) { |
|
||||
auto const& epochClass = *epochClassIt; |
|
||||
|
|
||||
// Find the remaining set of reachable states via DFS.
|
|
||||
std::vector<std::pair<uint64_t, MemoryState>> dfsStack; |
|
||||
for (MemoryState const& memState : memoryStateMap) { |
|
||||
for (auto const& modelState : reachableProductStates[memState]) { |
|
||||
dfsStack.emplace_back(modelState, memState); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
while (!dfsStack.empty()) { |
|
||||
uint64_t currentModelState = dfsStack.back().first; |
|
||||
MemoryState currentMemoryState = dfsStack.back().second; |
|
||||
uint64_t currentMemoryStateIndex = inverseMemoryStateMap[currentMemoryState]; |
|
||||
dfsStack.pop_back(); |
|
||||
|
|
||||
for (uint64_t choice = modelTransitions.getRowGroupIndices()[currentModelState]; choice != modelTransitions.getRowGroupIndices()[currentModelState + 1]; ++choice) { |
|
||||
|
|
||||
for (auto transitionIt = modelTransitions.getRow(choice).begin(); transitionIt < modelTransitions.getRow(choice).end(); ++transitionIt) { |
|
||||
|
|
||||
MemoryState successorMemoryState = memoryStateMap[memory.getSuccessorMemoryState(currentMemoryStateIndex, transitionIt - modelTransitions.begin())]; |
|
||||
successorMemoryState = transformMemoryState(successorMemoryState, epochClass, currentMemoryState); |
|
||||
if (!reachableProductStates[successorMemoryState].get(transitionIt->getColumn())) { |
|
||||
reachableProductStates[successorMemoryState].set(transitionIt->getColumn(), true); |
|
||||
dfsStack.emplace_back(transitionIt->getColumn(), successorMemoryState); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
for (uint64_t memStateIndex = 0; memStateIndex < memoryStateManager.getMemoryStateCount(); ++memStateIndex) { |
|
||||
for (auto const& modelState : reachableProductStates[memoryStateMap[memStateIndex]]) { |
|
||||
productBuilder.addReachableState(modelState, memStateIndex); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType> |
|
||||
storm::models::sparse::Mdp<ValueType> const& ProductModel<ValueType>::getProduct() const { |
|
||||
return *product; |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType> |
|
||||
std::vector<typename ProductModel<ValueType>::Epoch> const& ProductModel<ValueType>::getSteps() const { |
|
||||
return steps; |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType> |
|
||||
bool ProductModel<ValueType>::productStateExists(uint64_t const& modelState, MemoryState const& memoryState) const { |
|
||||
return modelMemoryToProductStateMap[modelState * memoryStateManager.getUpperMemoryStateBound() + memoryState] < getProduct().getNumberOfStates(); |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType> |
|
||||
uint64_t ProductModel<ValueType>::getProductState(uint64_t const& modelState, MemoryState const& memoryState) const { |
|
||||
STORM_LOG_ASSERT(productStateExists(modelState, memoryState), "Tried to obtain a state in the model-memory-product that does not exist"); |
|
||||
return modelMemoryToProductStateMap[modelState * memoryStateManager.getUpperMemoryStateBound() + memoryState]; |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType> |
|
||||
uint64_t ProductModel<ValueType>::getInitialProductState(uint64_t const& initialModelState, storm::storage::BitVector const& initialModelStates) const { |
|
||||
auto productInitStateIt = getProduct().getInitialStates().begin(); |
|
||||
productInitStateIt += initialModelStates.getNumberOfSetBitsBeforeIndex(initialModelState); |
|
||||
STORM_LOG_ASSERT(getModelState(*productInitStateIt) == initialModelState, "Could not find the corresponding initial state in the product model."); |
|
||||
return transformProductState(*productInitStateIt, epochManager.getEpochClass(epochManager.getZeroEpoch()), memoryStateManager.getInitialMemoryState()); |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType> |
|
||||
uint64_t ProductModel<ValueType>::getModelState(uint64_t const& productState) const { |
|
||||
return productToModelStateMap[productState]; |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType> |
|
||||
typename ProductModel<ValueType>::MemoryState ProductModel<ValueType>::getMemoryState(uint64_t const& productState) const { |
|
||||
return productToMemoryStateMap[productState]; |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType> |
|
||||
MemoryStateManager const& ProductModel<ValueType>::getMemoryStateManager() const { |
|
||||
return memoryStateManager; |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType> |
|
||||
uint64_t ProductModel<ValueType>::getProductStateFromChoice(uint64_t const& productChoice) const { |
|
||||
return choiceToStateMap[productChoice]; |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType> |
|
||||
std::vector<std::vector<ValueType>> ProductModel<ValueType>::computeObjectiveRewards(EpochClass const& epochClass, std::vector<storm::modelchecker::multiobjective::Objective<ValueType>> const& objectives) const { |
|
||||
std::vector<std::vector<ValueType>> objectiveRewards; |
|
||||
objectiveRewards.reserve(objectives.size()); |
|
||||
|
|
||||
for (uint64_t objIndex = 0; objIndex < objectives.size(); ++objIndex) { |
|
||||
auto const& formula = *objectives[objIndex].formula; |
|
||||
if (formula.isProbabilityOperatorFormula()) { |
|
||||
storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Mdp<ValueType>> mc(getProduct()); |
|
||||
std::vector<uint64_t> dimensionIndexMap; |
|
||||
for (auto const& globalDimensionIndex : objectiveDimensions[objIndex]) { |
|
||||
dimensionIndexMap.push_back(globalDimensionIndex); |
|
||||
} |
|
||||
|
|
||||
std::shared_ptr<storm::logic::Formula const> sinkStatesFormula; |
|
||||
for (auto const& dim : objectiveDimensions[objIndex]) { |
|
||||
auto memLabelFormula = std::make_shared<storm::logic::AtomicLabelFormula>(dimensions[dim].memoryLabel.get()); |
|
||||
if (sinkStatesFormula) { |
|
||||
sinkStatesFormula = std::make_shared<storm::logic::BinaryBooleanStateFormula>(storm::logic::BinaryBooleanStateFormula::OperatorType::Or, sinkStatesFormula, memLabelFormula); |
|
||||
} else { |
|
||||
sinkStatesFormula = memLabelFormula; |
|
||||
} |
|
||||
} |
|
||||
sinkStatesFormula = std::make_shared<storm::logic::UnaryBooleanStateFormula>(storm::logic::UnaryBooleanStateFormula::OperatorType::Not, sinkStatesFormula); |
|
||||
|
|
||||
std::vector<ValueType> objRew(getProduct().getTransitionMatrix().getRowCount(), storm::utility::zero<ValueType>()); |
|
||||
storm::storage::BitVector relevantObjectives(objectiveDimensions[objIndex].getNumberOfSetBits()); |
|
||||
|
|
||||
while (!relevantObjectives.full()) { |
|
||||
relevantObjectives.increment(); |
|
||||
|
|
||||
// find out whether objective reward should be earned within this epoch class
|
|
||||
bool collectRewardInEpoch = true; |
|
||||
for (auto const& subObjIndex : relevantObjectives) { |
|
||||
if (dimensions[dimensionIndexMap[subObjIndex]].isUpperBounded && epochManager.isBottomDimensionEpochClass(epochClass, dimensionIndexMap[subObjIndex])) { |
|
||||
collectRewardInEpoch = false; |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (collectRewardInEpoch) { |
|
||||
std::shared_ptr<storm::logic::Formula const> relevantStatesFormula; |
|
||||
std::shared_ptr<storm::logic::Formula const> goalStatesFormula = storm::logic::CloneVisitor().clone(*sinkStatesFormula); |
|
||||
for (uint64_t subObjIndex = 0; subObjIndex < dimensionIndexMap.size(); ++subObjIndex) { |
|
||||
std::shared_ptr<storm::logic::Formula> memLabelFormula = std::make_shared<storm::logic::AtomicLabelFormula>(dimensions[dimensionIndexMap[subObjIndex]].memoryLabel.get()); |
|
||||
if (relevantObjectives.get(subObjIndex)) { |
|
||||
auto rightSubFormula = dimensions[dimensionIndexMap[subObjIndex]].formula->asBoundedUntilFormula().getRightSubformula().asSharedPointer(); |
|
||||
goalStatesFormula = std::make_shared<storm::logic::BinaryBooleanStateFormula>(storm::logic::BinaryBooleanStateFormula::OperatorType::And, goalStatesFormula, rightSubFormula); |
|
||||
} else { |
|
||||
memLabelFormula = std::make_shared<storm::logic::UnaryBooleanStateFormula>(storm::logic::UnaryBooleanStateFormula::OperatorType::Not, memLabelFormula); |
|
||||
} |
|
||||
if (relevantStatesFormula) { |
|
||||
relevantStatesFormula = std::make_shared<storm::logic::BinaryBooleanStateFormula>(storm::logic::BinaryBooleanStateFormula::OperatorType::And, relevantStatesFormula, memLabelFormula); |
|
||||
} else { |
|
||||
relevantStatesFormula = memLabelFormula; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
storm::storage::BitVector relevantStates = mc.check(*relevantStatesFormula)->asExplicitQualitativeCheckResult().getTruthValuesVector(); |
|
||||
storm::storage::BitVector relevantChoices = getProduct().getTransitionMatrix().getRowFilter(relevantStates); |
|
||||
storm::storage::BitVector goalStates = mc.check(*goalStatesFormula)->asExplicitQualitativeCheckResult().getTruthValuesVector(); |
|
||||
for (auto const& choice : relevantChoices) { |
|
||||
objRew[choice] += getProduct().getTransitionMatrix().getConstrainedRowSum(choice, goalStates); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
objectiveRewards.push_back(std::move(objRew)); |
|
||||
|
|
||||
} else if (formula.isRewardOperatorFormula()) { |
|
||||
auto const& rewModel = getProduct().getRewardModel(formula.asRewardOperatorFormula().getRewardModelName()); |
|
||||
STORM_LOG_THROW(!rewModel.hasTransitionRewards(), storm::exceptions::NotSupportedException, "Reward model has transition rewards which is not expected."); |
|
||||
bool rewardCollectedInEpoch = true; |
|
||||
if (formula.getSubformula().isCumulativeRewardFormula()) { |
|
||||
for (auto const& dim : objectiveDimensions[objIndex]) { |
|
||||
if (epochManager.isBottomDimensionEpochClass(epochClass, dim)) { |
|
||||
rewardCollectedInEpoch = false; |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
} else { |
|
||||
STORM_LOG_THROW(formula.getSubformula().isTotalRewardFormula(), storm::exceptions::UnexpectedException, "Unexpected type of formula " << formula); |
|
||||
} |
|
||||
if (rewardCollectedInEpoch) { |
|
||||
objectiveRewards.push_back(rewModel.getTotalRewardVector(getProduct().getTransitionMatrix())); |
|
||||
} else { |
|
||||
objectiveRewards.emplace_back(getProduct().getTransitionMatrix().getRowCount(), storm::utility::zero<ValueType>()); |
|
||||
} |
|
||||
} else { |
|
||||
STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected type of formula " << formula); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return objectiveRewards; |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType> |
|
||||
storm::storage::BitVector const& ProductModel<ValueType>::getInStates(EpochClass const& epochClass) const { |
|
||||
STORM_LOG_ASSERT(inStates.find(epochClass) != inStates.end(), "Could not find InStates for the given epoch class"); |
|
||||
return inStates.find(epochClass)->second; |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType> |
|
||||
void ProductModel<ValueType>::computeReachableStatesInEpochClasses() { |
|
||||
std::set<Epoch> possibleSteps(steps.begin(), steps.end()); |
|
||||
std::set<EpochClass, std::function<bool(EpochClass const&, EpochClass const&)>> reachableEpochClasses(std::bind(&EpochManager::epochClassOrder, &epochManager, std::placeholders::_1, std::placeholders::_2)); |
|
||||
|
|
||||
collectReachableEpochClasses(reachableEpochClasses, possibleSteps); |
|
||||
|
|
||||
for (auto epochClassIt = reachableEpochClasses.rbegin(); epochClassIt != reachableEpochClasses.rend(); ++epochClassIt) { |
|
||||
std::vector<EpochClass> predecessors; |
|
||||
for (auto predecessorIt = reachableEpochClasses.rbegin(); predecessorIt != epochClassIt; ++predecessorIt) { |
|
||||
if (epochManager.isPredecessorEpochClass(*predecessorIt, *epochClassIt)) { |
|
||||
predecessors.push_back(*predecessorIt); |
|
||||
} |
|
||||
} |
|
||||
computeReachableStates(*epochClassIt, predecessors); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType> |
|
||||
void ProductModel<ValueType>::collectReachableEpochClasses(std::set<EpochClass, std::function<bool(EpochClass const&, EpochClass const&)>>& reachableEpochClasses, std::set<Epoch> const& possibleSteps) const { |
|
||||
|
|
||||
Epoch startEpoch = epochManager.getZeroEpoch(); |
|
||||
for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) { |
|
||||
STORM_LOG_ASSERT(dimensions[dim].maxValue, "No max-value for dimension " << dim << " was given."); |
|
||||
epochManager.setDimensionOfEpoch(startEpoch, dim, dimensions[dim].maxValue.get()); |
|
||||
} |
|
||||
|
|
||||
std::set<Epoch> seenEpochs({startEpoch}); |
|
||||
std::vector<Epoch> dfsStack({startEpoch}); |
|
||||
|
|
||||
reachableEpochClasses.insert(epochManager.getEpochClass(startEpoch)); |
|
||||
|
|
||||
// Perform a DFS to find all the reachable epochs
|
|
||||
while (!dfsStack.empty()) { |
|
||||
Epoch currentEpoch = dfsStack.back(); |
|
||||
dfsStack.pop_back(); |
|
||||
for (auto const& step : possibleSteps) { |
|
||||
Epoch successorEpoch = epochManager.getSuccessorEpoch(currentEpoch, step); |
|
||||
if (seenEpochs.insert(successorEpoch).second) { |
|
||||
reachableEpochClasses.insert(epochManager.getEpochClass(successorEpoch)); |
|
||||
dfsStack.push_back(std::move(successorEpoch)); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType> |
|
||||
void ProductModel<ValueType>::computeReachableStates(EpochClass const& epochClass, std::vector<EpochClass> const& predecessors) { |
|
||||
|
|
||||
storm::storage::BitVector bottomDimensions(epochManager.getDimensionCount(), false); |
|
||||
for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) { |
|
||||
if (epochManager.isBottomDimensionEpochClass(epochClass, dim)) { |
|
||||
bottomDimensions.set(dim, true); |
|
||||
} |
|
||||
} |
|
||||
storm::storage::BitVector nonBottomDimensions = ~bottomDimensions; |
|
||||
|
|
||||
storm::storage::BitVector ecInStates(getProduct().getNumberOfStates(), false); |
|
||||
|
|
||||
if (!epochManager.hasBottomDimensionEpochClass(epochClass)) { |
|
||||
for (auto const& initState : getProduct().getInitialStates()) { |
|
||||
uint64_t transformedInitState = transformProductState(initState, epochClass, memoryStateManager.getInitialMemoryState()); |
|
||||
ecInStates.set(transformedInitState, true); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
for (auto const& predecessor : predecessors) { |
|
||||
storm::storage::BitVector positiveStepDimensions(epochManager.getDimensionCount(), false); |
|
||||
for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) { |
|
||||
if (!epochManager.isBottomDimensionEpochClass(predecessor, dim) && bottomDimensions.get(dim)) { |
|
||||
positiveStepDimensions.set(dim, true); |
|
||||
} |
|
||||
} |
|
||||
STORM_LOG_ASSERT(reachableStates.find(predecessor) != reachableStates.end(), "Could not find reachable states of predecessor epoch class."); |
|
||||
storm::storage::BitVector predecessorStates = reachableStates.find(predecessor)->second; |
|
||||
for (auto const& predecessorState : predecessorStates) { |
|
||||
uint64_t predecessorMemoryState = getMemoryState(predecessorState); |
|
||||
for (uint64_t choice = getProduct().getTransitionMatrix().getRowGroupIndices()[predecessorState]; choice < getProduct().getTransitionMatrix().getRowGroupIndices()[predecessorState + 1]; ++choice) { |
|
||||
bool choiceLeadsToThisClass = false; |
|
||||
Epoch const& choiceStep = getSteps()[choice]; |
|
||||
for (auto const& dim : positiveStepDimensions) { |
|
||||
if (epochManager.getDimensionOfEpoch(choiceStep, dim) > 0) { |
|
||||
choiceLeadsToThisClass = true; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (choiceLeadsToThisClass) { |
|
||||
for (auto const& transition : getProduct().getTransitionMatrix().getRow(choice)) { |
|
||||
uint64_t successorState = transformProductState(transition.getColumn(), epochClass, predecessorMemoryState); |
|
||||
|
|
||||
ecInStates.set(successorState, true); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// Find all states reachable from an InState via DFS.
|
|
||||
storm::storage::BitVector ecReachableStates = ecInStates; |
|
||||
std::vector<uint64_t> dfsStack(ecReachableStates.begin(), ecReachableStates.end()); |
|
||||
|
|
||||
while (!dfsStack.empty()) { |
|
||||
uint64_t currentState = dfsStack.back(); |
|
||||
uint64_t currentMemoryState = getMemoryState(currentState); |
|
||||
dfsStack.pop_back(); |
|
||||
|
|
||||
for (uint64_t choice = getProduct().getTransitionMatrix().getRowGroupIndices()[currentState]; choice != getProduct().getTransitionMatrix().getRowGroupIndices()[currentState + 1]; ++choice) { |
|
||||
|
|
||||
bool choiceLeadsOutsideOfEpoch = false; |
|
||||
Epoch const& choiceStep = getSteps()[choice]; |
|
||||
for (auto const& dim : nonBottomDimensions) { |
|
||||
if (epochManager.getDimensionOfEpoch(choiceStep, dim) > 0) { |
|
||||
choiceLeadsOutsideOfEpoch = true; |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
for (auto const& transition : getProduct().getTransitionMatrix().getRow(choice)) { |
|
||||
uint64_t successorState = transformProductState(transition.getColumn(), epochClass, currentMemoryState); |
|
||||
if (choiceLeadsOutsideOfEpoch) { |
|
||||
ecInStates.set(successorState, true); |
|
||||
} |
|
||||
if (!ecReachableStates.get(successorState)) { |
|
||||
ecReachableStates.set(successorState, true); |
|
||||
dfsStack.push_back(successorState); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
reachableStates[epochClass] = std::move(ecReachableStates); |
|
||||
|
|
||||
inStates[epochClass] = std::move(ecInStates); |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType> |
|
||||
typename ProductModel<ValueType>::MemoryState ProductModel<ValueType>::transformMemoryState(MemoryState const& memoryState, EpochClass const& epochClass, MemoryState const& predecessorMemoryState) const { |
|
||||
MemoryState memoryStatePrime = memoryState; |
|
||||
|
|
||||
for (auto const& objDimensions : objectiveDimensions) { |
|
||||
for (auto const& dim : objDimensions) { |
|
||||
auto const& dimension = dimensions[dim]; |
|
||||
if (dimension.memoryLabel) { |
|
||||
bool dimUpperBounded = dimension.isUpperBounded; |
|
||||
bool dimBottom = epochManager.isBottomDimensionEpochClass(epochClass, dim); |
|
||||
if (dimUpperBounded && dimBottom && memoryStateManager.isRelevantDimension(predecessorMemoryState, dim)) { |
|
||||
STORM_LOG_ASSERT(objDimensions == dimension.dependentDimensions, "Unexpected set of dependent dimensions"); |
|
||||
memoryStateManager.setRelevantDimensions(memoryStatePrime, objDimensions, false); |
|
||||
break; |
|
||||
} else if (!dimUpperBounded && !dimBottom && memoryStateManager.isRelevantDimension(predecessorMemoryState, dim)) { |
|
||||
memoryStateManager.setRelevantDimensions(memoryStatePrime, dimension.dependentDimensions, true); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// std::cout << "Transformed memory state " << memoryStateManager.toString(memoryState) << " at epoch class " << epochClass << " with predecessor " << memoryStateManager.toString(predecessorMemoryState) << " to " << memoryStateManager.toString(memoryStatePrime) << std::endl;
|
|
||||
|
|
||||
return memoryStatePrime; |
|
||||
} |
|
||||
|
|
||||
template<typename ValueType> |
|
||||
uint64_t ProductModel<ValueType>::transformProductState(uint64_t const& productState, EpochClass const& epochClass, MemoryState const& predecessorMemoryState) const { |
|
||||
return getProductState(getModelState(productState), transformMemoryState(getMemoryState(productState), epochClass, predecessorMemoryState)); |
|
||||
} |
|
||||
|
|
||||
template class ProductModel<double>; |
|
||||
template class ProductModel<storm::RationalNumber>; |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,82 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include <boost/optional.hpp> |
|
||||
|
|
||||
#include "storm/storage/BitVector.h" |
|
||||
#include "storm/modelchecker/multiobjective/Objective.h" |
|
||||
#include "storm/modelchecker/multiobjective/rewardbounded/EpochManager.h" |
|
||||
#include "storm/modelchecker/multiobjective/rewardbounded/MemoryStateManager.h" |
|
||||
#include "storm/modelchecker/multiobjective/rewardbounded/Dimension.h" |
|
||||
#include "storm/models/sparse/Mdp.h" |
|
||||
#include "storm/utility/vector.h" |
|
||||
#include "storm/storage/memorystructure/MemoryStructure.h" |
|
||||
#include "storm/storage/memorystructure/SparseModelMemoryProduct.h" |
|
||||
|
|
||||
namespace storm { |
|
||||
namespace modelchecker { |
|
||||
namespace multiobjective { |
|
||||
|
|
||||
|
|
||||
template<typename ValueType> |
|
||||
class ProductModel { |
|
||||
public: |
|
||||
|
|
||||
typedef typename EpochManager::Epoch Epoch; |
|
||||
typedef typename EpochManager::EpochClass EpochClass; |
|
||||
typedef typename MemoryStateManager::MemoryState MemoryState; |
|
||||
|
|
||||
|
|
||||
ProductModel(storm::models::sparse::Mdp<ValueType> const& model, std::vector<storm::modelchecker::multiobjective::Objective<ValueType>> const& objectives, std::vector<Dimension<ValueType>> const& dimensions, std::vector<storm::storage::BitVector> const& objectiveDimensions, EpochManager const& epochManager, std::vector<Epoch> const& originalModelSteps); |
|
||||
|
|
||||
storm::models::sparse::Mdp<ValueType> const& getProduct() const; |
|
||||
std::vector<Epoch> const& getSteps() const; |
|
||||
|
|
||||
bool productStateExists(uint64_t const& modelState, uint64_t const& memoryState) const; |
|
||||
uint64_t getProductState(uint64_t const& modelState, uint64_t const& memoryState) const; |
|
||||
uint64_t getInitialProductState(uint64_t const& initialModelState, storm::storage::BitVector const& initialModelStates) const; |
|
||||
uint64_t getModelState(uint64_t const& productState) const; |
|
||||
MemoryState getMemoryState(uint64_t const& productState) const; |
|
||||
MemoryStateManager const& getMemoryStateManager() const; |
|
||||
|
|
||||
uint64_t getProductStateFromChoice(uint64_t const& productChoice) const; |
|
||||
|
|
||||
std::vector<std::vector<ValueType>> computeObjectiveRewards(EpochClass const& epochClass, std::vector<storm::modelchecker::multiobjective::Objective<ValueType>> const& objectives) const; |
|
||||
storm::storage::BitVector const& getInStates(EpochClass const& epochClass) const; |
|
||||
|
|
||||
|
|
||||
MemoryState transformMemoryState(MemoryState const& memoryState, EpochClass const& epochClass, MemoryState const& predecessorMemoryState) const; |
|
||||
uint64_t transformProductState(uint64_t const& productState, EpochClass const& epochClass, MemoryState const& predecessorMemoryState) const; |
|
||||
|
|
||||
|
|
||||
private: |
|
||||
|
|
||||
storm::storage::MemoryStructure computeMemoryStructure(storm::models::sparse::Mdp<ValueType> const& model, std::vector<storm::modelchecker::multiobjective::Objective<ValueType>> const& objectives) const; |
|
||||
std::vector<MemoryState> computeMemoryStateMap(storm::storage::MemoryStructure const& memory) const; |
|
||||
|
|
||||
|
|
||||
void setReachableProductStates(storm::storage::SparseModelMemoryProduct<ValueType>& productBuilder, std::vector<Epoch> const& originalModelSteps, std::vector<MemoryState> const& memoryStateMap) const; |
|
||||
|
|
||||
void collectReachableEpochClasses(std::set<EpochClass, std::function<bool(EpochClass const&, EpochClass const&)>>& reachableEpochClasses, std::set<Epoch> const& possibleSteps) const; |
|
||||
|
|
||||
void computeReachableStatesInEpochClasses(); |
|
||||
void computeReachableStates(EpochClass const& epochClass, std::vector<EpochClass> const& predecessors); |
|
||||
|
|
||||
std::vector<Dimension<ValueType>> const& dimensions; |
|
||||
std::vector<storm::storage::BitVector> const& objectiveDimensions; |
|
||||
EpochManager const& epochManager; |
|
||||
MemoryStateManager memoryStateManager; |
|
||||
|
|
||||
std::shared_ptr<storm::models::sparse::Mdp<ValueType>> product; |
|
||||
std::vector<Epoch> steps; |
|
||||
std::map<EpochClass, storm::storage::BitVector> reachableStates; |
|
||||
std::map<EpochClass, storm::storage::BitVector> inStates; |
|
||||
|
|
||||
std::vector<uint64_t> modelMemoryToProductStateMap; |
|
||||
std::vector<uint64_t> productToModelStateMap; |
|
||||
std::vector<MemoryState> productToMemoryStateMap; |
|
||||
std::vector<uint64_t> choiceToStateMap; |
|
||||
|
|
||||
}; |
|
||||
} |
|
||||
} |
|
||||
} |
|
Reference in new issue
xxxxxxxxxx