387 lines
22 KiB

#include "storm/utility/vector.h"
#include "storm/storage/Scheduler.h"
#include "storm/utility/macros.h"
#include "storm/adapters/JsonAdapter.h"
#include "storm/exceptions/NotImplementedException.h"
#include <boost/algorithm/string/join.hpp>
namespace storm {
namespace storage {
template <typename ValueType>
Scheduler<ValueType>::Scheduler(uint_fast64_t numberOfModelStates, boost::optional<storm::storage::MemoryStructure> const& memoryStructure) : memoryStructure(memoryStructure) {
uint_fast64_t numOfMemoryStates = memoryStructure ? memoryStructure->getNumberOfStates() : 1;
schedulerChoices = std::vector<std::vector<SchedulerChoice<ValueType>>>(numOfMemoryStates, std::vector<SchedulerChoice<ValueType>>(numberOfModelStates));
dontCareStates = std::vector<storm::storage::BitVector>(numOfMemoryStates, storm::storage::BitVector(numberOfModelStates, false));
numOfUndefinedChoices = numOfMemoryStates * numberOfModelStates;
numOfDeterministicChoices = 0;
numOfDontCareStates = 0;
}
template <typename ValueType>
Scheduler<ValueType>::Scheduler(uint_fast64_t numberOfModelStates, boost::optional<storm::storage::MemoryStructure>&& memoryStructure) : memoryStructure(std::move(memoryStructure)) {
uint_fast64_t numOfMemoryStates = this->memoryStructure ? this->memoryStructure->getNumberOfStates() : 1;
schedulerChoices = std::vector<std::vector<SchedulerChoice<ValueType>>>(numOfMemoryStates, std::vector<SchedulerChoice<ValueType>>(numberOfModelStates));
dontCareStates = std::vector<storm::storage::BitVector>(numOfMemoryStates, storm::storage::BitVector(numberOfModelStates, false));
numOfUndefinedChoices = numOfMemoryStates * numberOfModelStates;
numOfDeterministicChoices = 0;
numOfDontCareStates = 0;
}
template <typename ValueType>
void Scheduler<ValueType>::setChoice(SchedulerChoice<ValueType> const& choice, uint_fast64_t modelState, uint_fast64_t memoryState) {
STORM_LOG_ASSERT(memoryState < getNumberOfMemoryStates(), "Illegal memory state index");
STORM_LOG_ASSERT(modelState < schedulerChoices[memoryState].size(), "Illegal model state index");
auto& schedulerChoice = schedulerChoices[memoryState][modelState];
if (schedulerChoice.isDefined()) {
if (!choice.isDefined()) {
++numOfUndefinedChoices;
}
} else {
if (choice.isDefined()) {
assert(numOfUndefinedChoices > 0);
--numOfUndefinedChoices;
}
}
if (schedulerChoice.isDeterministic()) {
if (!choice.isDeterministic()) {
assert(numOfDeterministicChoices > 0);
--numOfDeterministicChoices;
}
} else {
if (choice.isDeterministic()) {
++numOfDeterministicChoices;
}
}
schedulerChoice = choice;
}
template <typename ValueType>
bool Scheduler<ValueType>::isChoiceSelected(BitVector const& selectedStates, uint64_t memoryState) const {
for (auto selectedState : selectedStates) {
auto& schedulerChoice = schedulerChoices[memoryState][selectedState];
if (!schedulerChoice.isDefined()) {
return false;
}
}
return true;
}
template <typename ValueType>
void Scheduler<ValueType>::clearChoice(uint_fast64_t modelState, uint_fast64_t memoryState) {
STORM_LOG_ASSERT(memoryState < getNumberOfMemoryStates(), "Illegal memory state index");
STORM_LOG_ASSERT(modelState < schedulerChoices[memoryState].size(), "Illegal model state index");
setChoice(SchedulerChoice<ValueType>(), modelState, memoryState);
}
template <typename ValueType>
SchedulerChoice<ValueType> const& Scheduler<ValueType>::getChoice(uint_fast64_t modelState, uint_fast64_t memoryState) const {
STORM_LOG_ASSERT(memoryState < getNumberOfMemoryStates(), "Illegal memory state index");
STORM_LOG_ASSERT(modelState < schedulerChoices[memoryState].size(), "Illegal model state index");
return schedulerChoices[memoryState][modelState];
}
template <typename ValueType>
void Scheduler<ValueType>::setDontCare(uint_fast64_t modelState, uint_fast64_t memoryState, bool setArbitraryChoice) {
STORM_LOG_ASSERT(memoryState < getNumberOfMemoryStates(), "Illegal memory state index");
STORM_LOG_ASSERT(modelState < schedulerChoices[memoryState].size(), "Illegal model state index");
if (!dontCareStates[memoryState].get(modelState)) {
auto& schedulerChoice = schedulerChoices[memoryState][modelState];
if (!schedulerChoice.isDefined() && setArbitraryChoice) {
// Set an arbitrary choice
this->setChoice(0, modelState, memoryState);
}
dontCareStates[memoryState].set(modelState, true);
++numOfDontCareStates;
}
}
template <typename ValueType>
void Scheduler<ValueType>::unSetDontCare(uint_fast64_t modelState, uint_fast64_t memoryState) {
STORM_LOG_ASSERT(memoryState < getNumberOfMemoryStates(), "Illegal memory state index");
STORM_LOG_ASSERT(modelState < schedulerChoices[memoryState].size(), "Illegal model state index");
if (dontCareStates[memoryState].get(modelState)) {
dontCareStates[memoryState].set(modelState, false);
--numOfDontCareStates;
}
}
template <typename ValueType>
bool Scheduler<ValueType>::isDontCare(uint_fast64_t modelState, uint64_t memoryState) const {
return dontCareStates[memoryState].get(modelState);
}
template<typename ValueType>
storm::storage::BitVector Scheduler<ValueType>::computeActionSupport(std::vector<uint_fast64_t> const& nondeterministicChoiceIndices) const {
auto nrActions = nondeterministicChoiceIndices.back();
storm::storage::BitVector result(nrActions);
for (auto const& choicesPerMemoryNode : schedulerChoices) {
STORM_LOG_ASSERT(nondeterministicChoiceIndices.size()-2 < choicesPerMemoryNode.size(), "Illegal model state index");
for (uint64_t stateId = 0; stateId < nondeterministicChoiceIndices.size()-1; ++stateId) {
for (auto const& schedChoice : choicesPerMemoryNode[stateId].getChoiceAsDistribution()) {
STORM_LOG_ASSERT(schedChoice.first < nondeterministicChoiceIndices[stateId+1] - nondeterministicChoiceIndices[stateId], "Scheduler chooses action indexed " << schedChoice.first << " in state id " << stateId << " but state contains only " << nondeterministicChoiceIndices[stateId+1] - nondeterministicChoiceIndices[stateId] << " choices .");
result.set(nondeterministicChoiceIndices[stateId] + schedChoice.first);
}
}
}
return result;
}
template <typename ValueType>
bool Scheduler<ValueType>::isPartialScheduler() const {
return numOfUndefinedChoices != 0;
}
template <typename ValueType>
bool Scheduler<ValueType>::isDeterministicScheduler() const {
return numOfDeterministicChoices == (schedulerChoices.size() * schedulerChoices.begin()->size()) - numOfUndefinedChoices;
}
template <typename ValueType>
bool Scheduler<ValueType>::isMemorylessScheduler() const {
return getNumberOfMemoryStates() == 1;
}
template <typename ValueType>
uint_fast64_t Scheduler<ValueType>::getNumberOfMemoryStates() const {
return memoryStructure ? memoryStructure->getNumberOfStates() : 1;
}
template <typename ValueType>
boost::optional<storm::storage::MemoryStructure> const& Scheduler<ValueType>::getMemoryStructure() const {
return memoryStructure;
}
template <typename ValueType>
void Scheduler<ValueType>::printToStream(std::ostream& out, std::shared_ptr<storm::models::sparse::Model<ValueType>> model, bool skipUniqueChoices, bool skipDontCareStates) const {
STORM_LOG_THROW(model == nullptr || model->getNumberOfStates() == schedulerChoices.front().size(), storm::exceptions::InvalidOperationException, "The given model is not compatible with this scheduler.");
bool const stateValuationsGiven = model != nullptr && model->hasStateValuations();
bool const choiceLabelsGiven = model != nullptr && model->hasChoiceLabeling();
bool const choiceOriginsGiven = model != nullptr && model->hasChoiceOrigins();
uint_fast64_t widthOfStates = std::to_string(schedulerChoices.front().size()).length();
if (stateValuationsGiven) {
widthOfStates += model->getStateValuations().getStateInfo(schedulerChoices.front().size() - 1).length() + 5;
}
widthOfStates = std::max(widthOfStates, (uint_fast64_t)12);
uint_fast64_t numOfSkippedStatesWithUniqueChoice = 0;
out << "___________________________________________________________________" << std::endl;
out << (isPartialScheduler() ? "Partially" : "Fully") << " defined ";
out << (isMemorylessScheduler() ? "memoryless " : "");
out << (isDeterministicScheduler() ? "deterministic" : "randomized") << " scheduler";
if (!isMemorylessScheduler()) {
out << " with " << getNumberOfMemoryStates() << " memory states";
}
out << ":" << std::endl;
STORM_LOG_WARN_COND(!(skipUniqueChoices && model == nullptr), "Can not skip unique choices if the model is not given.");
out << std::setw(widthOfStates) << "model state:" << " " << (isMemorylessScheduler() ? "" : " memory: ") << "choice(s)" << (isMemorylessScheduler() ? "" : " memory updates: ") << std::endl;
for (uint_fast64_t state = 0; state < schedulerChoices.front().size(); ++state) {
std::stringstream stateString;
// Check whether the state is skipped
if (skipUniqueChoices && model != nullptr && model->getTransitionMatrix().getRowGroupSize(state) == 1) {
++numOfSkippedStatesWithUniqueChoice;
continue;
}
// Print the state info
if (stateValuationsGiven) {
stateString << std::setw(widthOfStates) << (std::to_string(state) + ": " + model->getStateValuations().getStateInfo(state));
} else {
stateString << std::setw(widthOfStates) << state;
}
stateString << " ";
bool firstMemoryState = true;
for (uint_fast64_t memoryState = 0; memoryState < getNumberOfMemoryStates(); ++memoryState) {
// Ignore dontCare states
if(skipDontCareStates && isDontCare(state, memoryState)) {
continue;
}
// Indent if this is not the first memory state
if (firstMemoryState) {
firstMemoryState = false;
} else {
stateString << std::setw(widthOfStates) << "";
stateString << " ";
}
// Print the memory state info
if (!isMemorylessScheduler()) {
stateString << "m" << std::setw(8) << memoryState;
}
stateString << " ";
// Print choice info
SchedulerChoice<ValueType> const& choice = schedulerChoices[memoryState][state];
if (choice.isDefined()) {
if (choice.isDeterministic()) {
if (choiceOriginsGiven) {
stateString << model->getChoiceOrigins()->getChoiceInfo(model->getTransitionMatrix().getRowGroupIndices()[state] + choice.getDeterministicChoice());
} else {
stateString << choice.getDeterministicChoice();
}
if (choiceLabelsGiven) {
auto choiceLabels = model->getChoiceLabeling().getLabelsOfChoice(model->getTransitionMatrix().getRowGroupIndices()[state] + choice.getDeterministicChoice());
stateString << " {" << boost::join(choiceLabels, ", ") << "}";
}
} else {
bool firstChoice = true;
for (auto const& choiceProbPair : choice.getChoiceAsDistribution()) {
if (firstChoice) {
firstChoice = false;
} else {
stateString << " + ";
}
stateString << choiceProbPair.second << ": (";
if (choiceOriginsGiven) {
stateString << model->getChoiceOrigins()->getChoiceInfo(model->getTransitionMatrix().getRowGroupIndices()[state] + choiceProbPair.first);
} else {
stateString << choiceProbPair.first;
}
if (choiceLabelsGiven) {
auto choiceLabels = model->getChoiceLabeling().getLabelsOfChoice(model->getTransitionMatrix().getRowGroupIndices()[state] + choiceProbPair.first);
stateString << " {" << boost::join(choiceLabels, ", ") << "}";
}
stateString << ")";
}
}
} else {
if(!printUndefinedChoices) continue;
stateString << "undefined.";
}
// Print memory updates
if(!isMemorylessScheduler()) {
stateString << std::setw(widthOfStates) << "";
// The memory updates do not depend on the actual choice, they only depend on the current model- and memory state as well as the successor model state.
for (auto const& choiceProbPair : choice.getChoiceAsDistribution()) {
uint64_t row = model->getTransitionMatrix().getRowGroupIndices()[state] + choiceProbPair.first;
bool firstUpdate = true;
for (auto entryIt = model->getTransitionMatrix().getRow(row).begin(); entryIt < model->getTransitionMatrix().getRow(row).end(); ++entryIt) {
if (firstUpdate) {
firstUpdate = false;
} else {
stateString << ", ";
}
stateString << "model state' = " << entryIt->getColumn() << ": -> " << "(m' = "<<this->memoryStructure->getSuccessorMemoryState(memoryState, entryIt - model->getTransitionMatrix().begin()) <<")";
}
}
}
out << stateString.str();
out << std::endl;
}
}
if (numOfSkippedStatesWithUniqueChoice > 0) {
out << "Skipped " << numOfSkippedStatesWithUniqueChoice << " deterministic states with unique choice." << std::endl;
}
out << "___________________________________________________________________" << std::endl;
}
template <>
void Scheduler<float>::printJsonToStream(std::ostream& out, std::shared_ptr<storm::models::sparse::Model<float>> model, bool skipUniqueChoices, bool skipDontCareStates) const {
STORM_LOG_THROW(isMemorylessScheduler(), storm::exceptions::NotImplementedException, "Json export of schedulers not implemented for this value type.");
}
template <typename ValueType>
void Scheduler<ValueType>::printJsonToStream(std::ostream& out, std::shared_ptr<storm::models::sparse::Model<ValueType>> model, bool skipUniqueChoices, bool skipDontCareStates) const {
STORM_LOG_THROW(model == nullptr || model->getNumberOfStates() == schedulerChoices.front().size(), storm::exceptions::InvalidOperationException, "The given model is not compatible with this scheduler.");
STORM_LOG_WARN_COND(!(skipUniqueChoices && model == nullptr), "Can not skip unique choices if the model is not given.");
storm::json<storm::RationalNumber> output;
for (uint64_t state = 0; state < schedulerChoices.front().size(); ++state) {
// Check whether the state is skipped
if (skipUniqueChoices && model != nullptr && model->getTransitionMatrix().getRowGroupSize(state) == 1) {
continue;
}
for (uint_fast64_t memoryState = 0; memoryState < getNumberOfMemoryStates(); ++memoryState) {
// Ignore dontCare states
if (skipDontCareStates && isDontCare(state, memoryState)) {
continue;
}
storm::json<storm::RationalNumber> stateChoicesJson;
if (model && model->hasStateValuations()) {
stateChoicesJson["s"] = model->getStateValuations().template toJson<storm::RationalNumber>(state);
} else {
stateChoicesJson["s"] = state;
}
if (!isMemorylessScheduler()) {
stateChoicesJson["m"] = memoryState;
}
auto const &choice = schedulerChoices[memoryState][state];
storm::json<storm::RationalNumber> choicesJson;
if (choice.isDefined()) {
for (auto const &choiceProbPair : choice.getChoiceAsDistribution()) {
uint64_t globalChoiceIndex = model->getTransitionMatrix().getRowGroupIndices()[state] + choiceProbPair.first;
storm::json<storm::RationalNumber> choiceJson;
if (model && model->hasChoiceOrigins() &&
model->getChoiceOrigins()->getIdentifier(globalChoiceIndex) !=
model->getChoiceOrigins()->getIdentifierForChoicesWithNoOrigin()) {
choiceJson["origin"] = model->getChoiceOrigins()->getChoiceAsJson(globalChoiceIndex);
}
if (model && model->hasChoiceLabeling()) {
auto choiceLabels = model->getChoiceLabeling().getLabelsOfChoice(globalChoiceIndex);
choiceJson["labels"] = std::vector<std::string>(choiceLabels.begin(),
choiceLabels.end());
}
choiceJson["index"] = globalChoiceIndex;
choiceJson["prob"] = storm::utility::convertNumber<storm::RationalNumber>(
choiceProbPair.second);
// Memory updates
if(!isMemorylessScheduler()) {
choiceJson["memory-updates"] = std::vector<storm::json<storm::RationalNumber>>();
uint64_t row = model->getTransitionMatrix().getRowGroupIndices()[state] + choiceProbPair.first;
for (auto entryIt = model->getTransitionMatrix().getRow(row).begin(); entryIt < model->getTransitionMatrix().getRow(row).end(); ++entryIt) {
storm::json<storm::RationalNumber> updateJson;
// next model state
if (model && model->hasStateValuations()) {
updateJson["s'"] = model->getStateValuations().template toJson<storm::RationalNumber>(entryIt->getColumn());
} else {
updateJson["s'"] = entryIt->getColumn();
}
// next memory state
updateJson["m'"] = this->memoryStructure->getSuccessorMemoryState(memoryState, entryIt - model->getTransitionMatrix().begin());
choiceJson["memory-updates"].push_back(std::move(updateJson));
}
}
choicesJson.push_back(std::move(choiceJson));
}
} else {
choicesJson = "undefined";
}
stateChoicesJson["c"] = std::move(choicesJson);
output.push_back(std::move(stateChoicesJson));
}
}
out << output.dump(4);
}
template <typename ValueType>
void Scheduler<ValueType>::setPrintUndefinedChoices(bool value) {
printUndefinedChoices = value;
}
template class Scheduler<double>;
template class Scheduler<float>;
template class Scheduler<storm::RationalNumber>;
template class Scheduler<storm::RationalFunction>;
}
}