TimQu
7 years ago
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; |
|||
|
|||
}; |
|||
} |
|||
} |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue