Tim Quatmann
4 years ago
30 changed files with 1509 additions and 106 deletions
-
6CHANGELOG.md
-
2src/storm-pomdp/generator/BeliefSupportTracker.cpp
-
517src/storm-pomdp/generator/NondeterministicBeliefTracker.cpp
-
215src/storm-pomdp/generator/NondeterministicBeliefTracker.h
-
1src/storm-pomdp/transformer/MakePOMDPCanonic.cpp
-
202src/storm-pomdp/transformer/ObservationTraceUnfolder.cpp
-
49src/storm-pomdp/transformer/ObservationTraceUnfolder.h
-
13src/storm/builder/BuilderOptions.cpp
-
12src/storm/builder/BuilderOptions.h
-
62src/storm/builder/ExplicitModelBuilder.cpp
-
59src/storm/generator/NextStateGenerator.cpp
-
6src/storm/generator/NextStateGenerator.h
-
7src/storm/generator/VariableInformation.cpp
-
11src/storm/generator/VariableInformation.h
-
4src/storm/models/ModelBase.cpp
-
7src/storm/models/ModelBase.h
-
24src/storm/models/sparse/Pomdp.cpp
-
14src/storm/models/sparse/Pomdp.h
-
4src/storm/simulator/DiscreteTimeSparseModelSimulator.cpp
-
3src/storm/solver/helper/OptimisticValueIterationHelper.cpp
-
2src/storm/storage/expressions/ExpressionManager.cpp
-
138src/storm/storage/geometry/ReduceVertexCloud.cpp
-
36src/storm/storage/geometry/ReduceVertexCloud.h
-
2src/storm/storage/sparse/ModelComponents.h
-
120src/storm/storage/sparse/StateValuations.cpp
-
44src/storm/storage/sparse/StateValuations.h
-
6src/storm/utility/SignalHandler.h
-
24src/storm/utility/random.cpp
-
15src/storm/utility/random.h
-
10src/storm/utility/vector.h
@ -0,0 +1,517 @@ |
|||
|
|||
#include "storm-pomdp/generator/NondeterministicBeliefTracker.h"
|
|||
#include "storm/utility/ConstantsComparator.h"
|
|||
#include "storm/storage/geometry/nativepolytopeconversion/QuickHull.h"
|
|||
#include "storm/storage/geometry/ReduceVertexCloud.h"
|
|||
#include "storm/utility/vector.h"
|
|||
#include "storm/utility/Stopwatch.h"
|
|||
|
|||
namespace storm { |
|||
namespace generator { |
|||
|
|||
template<typename ValueType> |
|||
BeliefStateManager<ValueType>::BeliefStateManager(storm::models::sparse::Pomdp<ValueType> const& pomdp) |
|||
: pomdp(pomdp) |
|||
{ |
|||
numberActionsPerObservation = std::vector<uint64_t>(pomdp.getNrObservations(), 0); |
|||
statePerObservationAndOffset = std::vector<std::vector<uint64_t>>(pomdp.getNrObservations(), std::vector<uint64_t>()); |
|||
for (uint64_t state = 0; state < pomdp.getNumberOfStates(); ++state) { |
|||
numberActionsPerObservation[pomdp.getObservation(state)] = pomdp.getNumberOfChoices(state); |
|||
statePerObservationAndOffset[pomdp.getObservation(state)].push_back(state); |
|||
observationOffsetId.push_back(statePerObservationAndOffset[pomdp.getObservation(state)].size() - 1); |
|||
} |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
uint32_t BeliefStateManager<ValueType>::getObservation(uint64_t state) const { |
|||
return pomdp.getObservation(state); |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
uint64_t BeliefStateManager<ValueType>::getNumberOfStates() const { |
|||
return pomdp.getNumberOfStates(); |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
uint64_t BeliefStateManager<ValueType>::getActionsForObservation(uint32_t observation) const { |
|||
return numberActionsPerObservation[observation]; |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
ValueType BeliefStateManager<ValueType>::getRisk(uint64_t state) const { |
|||
return riskPerState.at(state); |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
storm::models::sparse::Pomdp<ValueType> const& BeliefStateManager<ValueType>::getPomdp() const { |
|||
return pomdp; |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
void BeliefStateManager<ValueType>::setRiskPerState(std::vector<ValueType> const& risk) { |
|||
riskPerState = risk; |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
uint64_t BeliefStateManager<ValueType>::getFreshId() { |
|||
beliefIdCounter++; |
|||
return beliefIdCounter; |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
uint64_t BeliefStateManager<ValueType>::getObservationOffset(uint64_t state) const { |
|||
STORM_LOG_ASSERT(state < observationOffsetId.size(), "State " << state << " not a state id"); |
|||
return observationOffsetId[state]; |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
uint64_t BeliefStateManager<ValueType>::numberOfStatesPerObservation(uint32_t observation) const { |
|||
STORM_LOG_ASSERT(observation < observationOffsetId.size(), "Observation " << observation << " not an observation id"); |
|||
return statePerObservationAndOffset[observation].size(); |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
uint64_t BeliefStateManager<ValueType>::getState(uint32_t obs, uint64_t offset) const { |
|||
STORM_LOG_ASSERT(obs < statePerObservationAndOffset.size(), "Obs " << obs << " not a known observatoin"); |
|||
STORM_LOG_ASSERT(offset < statePerObservationAndOffset[obs].size(), "Offset " << offset << " too high for observation " << obs); |
|||
return statePerObservationAndOffset[obs][offset]; |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
SparseBeliefState<ValueType>::SparseBeliefState(std::shared_ptr<BeliefStateManager<ValueType>> const& manager, uint64_t state) |
|||
: manager(manager), belief(), id(0), prevId(0) |
|||
{ |
|||
id = manager->getFreshId(); |
|||
belief[state] = storm::utility::one<ValueType>(); |
|||
risk = manager->getRisk(state); |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
SparseBeliefState<ValueType>::SparseBeliefState(std::shared_ptr<BeliefStateManager<ValueType>> const& manager, std::map<uint64_t, ValueType> const& belief, |
|||
std::size_t hash, ValueType const& risk, uint64_t prevId) |
|||
: manager(manager), belief(belief), prestoredhash(hash), risk(risk), id(0), prevId(prevId) |
|||
{ |
|||
id = manager->getFreshId(); |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
ValueType SparseBeliefState<ValueType>::get(uint64_t state) const { |
|||
return belief.at(state); |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
ValueType SparseBeliefState<ValueType>::getRisk() const { |
|||
return risk; |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
std::size_t SparseBeliefState<ValueType>::hash() const noexcept { |
|||
return prestoredhash; |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
bool SparseBeliefState<ValueType>::isValid() const { |
|||
return !belief.empty(); |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
std::string SparseBeliefState<ValueType>::toString() const { |
|||
std::stringstream sstr; |
|||
sstr << "id: " << id << "; "; |
|||
bool first = true; |
|||
for (auto const& beliefentry : belief) { |
|||
if (!first) { |
|||
sstr << ", "; |
|||
} else { |
|||
first = false; |
|||
} |
|||
sstr << beliefentry.first << " : " << beliefentry.second; |
|||
} |
|||
sstr << " (from " << prevId << ")"; |
|||
return sstr.str(); |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
bool operator==(SparseBeliefState<ValueType> const& lhs, SparseBeliefState<ValueType> const& rhs) { |
|||
if (lhs.hash() != rhs.hash()) { |
|||
return false; |
|||
} |
|||
if (lhs.belief.size() != rhs.belief.size()) { |
|||
return false; |
|||
} |
|||
storm::utility::ConstantsComparator<ValueType> cmp(0.00001, true); |
|||
auto lhsIt = lhs.belief.begin(); |
|||
auto rhsIt = rhs.belief.begin(); |
|||
while(lhsIt != lhs.belief.end()) { |
|||
if (lhsIt->first != rhsIt->first || !cmp.isEqual(lhsIt->second, rhsIt->second)) { |
|||
return false; |
|||
} |
|||
++lhsIt; |
|||
++rhsIt; |
|||
} |
|||
return true; |
|||
//return std::equal(lhs.belief.begin(), lhs.belief.end(), rhs.belief.begin());
|
|||
} |
|||
|
|||
|
|||
template<typename ValueType> |
|||
void SparseBeliefState<ValueType>::update(uint32_t newObservation, std::unordered_set<SparseBeliefState<ValueType>>& previousBeliefs) const { |
|||
updateHelper({{}}, {storm::utility::zero<ValueType>()}, belief.begin(), newObservation, previousBeliefs); |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
uint64_t SparseBeliefState<ValueType>::getSupportSize() const { |
|||
return manager->getNumberOfStates(); |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
std::map<uint64_t, ValueType> const& SparseBeliefState<ValueType>::getBeliefMap() const { |
|||
return belief; |
|||
} |
|||
|
|||
|
|||
template<typename ValueType> |
|||
void SparseBeliefState<ValueType>::setSupport(storm::storage::BitVector& support) const { |
|||
for(auto const& entry : belief) { |
|||
support.set(entry.first, true); |
|||
} |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
void SparseBeliefState<ValueType>::updateHelper(std::vector<std::map<uint64_t, ValueType>> const& partialBeliefs, std::vector<ValueType> const& sums, typename std::map<uint64_t, ValueType>::const_iterator nextStateIt, uint32_t newObservation, std::unordered_set<SparseBeliefState<ValueType>>& previousBeliefs) const { |
|||
if(nextStateIt == belief.end()) { |
|||
for (uint64_t i = 0; i < partialBeliefs.size(); ++i) { |
|||
auto const& partialBelief = partialBeliefs[i]; |
|||
auto const& sum = sums[i]; |
|||
if (storm::utility::isZero(sum)) { |
|||
continue; |
|||
} |
|||
std::size_t newHash = 0; |
|||
ValueType risk = storm::utility::zero<ValueType>(); |
|||
std::map<uint64_t, ValueType> finalBelief; |
|||
for (auto &entry : partialBelief) { |
|||
assert(!storm::utility::isZero(sum)); |
|||
finalBelief[entry.first] = entry.second / sum; |
|||
//boost::hash_combine(newHash, std::hash<ValueType>()(entry.second));
|
|||
boost::hash_combine(newHash, entry.first); |
|||
risk += entry.second / sum * manager->getRisk(entry.first); |
|||
} |
|||
previousBeliefs.insert(SparseBeliefState<ValueType>(manager, finalBelief, newHash, risk, id)); |
|||
} |
|||
} else { |
|||
uint64_t state = nextStateIt->first; |
|||
auto newNextStateIt = nextStateIt; |
|||
newNextStateIt++; |
|||
std::vector<std::map<uint64_t, ValueType>> newPartialBeliefs; |
|||
std::vector<ValueType> newSums; |
|||
for (uint64_t i = 0; i < partialBeliefs.size(); ++i) { |
|||
|
|||
for (auto row = manager->getPomdp().getNondeterministicChoiceIndices()[state]; |
|||
row < manager->getPomdp().getNondeterministicChoiceIndices()[state + 1]; ++row) { |
|||
std::map<uint64_t, ValueType> newPartialBelief = partialBeliefs[i]; |
|||
ValueType newSum = sums[i]; |
|||
for (auto const &transition : manager->getPomdp().getTransitionMatrix().getRow(row)) { |
|||
if (newObservation != manager->getPomdp().getObservation(transition.getColumn())) { |
|||
continue; |
|||
} |
|||
|
|||
if (newPartialBelief.count(transition.getColumn()) == 0) { |
|||
newPartialBelief[transition.getColumn()] = transition.getValue() * nextStateIt->second; |
|||
} else { |
|||
newPartialBelief[transition.getColumn()] += transition.getValue() * nextStateIt->second; |
|||
} |
|||
newSum += transition.getValue() * nextStateIt->second; |
|||
|
|||
} |
|||
newPartialBeliefs.push_back(newPartialBelief); |
|||
newSums.push_back(newSum); |
|||
} |
|||
} |
|||
updateHelper(newPartialBeliefs, newSums, newNextStateIt, newObservation, previousBeliefs); |
|||
|
|||
} |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
bool operator==(ObservationDenseBeliefState<ValueType> const& lhs, ObservationDenseBeliefState<ValueType> const& rhs) { |
|||
if (lhs.hash() != rhs.hash()) { |
|||
return false; |
|||
} |
|||
if (lhs.observation != rhs.observation) { |
|||
return false; |
|||
} |
|||
storm::utility::ConstantsComparator<ValueType> cmp(0.00001, true); |
|||
auto lhsIt = lhs.belief.begin(); |
|||
auto rhsIt = rhs.belief.begin(); |
|||
while(lhsIt != lhs.belief.end()) { |
|||
if (!cmp.isEqual(*lhsIt, *rhsIt)) { |
|||
return false; |
|||
} |
|||
++lhsIt; |
|||
++rhsIt; |
|||
} |
|||
return true; |
|||
//return std::equal(lhs.belief.begin(), lhs.belief.end(), rhs.belief.begin());
|
|||
} |
|||
|
|||
template<typename ValueType> |
|||
ObservationDenseBeliefState<ValueType>::ObservationDenseBeliefState(std::shared_ptr<BeliefStateManager<ValueType>> const& manager, uint64_t state) |
|||
: manager(manager), belief(manager->numberOfStatesPerObservation(manager->getObservation(state))) |
|||
{ |
|||
observation = manager->getObservation(state); |
|||
belief[manager->getObservationOffset(state)] = storm::utility::one<ValueType>(); |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
ObservationDenseBeliefState<ValueType>::ObservationDenseBeliefState(std::shared_ptr<BeliefStateManager<ValueType>> const& manager, uint32_t observation, std::vector<ValueType> const& belief, std::size_t newHash, ValueType const& risk, uint64_t prevId) |
|||
: manager(manager), belief(belief), observation(observation), prestoredhash(newHash), risk(risk), id(manager->getFreshId()), prevId(prevId) |
|||
{ |
|||
// Intentionally left empty.
|
|||
} |
|||
|
|||
|
|||
template<typename ValueType> |
|||
void ObservationDenseBeliefState<ValueType>::update(uint32_t newObservation, std::unordered_set<ObservationDenseBeliefState>& previousBeliefs) const { |
|||
updateHelper({{}}, {storm::utility::zero<ValueType>()}, 0, newObservation, previousBeliefs); |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
void ObservationDenseBeliefState<ValueType>::updateHelper(std::vector<std::map<uint64_t, ValueType>> const& partialBeliefs, std::vector<ValueType> const& sums, uint64_t currentEntry, uint32_t newObservation, std::unordered_set<ObservationDenseBeliefState<ValueType>>& previousBeliefs) const { |
|||
while(currentEntry != belief.size() && storm::utility::isZero(belief[currentEntry])) { |
|||
currentEntry++; |
|||
} |
|||
if(currentEntry == belief.size()) { |
|||
for (uint64_t i = 0; i < partialBeliefs.size(); ++i) { |
|||
auto const& partialBelief = partialBeliefs[i]; |
|||
auto const& sum = sums[i]; |
|||
if (storm::utility::isZero(sum)) { |
|||
continue; |
|||
} |
|||
std::size_t newHash = 0; |
|||
ValueType risk = storm::utility::zero<ValueType>(); |
|||
std::vector<ValueType> finalBelief(manager->numberOfStatesPerObservation(observation), storm::utility::zero<ValueType>()); |
|||
for (auto &entry : partialBelief) { |
|||
assert(!storm::utility::isZero(sum)); |
|||
finalBelief[manager->getObservationOffset(entry.first)] = (entry.second / sum); |
|||
//boost::hash_combine(newHash, std::hash<ValueType>()(entry.second));
|
|||
boost::hash_combine(newHash, entry.first); |
|||
risk += entry.second / sum * manager->getRisk(entry.first); |
|||
} |
|||
previousBeliefs.insert(ObservationDenseBeliefState<ValueType>(manager, newObservation, finalBelief, newHash, risk, id)); |
|||
} |
|||
} else { |
|||
uint64_t state = manager->getState(observation,currentEntry); |
|||
uint64_t nextEntry = currentEntry + 1; |
|||
std::vector<std::map<uint64_t, ValueType>> newPartialBeliefs; |
|||
std::vector<ValueType> newSums; |
|||
for (uint64_t i = 0; i < partialBeliefs.size(); ++i) { |
|||
|
|||
for (auto row = manager->getPomdp().getNondeterministicChoiceIndices()[state]; |
|||
row < manager->getPomdp().getNondeterministicChoiceIndices()[state + 1]; ++row) { |
|||
std::map<uint64_t, ValueType> newPartialBelief = partialBeliefs[i]; |
|||
ValueType newSum = sums[i]; |
|||
for (auto const &transition : manager->getPomdp().getTransitionMatrix().getRow(row)) { |
|||
if (newObservation != manager->getPomdp().getObservation(transition.getColumn())) { |
|||
continue; |
|||
} |
|||
|
|||
if (newPartialBelief.count(transition.getColumn()) == 0) { |
|||
newPartialBelief[transition.getColumn()] = transition.getValue() * belief[currentEntry]; |
|||
} else { |
|||
newPartialBelief[transition.getColumn()] += transition.getValue() * belief[currentEntry]; |
|||
} |
|||
newSum += transition.getValue() * belief[currentEntry]; |
|||
|
|||
} |
|||
newPartialBeliefs.push_back(newPartialBelief); |
|||
newSums.push_back(newSum); |
|||
} |
|||
} |
|||
updateHelper(newPartialBeliefs, newSums, nextEntry, newObservation, previousBeliefs); |
|||
} |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
std::size_t ObservationDenseBeliefState<ValueType>::hash() const noexcept { |
|||
return prestoredhash; |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
ValueType ObservationDenseBeliefState<ValueType>::get(uint64_t state) const { |
|||
if (manager->getObservation(state) != state) { |
|||
return storm::utility::zero<ValueType>(); |
|||
} |
|||
return belief[manager->getObservationOffset(state)]; |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
ValueType ObservationDenseBeliefState<ValueType>::getRisk() const { |
|||
return risk; |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
uint64_t ObservationDenseBeliefState<ValueType>::getSupportSize() const { |
|||
return belief.size(); |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
void ObservationDenseBeliefState<ValueType>::setSupport(storm::storage::BitVector& support) const { |
|||
storm::utility::vector::setNonzeroIndices(belief, support); |
|||
} |
|||
|
|||
|
|||
template<typename ValueType> |
|||
std::string ObservationDenseBeliefState<ValueType>::toString() const { |
|||
std::stringstream sstr; |
|||
sstr << "id: " << id << "; "; |
|||
bool first = true; |
|||
uint64_t i = 0; |
|||
for (auto const& beliefentry : belief) { |
|||
|
|||
if (!storm::utility::isZero(beliefentry)) { |
|||
if (!first) { |
|||
sstr << ", "; |
|||
} else { |
|||
first = false; |
|||
} |
|||
|
|||
sstr << manager->getState(observation, i) << " : " << beliefentry; |
|||
} |
|||
i++; |
|||
} |
|||
sstr << " (from " << prevId << ")"; |
|||
return sstr.str(); |
|||
} |
|||
|
|||
|
|||
template<typename ValueType, typename BeliefState> |
|||
NondeterministicBeliefTracker<ValueType, BeliefState>::NondeterministicBeliefTracker(storm::models::sparse::Pomdp<ValueType> const& pomdp, typename NondeterministicBeliefTracker<ValueType, BeliefState>::Options options ) : |
|||
pomdp(pomdp), manager(std::make_shared<BeliefStateManager<ValueType>>(pomdp)), beliefs(), options(options) { |
|||
//
|
|||
} |
|||
|
|||
template<typename ValueType, typename BeliefState> |
|||
bool NondeterministicBeliefTracker<ValueType, BeliefState>::reset(uint32_t observation) { |
|||
bool hit = false; |
|||
for (auto state : pomdp.getInitialStates()) { |
|||
if (observation == pomdp.getObservation(state)) { |
|||
hit = true; |
|||
beliefs.emplace(manager, state); |
|||
} |
|||
} |
|||
lastObservation = observation; |
|||
return hit; |
|||
} |
|||
|
|||
template<typename ValueType, typename BeliefState> |
|||
bool NondeterministicBeliefTracker<ValueType, BeliefState>::track(uint64_t newObservation) { |
|||
STORM_LOG_THROW(!beliefs.empty(), storm::exceptions::InvalidOperationException, "Cannot track without a belief (need to reset)."); |
|||
std::unordered_set<BeliefState> newBeliefs; |
|||
storm::utility::Stopwatch trackTimer(true); |
|||
for (auto const& belief : beliefs) { |
|||
belief.update(newObservation, newBeliefs); |
|||
if (options.trackTimeOut > 0 && trackTimer.getTimeInMilliseconds() > options.trackTimeOut) { |
|||
return false; |
|||
} |
|||
} |
|||
beliefs = newBeliefs; |
|||
lastObservation = newObservation; |
|||
return !beliefs.empty(); |
|||
} |
|||
|
|||
template<typename ValueType, typename BeliefState> |
|||
ValueType NondeterministicBeliefTracker<ValueType, BeliefState>::getCurrentRisk(bool max) { |
|||
STORM_LOG_THROW(!beliefs.empty(), storm::exceptions::InvalidOperationException, "Risk is only defined for beliefs (run reset() first)."); |
|||
ValueType result = beliefs.begin()->getRisk(); |
|||
if (max) { |
|||
for (auto const& belief : beliefs) { |
|||
if (belief.getRisk() > result) { |
|||
result = belief.getRisk(); |
|||
} |
|||
} |
|||
} else { |
|||
for (auto const& belief : beliefs) { |
|||
if (belief.getRisk() < result) { |
|||
result = belief.getRisk(); |
|||
} |
|||
} |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
template<typename ValueType, typename BeliefState> |
|||
void NondeterministicBeliefTracker<ValueType, BeliefState>::setRisk(std::vector<ValueType> const& risk) { |
|||
manager->setRiskPerState(risk); |
|||
} |
|||
|
|||
template<typename ValueType, typename BeliefState> |
|||
std::unordered_set<BeliefState> const& NondeterministicBeliefTracker<ValueType, BeliefState>::getCurrentBeliefs() const { |
|||
return beliefs; |
|||
} |
|||
|
|||
template<typename ValueType, typename BeliefState> |
|||
uint32_t NondeterministicBeliefTracker<ValueType, BeliefState>::getCurrentObservation() const { |
|||
return lastObservation; |
|||
} |
|||
|
|||
template<typename ValueType, typename BeliefState> |
|||
uint64_t NondeterministicBeliefTracker<ValueType, BeliefState>::getNumberOfBeliefs() const { |
|||
return beliefs.size(); |
|||
} |
|||
|
|||
template<typename ValueType, typename BeliefState> |
|||
uint64_t NondeterministicBeliefTracker<ValueType, BeliefState>::getCurrentDimension() const { |
|||
storm::storage::BitVector support(beliefs.begin()->getSupportSize()); |
|||
for(auto const& belief : beliefs) { |
|||
belief.setSupport(support); |
|||
} |
|||
return support.getNumberOfSetBits(); |
|||
} |
|||
|
|||
|
|||
|
|||
//
|
|||
template<typename ValueType, typename BeliefState> |
|||
uint64_t NondeterministicBeliefTracker<ValueType, BeliefState>::reduce() { |
|||
reductionTimedOut = false; |
|||
std::shared_ptr<storm::utility::solver::SmtSolverFactory> solverFactory = std::make_shared<storm::utility::solver::Z3SmtSolverFactory>(); |
|||
storm::storage::geometry::ReduceVertexCloud<ValueType> rvc(solverFactory, options.wiggle, options.timeOut); |
|||
std::vector<std::map<uint64_t, ValueType>> points; |
|||
std::vector<typename std::unordered_set<BeliefState>::iterator> iterators; |
|||
for (auto it = beliefs.begin(); it != beliefs.end(); ++it) { |
|||
// TODO get rid of the getBeliefMap function.
|
|||
points.push_back(it->getBeliefMap()); |
|||
iterators.push_back(it); |
|||
} |
|||
auto res = rvc.eliminate(points, pomdp.getNumberOfStates()); |
|||
storm::storage::BitVector eliminate = ~res.first; |
|||
if (res.second) { |
|||
reductionTimedOut = true; |
|||
} |
|||
|
|||
auto selectedIterators = storm::utility::vector::filterVector(iterators, eliminate); |
|||
for (auto iter : selectedIterators) { |
|||
beliefs.erase(iter); |
|||
} |
|||
return eliminate.getNumberOfSetBits(); |
|||
} |
|||
|
|||
template<typename ValueType, typename BeliefState> |
|||
bool NondeterministicBeliefTracker<ValueType, BeliefState>::hasTimedOut() const { |
|||
return reductionTimedOut; |
|||
} |
|||
|
|||
|
|||
template class SparseBeliefState<double>; |
|||
template bool operator==(SparseBeliefState<double> const&, SparseBeliefState<double> const&); |
|||
template class NondeterministicBeliefTracker<double, SparseBeliefState<double>>; |
|||
//template class ObservationDenseBeliefState<double>;
|
|||
//template bool operator==(ObservationDenseBeliefState<double> const&, ObservationDenseBeliefState<double> const&);
|
|||
//template class NondeterministicBeliefTracker<double, ObservationDenseBeliefState<double>>;
|
|||
|
|||
template class SparseBeliefState<storm::RationalNumber>; |
|||
template bool operator==(SparseBeliefState<storm::RationalNumber> const&, SparseBeliefState<storm::RationalNumber> const&); |
|||
template class NondeterministicBeliefTracker<storm::RationalNumber, SparseBeliefState<storm::RationalNumber>>; |
|||
|
|||
} |
|||
} |
@ -0,0 +1,215 @@ |
|||
#pragma once |
|||
#include "storm/models/sparse/Pomdp.h" |
|||
|
|||
namespace storm { |
|||
namespace generator { |
|||
/** |
|||
* This class keeps track of common information of a set of beliefs. |
|||
* It also keeps a reference to the POMDP. The manager is referenced by all beliefs. |
|||
*/ |
|||
template<typename ValueType> |
|||
class BeliefStateManager { |
|||
public: |
|||
BeliefStateManager(storm::models::sparse::Pomdp<ValueType> const& pomdp); |
|||
storm::models::sparse::Pomdp<ValueType> const& getPomdp() const; |
|||
uint64_t getActionsForObservation(uint32_t observation) const; |
|||
ValueType getRisk(uint64_t) const; |
|||
void setRiskPerState(std::vector<ValueType> const& risk); |
|||
uint64_t getFreshId(); |
|||
uint32_t getObservation(uint64_t state) const; |
|||
uint64_t getObservationOffset(uint64_t state) const; |
|||
uint64_t getState(uint32_t obs, uint64_t offset) const; |
|||
uint64_t getNumberOfStates() const; |
|||
uint64_t numberOfStatesPerObservation(uint32_t observation) const; |
|||
|
|||
private: |
|||
storm::models::sparse::Pomdp<ValueType> const& pomdp; |
|||
std::vector<ValueType> riskPerState; |
|||
std::vector<uint64_t> numberActionsPerObservation; |
|||
uint64_t beliefIdCounter = 0; |
|||
std::vector<uint64_t> observationOffsetId; |
|||
std::vector<std::vector<uint64_t>> statePerObservationAndOffset; |
|||
}; |
|||
|
|||
|
|||
template<typename ValueType> |
|||
class SparseBeliefState; |
|||
template<typename ValueType> |
|||
bool operator==(SparseBeliefState<ValueType> const& lhs, SparseBeliefState<ValueType> const& rhs); |
|||
|
|||
/** |
|||
* SparseBeliefState stores beliefs in a sparse format. |
|||
*/ |
|||
template<typename ValueType> |
|||
class SparseBeliefState { |
|||
public: |
|||
SparseBeliefState(std::shared_ptr<BeliefStateManager<ValueType>> const& manager, uint64_t state); |
|||
/** |
|||
* Update the belief using the new observation |
|||
* @param newObservation |
|||
* @param previousBeliefs put the new belief in this set |
|||
*/ |
|||
void update(uint32_t newObservation, std::unordered_set<SparseBeliefState>& previousBeliefs) const; |
|||
std::size_t hash() const noexcept; |
|||
/** |
|||
* Get the estimate to be in the given state |
|||
* @param state |
|||
* @return |
|||
*/ |
|||
ValueType get(uint64_t state) const; |
|||
/** |
|||
* Get the weighted risk |
|||
* @return |
|||
*/ |
|||
ValueType getRisk() const; |
|||
|
|||
// Various getters |
|||
std::string toString() const; |
|||
bool isValid() const; |
|||
uint64_t getSupportSize() const; |
|||
void setSupport(storm::storage::BitVector&) const; |
|||
std::map<uint64_t, ValueType> const& getBeliefMap() const; |
|||
|
|||
friend bool operator==<>(SparseBeliefState<ValueType> const& lhs, SparseBeliefState<ValueType> const& rhs); |
|||
private: |
|||
void updateHelper(std::vector<std::map<uint64_t, ValueType>> const& partialBeliefs, std::vector<ValueType> const& sums, typename std::map<uint64_t, ValueType>::const_iterator nextStateIt, uint32_t newObservation, std::unordered_set<SparseBeliefState<ValueType>>& previousBeliefs) const; |
|||
SparseBeliefState(std::shared_ptr<BeliefStateManager<ValueType>> const& manager, std::map<uint64_t, ValueType> const& belief, std::size_t newHash, ValueType const& risk, uint64_t prevId); |
|||
std::shared_ptr<BeliefStateManager<ValueType>> manager; |
|||
|
|||
std::map<uint64_t, ValueType> belief; // map is ordered for unique hashing. |
|||
std::size_t prestoredhash = 0; |
|||
ValueType risk; |
|||
uint64_t id; |
|||
uint64_t prevId; |
|||
}; |
|||
|
|||
/** |
|||
* ObservationDenseBeliefState stores beliefs in a dense format (per observation). |
|||
*/ |
|||
template<typename ValueType> |
|||
class ObservationDenseBeliefState; |
|||
template<typename ValueType> |
|||
bool operator==(ObservationDenseBeliefState<ValueType> const& lhs, ObservationDenseBeliefState<ValueType> const& rhs); |
|||
|
|||
template<typename ValueType> |
|||
class ObservationDenseBeliefState { |
|||
public: |
|||
ObservationDenseBeliefState(std::shared_ptr<BeliefStateManager<ValueType>> const& manager, uint64_t state); |
|||
void update(uint32_t newObservation, std::unordered_set<ObservationDenseBeliefState>& previousBeliefs) const; |
|||
std::size_t hash() const noexcept; |
|||
ValueType get(uint64_t state) const; |
|||
ValueType getRisk() const; |
|||
std::string toString() const; |
|||
uint64_t getSupportSize() const; |
|||
void setSupport(storm::storage::BitVector&) const; |
|||
friend bool operator==<>(ObservationDenseBeliefState<ValueType> const& lhs, ObservationDenseBeliefState<ValueType> const& rhs); |
|||
private: |
|||
void updateHelper(std::vector<std::map<uint64_t, ValueType>> const& partialBeliefs, std::vector<ValueType> const& sums, uint64_t currentEntry, uint32_t newObservation, std::unordered_set<ObservationDenseBeliefState<ValueType>>& previousBeliefs) const; |
|||
ObservationDenseBeliefState(std::shared_ptr<BeliefStateManager<ValueType>> const& manager, uint32_t observation, std::vector<ValueType> const& belief, std::size_t newHash, ValueType const& risk, uint64_t prevId); |
|||
std::shared_ptr<BeliefStateManager<ValueType>> manager; |
|||
|
|||
std::vector<ValueType> belief; |
|||
uint64_t observation = 0; |
|||
std::size_t prestoredhash = 0; |
|||
ValueType risk; |
|||
uint64_t id; |
|||
uint64_t prevId; |
|||
}; |
|||
|
|||
/** |
|||
* This tracker implements state estimation for POMDPs. |
|||
* This corresponds to forward filtering in Junges, Torfah, Seshia. |
|||
* |
|||
* @tparam ValueType How are probabilities stored |
|||
* @tparam BeliefState What format to use for beliefs |
|||
*/ |
|||
template<typename ValueType, typename BeliefState> |
|||
class NondeterministicBeliefTracker { |
|||
public: |
|||
struct Options { |
|||
uint64_t trackTimeOut = 0; |
|||
uint64_t timeOut = 0; // for reduction, in milliseconds, 0 is no timeout |
|||
ValueType wiggle; // tolerance, anything above 0 means that we are incomplete. |
|||
}; |
|||
NondeterministicBeliefTracker(storm::models::sparse::Pomdp<ValueType> const& pomdp, typename NondeterministicBeliefTracker<ValueType, BeliefState>::Options options = Options()); |
|||
/** |
|||
* Start with a new trace. |
|||
* @param observation The initial observation to start with. |
|||
* @return |
|||
*/ |
|||
bool reset(uint32_t observation); |
|||
/** |
|||
* Extend the observed trace with the new observation |
|||
* @param newObservation |
|||
* @return |
|||
*/ |
|||
bool track(uint64_t newObservation); |
|||
/** |
|||
* Provides access to the current beliefs. |
|||
* @return |
|||
*/ |
|||
std::unordered_set<BeliefState> const& getCurrentBeliefs() const; |
|||
/** |
|||
* What was the last obervation that we made? |
|||
* @return |
|||
*/ |
|||
uint32_t getCurrentObservation() const; |
|||
/** |
|||
* How many beliefs are we currently tracking? |
|||
* @return |
|||
*/ |
|||
uint64_t getNumberOfBeliefs() const; |
|||
/** |
|||
* What is the (worst-case/best-case) risk over all beliefs |
|||
* @param max Should we take the max or the min? |
|||
* @return |
|||
*/ |
|||
ValueType getCurrentRisk(bool max=true); |
|||
/** |
|||
* Sets the state-risk to use for all beliefs. |
|||
* @param risk |
|||
*/ |
|||
void setRisk(std::vector<ValueType> const& risk); |
|||
/** |
|||
* What is the dimension of the current set of beliefs, i.e., |
|||
* what is the number of states we could possibly be in? |
|||
* @return |
|||
*/ |
|||
uint64_t getCurrentDimension() const; |
|||
/** |
|||
* Apply reductions to the belief state |
|||
* @return |
|||
*/ |
|||
uint64_t reduce(); |
|||
/** |
|||
* Did we time out during the computation? |
|||
* @return |
|||
*/ |
|||
bool hasTimedOut() const; |
|||
|
|||
private: |
|||
storm::models::sparse::Pomdp<ValueType> const& pomdp; |
|||
std::shared_ptr<BeliefStateManager<ValueType>> manager; |
|||
std::unordered_set<BeliefState> beliefs; |
|||
bool reductionTimedOut = false; |
|||
Options options; |
|||
uint32_t lastObservation; |
|||
}; |
|||
} |
|||
} |
|||
|
|||
// |
|||
namespace std { |
|||
template<typename T> |
|||
struct hash<storm::generator::SparseBeliefState<T>> { |
|||
std::size_t operator()(storm::generator::SparseBeliefState<T> const& s) const noexcept { |
|||
return s.hash(); |
|||
} |
|||
}; |
|||
template<typename T> |
|||
struct hash<storm::generator::ObservationDenseBeliefState<T>> { |
|||
std::size_t operator()(storm::generator::ObservationDenseBeliefState<T> const& s) const noexcept { |
|||
return s.hash(); |
|||
} |
|||
}; |
|||
} |
@ -0,0 +1,202 @@ |
|||
#include "storm/exceptions/InvalidArgumentException.h"
|
|||
#include "storm-pomdp/transformer/ObservationTraceUnfolder.h"
|
|||
#include "storm/storage/expressions/ExpressionManager.h"
|
|||
#include "storm/utility/ConstantsComparator.h"
|
|||
|
|||
#undef _VERBOSE_OBSERVATION_UNFOLDING
|
|||
|
|||
namespace storm { |
|||
namespace pomdp { |
|||
template<typename ValueType> |
|||
ObservationTraceUnfolder<ValueType>::ObservationTraceUnfolder(storm::models::sparse::Pomdp<ValueType> const& model, std::vector<ValueType> const& risk, |
|||
std::shared_ptr<storm::expressions::ExpressionManager>& exprManager) : model(model), risk(risk), exprManager(exprManager) { |
|||
statesPerObservation = std::vector<storm::storage::BitVector>(model.getNrObservations(), storm::storage::BitVector(model.getNumberOfStates())); |
|||
for (uint64_t state = 0; state < model.getNumberOfStates(); ++state) { |
|||
statesPerObservation[model.getObservation(state)].set(state, true); |
|||
} |
|||
svvar = exprManager->declareFreshIntegerVariable(false, "_s"); |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
std::shared_ptr<storm::models::sparse::Mdp<ValueType>> ObservationTraceUnfolder<ValueType>::transform( |
|||
const std::vector<uint32_t> &observations) { |
|||
std::vector<uint32_t> modifiedObservations = observations; |
|||
// First observation should be special.
|
|||
// This just makes the algorithm simpler because we do not treat the first step as a special case later.
|
|||
modifiedObservations[0] = model.getNrObservations(); |
|||
|
|||
storm::storage::BitVector initialStates = model.getInitialStates(); |
|||
storm::storage::BitVector actualInitialStates = initialStates; |
|||
for (uint64_t state : initialStates) { |
|||
if (model.getObservation(state) != observations[0]) { |
|||
actualInitialStates.set(state, false); |
|||
} |
|||
} |
|||
STORM_LOG_THROW(actualInitialStates.getNumberOfSetBits() == 1, storm::exceptions::InvalidArgumentException, "Must have unique initial state matching the observation"); |
|||
//
|
|||
statesPerObservation.resize(model.getNrObservations() + 1); |
|||
statesPerObservation[model.getNrObservations()] = actualInitialStates; |
|||
|
|||
#ifdef _VERBOSE_OBSERVATION_UNFOLDING
|
|||
std::cout << "build valution builder.." << std::endl; |
|||
#endif
|
|||
storm::storage::sparse::StateValuationsBuilder svbuilder; |
|||
svbuilder.addVariable(svvar); |
|||
|
|||
std::map<uint64_t,uint64_t> unfoldedToOld; |
|||
std::map<uint64_t,uint64_t> unfoldedToOldNextStep; |
|||
std::map<uint64_t,uint64_t> oldToUnfolded; |
|||
|
|||
#ifdef _VERBOSE_OBSERVATION_UNFOLDING
|
|||
std::cout << "start buildiing matrix..." << std::endl; |
|||
#endif
|
|||
|
|||
// Add this initial state state:
|
|||
unfoldedToOldNextStep[0] = actualInitialStates.getNextSetIndex(0); |
|||
|
|||
storm::storage::SparseMatrixBuilder<ValueType> transitionMatrixBuilder(0,0,0,true,true); |
|||
uint64_t newStateIndex = 1; |
|||
uint64_t newRowGroupStart = 0; |
|||
uint64_t newRowCount = 0; |
|||
// Notice that we are going to use a special last step
|
|||
|
|||
for (uint64_t step = 0; step < observations.size() - 1; ++step) { |
|||
oldToUnfolded.clear(); |
|||
unfoldedToOld = unfoldedToOldNextStep; |
|||
unfoldedToOldNextStep.clear(); |
|||
|
|||
for (auto const& unfoldedToOldEntry : unfoldedToOld) { |
|||
transitionMatrixBuilder.newRowGroup(newRowGroupStart); |
|||
#ifdef _VERBOSE_OBSERVATION_UNFOLDING
|
|||
std::cout << "\tconsider new state " << unfoldedToOldEntry.first << std::endl; |
|||
#endif
|
|||
assert(step == 0 || newRowCount == transitionMatrixBuilder.getLastRow() + 1); |
|||
svbuilder.addState(unfoldedToOldEntry.first, {}, {static_cast<int64_t>(unfoldedToOldEntry.second)}); |
|||
uint64_t oldRowIndexStart = model.getNondeterministicChoiceIndices()[unfoldedToOldEntry.second]; |
|||
uint64_t oldRowIndexEnd = model.getNondeterministicChoiceIndices()[unfoldedToOldEntry.second+1]; |
|||
|
|||
for (uint64_t oldRowIndex = oldRowIndexStart; oldRowIndex != oldRowIndexEnd; oldRowIndex++) { |
|||
#ifdef _VERBOSE_OBSERVATION_UNFOLDING
|
|||
std::cout << "\t\tconsider old action " << oldRowIndex << std::endl; |
|||
std::cout << "\t\tconsider new row nr " << newRowCount << std::endl; |
|||
#endif
|
|||
|
|||
ValueType resetProb = storm::utility::zero<ValueType>(); |
|||
// We first find the reset probability
|
|||
for (auto const &oldRowEntry : model.getTransitionMatrix().getRow(oldRowIndex)) { |
|||
if (model.getObservation(oldRowEntry.getColumn()) != observations[step + 1]) { |
|||
resetProb += oldRowEntry.getValue(); |
|||
} |
|||
} |
|||
#ifdef _VERBOSE_OBSERVATION_UNFOLDING
|
|||
std::cout << "\t\t\t add reset with probability " << resetProb << std::endl; |
|||
#endif
|
|||
|
|||
// Add the resets
|
|||
if (resetProb != storm::utility::zero<ValueType>()) { |
|||
transitionMatrixBuilder.addNextValue(newRowCount, 0, resetProb); |
|||
} |
|||
#ifdef _VERBOSE_OBSERVATION_UNFOLDING
|
|||
std::cout << "\t\t\t add other transitions..." << std::endl; |
|||
#endif
|
|||
|
|||
// Now, we build the outgoing transitions.
|
|||
for (auto const &oldRowEntry : model.getTransitionMatrix().getRow(oldRowIndex)) { |
|||
if (model.getObservation(oldRowEntry.getColumn()) != observations[step + 1]) { |
|||
continue;// already handled.
|
|||
} |
|||
uint64_t column = 0; |
|||
|
|||
auto entryIt = oldToUnfolded.find(oldRowEntry.getColumn()); |
|||
if (entryIt == oldToUnfolded.end()) { |
|||
column = newStateIndex; |
|||
oldToUnfolded[oldRowEntry.getColumn()] = column; |
|||
unfoldedToOldNextStep[column] = oldRowEntry.getColumn(); |
|||
newStateIndex++; |
|||
} else { |
|||
column = entryIt->second; |
|||
} |
|||
#ifdef _VERBOSE_OBSERVATION_UNFOLDING
|
|||
std::cout << "\t\t\t\t transition to " << column << "with probability " << oldRowEntry.getValue() << std::endl; |
|||
#endif
|
|||
transitionMatrixBuilder.addNextValue(newRowCount, column, |
|||
oldRowEntry.getValue()); |
|||
} |
|||
newRowCount++; |
|||
} |
|||
|
|||
newRowGroupStart = transitionMatrixBuilder.getLastRow() + 1; |
|||
|
|||
} |
|||
} |
|||
// Now, take care of the last step.
|
|||
uint64_t sinkState = newStateIndex; |
|||
uint64_t targetState = newStateIndex + 1; |
|||
auto cc = storm::utility::ConstantsComparator<ValueType>(); |
|||
for (auto const& unfoldedToOldEntry : unfoldedToOldNextStep) { |
|||
svbuilder.addState(unfoldedToOldEntry.first, {}, {static_cast<int64_t>(unfoldedToOldEntry.second)}); |
|||
|
|||
transitionMatrixBuilder.newRowGroup(newRowGroupStart); |
|||
STORM_LOG_ASSERT(risk.size() > unfoldedToOldEntry.second, "Must be a state"); |
|||
STORM_LOG_ASSERT(!cc.isLess(storm::utility::one<ValueType>(), risk[unfoldedToOldEntry.second]), "Risk must be a probability"); |
|||
STORM_LOG_ASSERT(!cc.isLess(risk[unfoldedToOldEntry.second], storm::utility::zero<ValueType>()), "Risk must be a probability"); |
|||
//std::cout << "risk is" << risk[unfoldedToOldEntry.second] << std::endl;
|
|||
if (!storm::utility::isOne(risk[unfoldedToOldEntry.second])) { |
|||
transitionMatrixBuilder.addNextValue(newRowGroupStart, sinkState, |
|||
storm::utility::one<ValueType>() - risk[unfoldedToOldEntry.second]); |
|||
} |
|||
if (!storm::utility::isZero(risk[unfoldedToOldEntry.second])) { |
|||
transitionMatrixBuilder.addNextValue(newRowGroupStart, targetState, |
|||
risk[unfoldedToOldEntry.second]); |
|||
} |
|||
newRowGroupStart++; |
|||
} |
|||
// sink state
|
|||
transitionMatrixBuilder.newRowGroup(newRowGroupStart); |
|||
transitionMatrixBuilder.addNextValue(newRowGroupStart, sinkState, storm::utility::one<ValueType>()); |
|||
svbuilder.addState(sinkState, {}, {-1}); |
|||
|
|||
newRowGroupStart++; |
|||
transitionMatrixBuilder.newRowGroup(newRowGroupStart); |
|||
// target state
|
|||
transitionMatrixBuilder.addNextValue(newRowGroupStart, targetState, storm::utility::one<ValueType>()); |
|||
svbuilder.addState(targetState, {}, {-1}); |
|||
#ifdef _VERBOSE_OBSERVATION_UNFOLDING
|
|||
std::cout << "build matrix..." << std::endl; |
|||
#endif
|
|||
|
|||
storm::storage::sparse::ModelComponents<ValueType> components; |
|||
components.transitionMatrix = transitionMatrixBuilder.build(); |
|||
#ifdef _VERBOSE_OBSERVATION_UNFOLDING
|
|||
std::cout << components.transitionMatrix << std::endl; |
|||
#endif
|
|||
STORM_LOG_ASSERT(components.transitionMatrix.getRowGroupCount() == targetState + 1, "Expect row group count (" << components.transitionMatrix.getRowGroupCount() << ") one more as target state index " << targetState << ")"); |
|||
|
|||
storm::models::sparse::StateLabeling labeling(components.transitionMatrix.getRowGroupCount()); |
|||
labeling.addLabel("_goal"); |
|||
labeling.addLabelToState("_goal", targetState); |
|||
labeling.addLabel("init"); |
|||
labeling.addLabelToState("init", 0); |
|||
components.stateLabeling = labeling; |
|||
components.stateValuations = svbuilder.build( components.transitionMatrix.getRowGroupCount()); |
|||
return std::make_shared<storm::models::sparse::Mdp<ValueType>>(std::move(components)); |
|||
|
|||
} |
|||
|
|||
template<typename ValueType> |
|||
std::shared_ptr<storm::models::sparse::Mdp<ValueType>> ObservationTraceUnfolder<ValueType>::extend(uint32_t observation) { |
|||
traceSoFar.push_back(observation); |
|||
return transform(traceSoFar); |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
void ObservationTraceUnfolder<ValueType>::reset(uint32_t observation) { |
|||
traceSoFar = {observation}; |
|||
} |
|||
|
|||
template class ObservationTraceUnfolder<double>; |
|||
template class ObservationTraceUnfolder<storm::RationalNumber>; |
|||
template class ObservationTraceUnfolder<storm::RationalFunction>; |
|||
|
|||
} |
|||
} |
@ -0,0 +1,49 @@ |
|||
#include "storm/models/sparse/Pomdp.h" |
|||
|
|||
namespace storm { |
|||
namespace pomdp { |
|||
/** |
|||
* Observation-trace unrolling to allow model checking for monitoring. |
|||
* This approach is outlined in Junges, Hazem, Seshia -- Runtime Monitoring for Markov Decision Processes |
|||
* @tparam ValueType ValueType for probabilities |
|||
*/ |
|||
template<typename ValueType> |
|||
class ObservationTraceUnfolder { |
|||
|
|||
public: |
|||
/** |
|||
* Initialize |
|||
* @param model the MDP with state-based observations |
|||
* @param risk the state risk |
|||
* @param exprManager an Expression Manager |
|||
*/ |
|||
ObservationTraceUnfolder(storm::models::sparse::Pomdp<ValueType> const& model, std::vector<ValueType> const& risk, std::shared_ptr<storm::expressions::ExpressionManager>& exprManager); |
|||
/** |
|||
* Transform in one shot |
|||
* @param observations |
|||
* @return |
|||
*/ |
|||
std::shared_ptr<storm::models::sparse::Mdp<ValueType>> transform(std::vector<uint32_t> const& observations); |
|||
/** |
|||
* Transform incrementaly |
|||
* @param observation |
|||
* @return |
|||
*/ |
|||
std::shared_ptr<storm::models::sparse::Mdp<ValueType>> extend(uint32_t observation); |
|||
/** |
|||
* When using the incremental approach, reset the observations made so far. |
|||
* @param observation The new initial observation |
|||
*/ |
|||
void reset(uint32_t observation); |
|||
private: |
|||
storm::models::sparse::Pomdp<ValueType> const& model; |
|||
std::vector<ValueType> risk; // TODO reconsider holding this as a reference, but there were some strange bugs |
|||
std::shared_ptr<storm::expressions::ExpressionManager>& exprManager; |
|||
std::vector<storm::storage::BitVector> statesPerObservation; |
|||
std::vector<uint32_t> traceSoFar; |
|||
storm::expressions::Variable svvar; |
|||
|
|||
}; |
|||
|
|||
} |
|||
} |
@ -0,0 +1,138 @@ |
|||
#include "storm/storage/geometry/ReduceVertexCloud.h"
|
|||
#include "storm/utility/Stopwatch.h"
|
|||
#undef _DEBUG_REDUCE_VERTEX_CLOUD
|
|||
|
|||
|
|||
namespace storm { |
|||
namespace storage { |
|||
namespace geometry { |
|||
|
|||
template<typename ValueType> |
|||
std::string toString(std::map<uint64_t, ValueType> const& point) { |
|||
std::stringstream sstr; |
|||
bool first = true; |
|||
for (auto const& entry : point) { |
|||
if (first) { |
|||
first = false; |
|||
} else { |
|||
sstr << ", "; |
|||
} |
|||
sstr << entry.first << " : " << entry.second; |
|||
} |
|||
return sstr.str(); |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
std::pair<storm::storage::BitVector, bool> ReduceVertexCloud<ValueType>::eliminate(std::vector<std::map<uint64_t, ValueType>> const& input, uint64_t maxdimension) { |
|||
std::shared_ptr<storm::expressions::ExpressionManager> expressionManager = std::make_shared<storm::expressions::ExpressionManager>(); |
|||
std::vector<storm::storage::BitVector> supports; |
|||
std::vector<storm::expressions::Variable> weightVariables; |
|||
std::vector<storm::expressions::Expression> weightVariableExpressions; |
|||
|
|||
for (uint64_t pointIndex = 0; pointIndex < input.size(); ++pointIndex) { |
|||
|
|||
// Compute the support vectors to quickly determine which input points could be relevant.
|
|||
supports.push_back(storm::storage::BitVector(maxdimension)); |
|||
for (auto const& entry : input[pointIndex]) { |
|||
supports.back().set(entry.first, true); |
|||
} |
|||
// Add a weight variable for each input point
|
|||
weightVariables.push_back(expressionManager->declareRationalVariable("w_"+ std::to_string(pointIndex))); |
|||
// For convenience and performance, obtain the expression.
|
|||
weightVariableExpressions.push_back(weightVariables.back().getExpression()); |
|||
} |
|||
|
|||
std::unique_ptr<storm::solver::SmtSolver> smtSolver = smtSolverFactory->create(*expressionManager); |
|||
for (auto const& weightVariableExpr : weightVariableExpressions) { |
|||
//smtSolver->add((weightVariableExpr == expressionManager->rational(0.0)) || (weightVariableExpr > expressionManager->rational(0.00001)));
|
|||
smtSolver->add((weightVariableExpr >= expressionManager->rational(0.0))); |
|||
smtSolver->add(weightVariableExpr < expressionManager->rational(1.0)); |
|||
} |
|||
if (storm::utility::isZero(wiggle)) { |
|||
smtSolver->add(storm::expressions::sum(weightVariableExpressions) <= |
|||
expressionManager->rational(1)); |
|||
} else { |
|||
smtSolver->add(storm::expressions::sum(weightVariableExpressions) <= |
|||
expressionManager->rational(1.0 + wiggle)); |
|||
smtSolver->add(storm::expressions::sum(weightVariableExpressions) >= |
|||
expressionManager->rational(1 - wiggle)); |
|||
} |
|||
|
|||
storm::utility::Stopwatch solverTime; |
|||
storm::utility::Stopwatch totalTime(true); |
|||
storm::storage::BitVector vertices(input.size()); |
|||
for (uint64_t pointIndex = 0; pointIndex < input.size(); ++pointIndex) { |
|||
#ifdef _DEBUG_REUCE_VERTEX_CLOUD
|
|||
std::cout << pointIndex << " out of " << input.size() << std::endl; |
|||
#endif
|
|||
smtSolver->push(); |
|||
std::map<uint64_t, std::vector<storm::expressions::Expression>> dimensionTerms; |
|||
for (auto const& entry : input[pointIndex]) { |
|||
dimensionTerms[entry.first] = {expressionManager->rational(-entry.second)}; |
|||
} |
|||
for (uint64_t potentialSupport = 0; potentialSupport < input.size(); ++potentialSupport) { |
|||
if (pointIndex == potentialSupport) { |
|||
smtSolver->add(weightVariableExpressions[potentialSupport] == expressionManager->rational(0.0)); |
|||
} else if (potentialSupport < pointIndex && !vertices.get(potentialSupport)) { |
|||
smtSolver->add(weightVariableExpressions[potentialSupport] == expressionManager->rational(0.0)); |
|||
} else if (supports[potentialSupport].isSubsetOf(supports[pointIndex])) { |
|||
for (auto const& entry : input[potentialSupport]) { |
|||
dimensionTerms[entry.first].push_back(weightVariableExpressions[potentialSupport] * expressionManager->rational(entry.second)); |
|||
} |
|||
} else { |
|||
smtSolver->add(weightVariableExpressions[potentialSupport] == expressionManager->rational(0.0)); |
|||
} |
|||
} |
|||
for (auto const& entry : dimensionTerms) { |
|||
smtSolver->add(storm::expressions::sum(entry.second) == expressionManager->rational(0.0)); |
|||
} |
|||
|
|||
solverTime.start(); |
|||
auto result = smtSolver->check(); |
|||
solverTime.stop(); |
|||
if (result == storm::solver::SmtSolver::CheckResult::Unsat) { |
|||
#ifdef _DEBUG_REDUCE_VERTEX_CLOUD
|
|||
if (input[pointIndex].size() == 2) { |
|||
std::cout << "point " << toString(input[pointIndex]) << " is a vertex:"; |
|||
std::cout << smtSolver->getSmtLibString() << std::endl; |
|||
} |
|||
#endif
|
|||
vertices.set(pointIndex, true); |
|||
} |
|||
#ifdef _DEBUG_REDUCE_VERTEX_CLOUD
|
|||
else |
|||
{ |
|||
std::cout << "point " << toString(input[pointIndex]) << " is a convex combination of "; |
|||
auto val = smtSolver->getModelAsValuation(); |
|||
uint64_t varIndex = 0; |
|||
for (auto const& wvar : weightVariables) { |
|||
if (!storm::utility::isZero(val.getRationalValue(wvar))) { |
|||
std::cout << toString(input[varIndex]) << " (weight: " << val.getRationalValue(wvar) << ")"; |
|||
} |
|||
varIndex++; |
|||
} |
|||
std::cout << std::endl; |
|||
} |
|||
if (timeOut > ) |
|||
#endif
|
|||
if (timeOut > 0 && totalTime.getTimeInMilliseconds() > timeOut) { |
|||
for (uint64_t remainingPoint = pointIndex + 1; remainingPoint < input.size(); ++remainingPoint) { |
|||
vertices.set(remainingPoint); |
|||
} |
|||
return {vertices, true}; |
|||
} |
|||
smtSolver->pop(); |
|||
#ifdef _DEBUG_REDUCE_VERTEX_CLOUD
|
|||
std::cout << "Solver time " << solverTime.getTimeInMilliseconds() << std::endl; |
|||
std::cout << "Total time " << totalTime.getTimeInMilliseconds() << std::endl; |
|||
#endif
|
|||
} |
|||
return {vertices, false}; |
|||
|
|||
} |
|||
|
|||
template class ReduceVertexCloud<double>; |
|||
template class ReduceVertexCloud<storm::RationalNumber>; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,36 @@ |
|||
#pragma once |
|||
|
|||
#include "storm/storage/BitVector.h" |
|||
#include "storm/solver/SmtSolver.h" |
|||
#include "storm/utility/solver.h" |
|||
|
|||
namespace storm { |
|||
namespace storage { |
|||
namespace geometry { |
|||
template<typename ValueType> |
|||
class ReduceVertexCloud { |
|||
public: |
|||
/*! |
|||
* |
|||
* @param smtSolverFactory |
|||
* @param wiggle |
|||
* @param timeout: Maximal time in milliseconds, 0 is no timeout |
|||
*/ |
|||
ReduceVertexCloud(std::shared_ptr<storm::utility::solver::SmtSolverFactory>& smtSolverFactory, ValueType wiggle = storm::utility::convertNumber<ValueType>(0.0), uint64_t timeout = 0) |
|||
: smtSolverFactory(smtSolverFactory), wiggle(wiggle), timeOut(timeout) |
|||
{ |
|||
|
|||
} |
|||
|
|||
std::pair<storm::storage::BitVector,bool> eliminate(std::vector<std::map<uint64_t, ValueType>> const& input, uint64_t maxdimension); |
|||
|
|||
private: |
|||
std::shared_ptr<storm::utility::solver::SmtSolverFactory>& smtSolverFactory; |
|||
ValueType wiggle; |
|||
uint64_t timeOut; |
|||
|
|||
}; |
|||
|
|||
} |
|||
} |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue