You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
430 lines
32 KiB
430 lines
32 KiB
#include "storm/modelchecker/multiobjective/pcaa/SparsePcaaWeightVectorChecker.h"
|
|
|
|
#include <map>
|
|
#include <set>
|
|
|
|
#include "storm/adapters/RationalFunctionAdapter.h"
|
|
#include "storm/models/sparse/Mdp.h"
|
|
#include "storm/models/sparse/MarkovAutomaton.h"
|
|
#include "storm/models/sparse/StandardRewardModel.h"
|
|
#include "storm/modelchecker/prctl/helper/SparseDtmcPrctlHelper.h"
|
|
#include "storm/solver/MinMaxLinearEquationSolver.h"
|
|
#include "storm/utility/graph.h"
|
|
#include "storm/utility/macros.h"
|
|
#include "storm/utility/vector.h"
|
|
#include "storm/logic/Formulas.h"
|
|
#include "storm/transformer/GoalStateMerger.h"
|
|
|
|
#include "storm/exceptions/IllegalFunctionCallException.h"
|
|
#include "storm/exceptions/UnexpectedException.h"
|
|
#include "storm/exceptions/NotImplementedException.h"
|
|
#include "storm/exceptions/NotSupportedException.h"
|
|
#include "storm/exceptions/UncheckedRequirementException.h"
|
|
|
|
namespace storm {
|
|
namespace modelchecker {
|
|
namespace multiobjective {
|
|
|
|
|
|
template <class SparseModelType>
|
|
SparsePcaaWeightVectorChecker<SparseModelType>::SparsePcaaWeightVectorChecker(SparseMultiObjectivePreprocessorResult<SparseModelType> const& preprocessorResult) :
|
|
PcaaWeightVectorChecker<SparseModelType>(preprocessorResult.objectives) {
|
|
|
|
STORM_LOG_THROW(preprocessorResult.rewardFinitenessType != SparseMultiObjectivePreprocessorResult<SparseModelType>::RewardFinitenessType::Infinite, storm::exceptions::NotSupportedException, "There is no Pareto optimal scheduler that yields finite reward for all objectives. This is not supported.");
|
|
STORM_LOG_THROW(preprocessorResult.rewardLessInfinityEStates, storm::exceptions::UnexpectedException, "The set of states with reward < infinity for some scheduler has not been computed during preprocessing.");
|
|
STORM_LOG_THROW(preprocessorResult.containsOnlyRewardObjectives(), storm::exceptions::NotSupportedException, "At least one objective was not reduced to an expected (total or cumulative) reward objective during preprocessing. This is not supported by the considered weight vector checker.");
|
|
STORM_LOG_THROW(preprocessorResult.preprocessedModel->getInitialStates().getNumberOfSetBits() == 1, storm::exceptions::NotSupportedException, "The model has multiple initial states.");
|
|
|
|
}
|
|
|
|
template <class SparseModelType>
|
|
void SparsePcaaWeightVectorChecker<SparseModelType>::initialize(SparseMultiObjectivePreprocessorResult<SparseModelType> const& preprocessorResult) {
|
|
// Build a subsystem of the preprocessor result model that discards states that yield infinite reward for all schedulers.
|
|
// We can also merge the states that will have reward zero anyway.
|
|
storm::storage::BitVector maybeStates = preprocessorResult.rewardLessInfinityEStates.get() & ~preprocessorResult.reward0AStates;
|
|
std::set<std::string> relevantRewardModels;
|
|
for (auto const& obj : this->objectives) {
|
|
obj.formula->gatherReferencedRewardModels(relevantRewardModels);
|
|
}
|
|
storm::transformer::GoalStateMerger<SparseModelType> merger(*preprocessorResult.preprocessedModel);
|
|
auto mergerResult = merger.mergeTargetAndSinkStates(maybeStates, preprocessorResult.reward0AStates, storm::storage::BitVector(maybeStates.size(), false), std::vector<std::string>(relevantRewardModels.begin(), relevantRewardModels.end()));
|
|
|
|
// Initialize data specific for the considered model type
|
|
initializeModelTypeSpecificData(*mergerResult.model);
|
|
|
|
// Initilize general data of the model
|
|
transitionMatrix = std::move(mergerResult.model->getTransitionMatrix());
|
|
initialState = *mergerResult.model->getInitialStates().begin();
|
|
reward0EStates = preprocessorResult.reward0EStates % maybeStates;
|
|
if (mergerResult.targetState) {
|
|
// There is an additional state in the result
|
|
reward0EStates.resize(reward0EStates.size() + 1, true);
|
|
|
|
// The overapproximation for the possible ec choices consists of the states that can reach the target states with prob. 0 and the target state itself.
|
|
storm::storage::BitVector targetStateAsVector(transitionMatrix.getRowGroupCount(), false);
|
|
targetStateAsVector.set(*mergerResult.targetState, true);
|
|
ecChoicesHint = transitionMatrix.getRowFilter(storm::utility::graph::performProb0E(transitionMatrix, transitionMatrix.getRowGroupIndices(), transitionMatrix.transpose(true), storm::storage::BitVector(targetStateAsVector.size(), true), targetStateAsVector));
|
|
ecChoicesHint.set(transitionMatrix.getRowGroupIndices()[*mergerResult.targetState], true);
|
|
} else {
|
|
ecChoicesHint = storm::storage::BitVector(transitionMatrix.getRowCount(), true);
|
|
}
|
|
|
|
// set data for unbounded objectives
|
|
objectivesWithNoUpperTimeBound = storm::storage::BitVector(this->objectives.size(), false);
|
|
actionsWithoutRewardInUnboundedPhase = storm::storage::BitVector(transitionMatrix.getRowCount(), true);
|
|
for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
|
|
auto const& formula = *this->objectives[objIndex].formula;
|
|
if (formula.getSubformula().isTotalRewardFormula()) {
|
|
objectivesWithNoUpperTimeBound.set(objIndex, true);
|
|
actionsWithoutRewardInUnboundedPhase &= storm::utility::vector::filterZero(actionRewards[objIndex]);
|
|
}
|
|
}
|
|
|
|
// initialize data for the results
|
|
checkHasBeenCalled = false;
|
|
objectiveResults.resize(this->objectives.size());
|
|
offsetsToUnderApproximation.resize(this->objectives.size(), storm::utility::zero<ValueType>());
|
|
offsetsToOverApproximation.resize(this->objectives.size(), storm::utility::zero<ValueType>());
|
|
optimalChoices.resize(transitionMatrix.getRowGroupCount(), 0);
|
|
}
|
|
|
|
|
|
template <class SparseModelType>
|
|
void SparsePcaaWeightVectorChecker<SparseModelType>::check(std::vector<ValueType> const& weightVector) {
|
|
checkHasBeenCalled = true;
|
|
STORM_LOG_INFO("Invoked WeightVectorChecker with weights " << std::endl << "\t" << storm::utility::vector::toString(storm::utility::vector::convertNumericVector<double>(weightVector)));
|
|
|
|
std::vector<ValueType> weightedRewardVector(transitionMatrix.getRowCount(), storm::utility::zero<ValueType>());
|
|
for (auto objIndex : objectivesWithNoUpperTimeBound) {
|
|
if (storm::solver::minimize(this->objectives[objIndex].formula->getOptimalityType())) {
|
|
storm::utility::vector::addScaledVector(weightedRewardVector, actionRewards[objIndex], -weightVector[objIndex]);
|
|
} else {
|
|
storm::utility::vector::addScaledVector(weightedRewardVector, actionRewards[objIndex], weightVector[objIndex]);
|
|
}
|
|
}
|
|
|
|
unboundedWeightedPhase(weightedRewardVector, weightVector);
|
|
|
|
unboundedIndividualPhase(weightVector);
|
|
// Only invoke boundedPhase if necessarry, i.e., if there is at least one objective with a time bound
|
|
for (auto const& obj : this->objectives) {
|
|
if (!obj.formula->getSubformula().isTotalRewardFormula()) {
|
|
boundedPhase(weightVector, weightedRewardVector);
|
|
break;
|
|
}
|
|
}
|
|
STORM_LOG_INFO("Weight vector check done. Lower bounds for results in initial state: " << storm::utility::vector::toString(storm::utility::vector::convertNumericVector<double>(getUnderApproximationOfInitialStateResults())));
|
|
// Validate that the results are sufficiently precise
|
|
ValueType resultingWeightedPrecision = storm::utility::abs<ValueType>(storm::utility::vector::dotProduct(getOverApproximationOfInitialStateResults(), weightVector) - storm::utility::vector::dotProduct(getUnderApproximationOfInitialStateResults(), weightVector));
|
|
resultingWeightedPrecision /= storm::utility::sqrt(storm::utility::vector::dotProduct(weightVector, weightVector));
|
|
STORM_LOG_THROW(resultingWeightedPrecision <= this->getWeightedPrecision(), storm::exceptions::UnexpectedException, "The desired precision was not reached");
|
|
}
|
|
|
|
template <class SparseModelType>
|
|
std::vector<typename SparsePcaaWeightVectorChecker<SparseModelType>::ValueType> SparsePcaaWeightVectorChecker<SparseModelType>::getUnderApproximationOfInitialStateResults() const {
|
|
STORM_LOG_THROW(checkHasBeenCalled, storm::exceptions::IllegalFunctionCallException, "Tried to retrieve results but check(..) has not been called before.");
|
|
std::vector<ValueType> res;
|
|
res.reserve(this->objectives.size());
|
|
for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
|
|
res.push_back(this->objectiveResults[objIndex][initialState] + this->offsetsToUnderApproximation[objIndex]);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
template <class SparseModelType>
|
|
std::vector<typename SparsePcaaWeightVectorChecker<SparseModelType>::ValueType> SparsePcaaWeightVectorChecker<SparseModelType>::getOverApproximationOfInitialStateResults() const {
|
|
STORM_LOG_THROW(checkHasBeenCalled, storm::exceptions::IllegalFunctionCallException, "Tried to retrieve results but check(..) has not been called before.");
|
|
std::vector<ValueType> res;
|
|
res.reserve(this->objectives.size());
|
|
for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
|
|
res.push_back(this->objectiveResults[objIndex][initialState] + this->offsetsToOverApproximation[objIndex]);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
template <class SparseModelType>
|
|
storm::storage::Scheduler<typename SparsePcaaWeightVectorChecker<SparseModelType>::ValueType> SparsePcaaWeightVectorChecker<SparseModelType>::computeScheduler() const {
|
|
STORM_LOG_THROW(this->checkHasBeenCalled, storm::exceptions::IllegalFunctionCallException, "Tried to retrieve results but check(..) has not been called before.");
|
|
for (auto const& obj : this->objectives) {
|
|
STORM_LOG_THROW(obj.formula->getSubformula().isTotalRewardFormula(), storm::exceptions::NotImplementedException, "Scheduler retrival is only implemented for objectives without time-bound.");
|
|
}
|
|
|
|
storm::storage::Scheduler<ValueType> result(this->optimalChoices.size());
|
|
uint_fast64_t state = 0;
|
|
for (auto const& choice : optimalChoices) {
|
|
result.setChoice(choice, state);
|
|
++state;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template <class SparseModelType>
|
|
void SparsePcaaWeightVectorChecker<SparseModelType>::unboundedWeightedPhase(std::vector<ValueType> const& weightedRewardVector, std::vector<ValueType> const& weightVector) {
|
|
|
|
if (this->objectivesWithNoUpperTimeBound.empty() || !storm::utility::vector::hasNonZeroEntry(weightedRewardVector)) {
|
|
this->weightedResult = std::vector<ValueType>(transitionMatrix.getRowGroupCount(), storm::utility::zero<ValueType>());
|
|
this->optimalChoices = std::vector<uint_fast64_t>(transitionMatrix.getRowGroupCount(), 0);
|
|
return;
|
|
}
|
|
|
|
updateEcQuotient(weightedRewardVector);
|
|
|
|
storm::utility::vector::selectVectorValues(ecQuotient->auxChoiceValues, ecQuotient->ecqToOriginalChoiceMapping, weightedRewardVector);
|
|
|
|
storm::solver::GeneralMinMaxLinearEquationSolverFactory<ValueType> solverFactory;
|
|
std::unique_ptr<storm::solver::MinMaxLinearEquationSolver<ValueType>> solver = solverFactory.create(ecQuotient->matrix);
|
|
solver->setTrackScheduler(true);
|
|
auto req = solver->getRequirements(storm::solver::EquationSystemType::StochasticShortestPath);
|
|
req.clearNoEndComponents();
|
|
boost::optional<ValueType> lowerBound = this->computeWeightedResultBound(true, weightVector, objectivesWithNoUpperTimeBound);
|
|
if (lowerBound) {
|
|
solver->setLowerBound(lowerBound.get());
|
|
req.clearLowerBounds();
|
|
}
|
|
boost::optional<ValueType> upperBound = this->computeWeightedResultBound(false, weightVector, objectivesWithNoUpperTimeBound);
|
|
if (upperBound) {
|
|
solver->setUpperBound(upperBound.get());
|
|
req.clearUpperBounds();
|
|
}
|
|
STORM_LOG_THROW(req.empty(), storm::exceptions::UncheckedRequirementException, "At least one requirement of the MinMaxSolver was not met.");
|
|
solver->setRequirementsChecked(true);
|
|
solver->setOptimizationDirection(storm::solver::OptimizationDirection::Maximize);
|
|
|
|
// Use the (0...0) vector as initial guess for the solution.
|
|
std::fill(ecQuotient->auxStateValues.begin(), ecQuotient->auxStateValues.end(), storm::utility::zero<ValueType>());
|
|
|
|
solver->solveEquations(ecQuotient->auxStateValues, ecQuotient->auxChoiceValues);
|
|
|
|
this->weightedResult = std::vector<ValueType>(transitionMatrix.getRowGroupCount());
|
|
|
|
transformReducedSolutionToOriginalModel(ecQuotient->matrix, ecQuotient->auxStateValues, solver->getSchedulerChoices(), ecQuotient->ecqToOriginalChoiceMapping, ecQuotient->originalToEcqStateMapping, this->weightedResult, this->optimalChoices);
|
|
}
|
|
|
|
template <class SparseModelType>
|
|
void SparsePcaaWeightVectorChecker<SparseModelType>::unboundedIndividualPhase(std::vector<ValueType> const& weightVector) {
|
|
if (objectivesWithNoUpperTimeBound.getNumberOfSetBits() == 1 && storm::utility::isOne(weightVector[*objectivesWithNoUpperTimeBound.begin()])) {
|
|
uint_fast64_t objIndex = *objectivesWithNoUpperTimeBound.begin();
|
|
objectiveResults[objIndex] = weightedResult;
|
|
if (storm::solver::minimize(this->objectives[objIndex].formula->getOptimalityType())) {
|
|
storm::utility::vector::scaleVectorInPlace(objectiveResults[objIndex], -storm::utility::one<ValueType>());
|
|
}
|
|
for (uint_fast64_t objIndex2 = 0; objIndex2 < this->objectives.size(); ++objIndex2) {
|
|
if (objIndex != objIndex2) {
|
|
objectiveResults[objIndex2] = std::vector<ValueType>(transitionMatrix.getRowGroupCount(), storm::utility::zero<ValueType>());
|
|
}
|
|
}
|
|
} else {
|
|
storm::storage::SparseMatrix<ValueType> deterministicMatrix = transitionMatrix.selectRowsFromRowGroups(this->optimalChoices, true);
|
|
storm::storage::SparseMatrix<ValueType> deterministicBackwardTransitions = deterministicMatrix.transpose();
|
|
std::vector<ValueType> deterministicStateRewards(deterministicMatrix.getRowCount());
|
|
storm::solver::GeneralLinearEquationSolverFactory<ValueType> linearEquationSolverFactory;
|
|
|
|
// We compute an estimate for the results of the individual objectives which is obtained from the weighted result and the result of the objectives computed so far.
|
|
// Note that weightedResult = Sum_{i=1}^{n} w_i * objectiveResult_i.
|
|
std::vector<ValueType> weightedSumOfUncheckedObjectives = weightedResult;
|
|
ValueType sumOfWeightsOfUncheckedObjectives = storm::utility::vector::sum_if(weightVector, objectivesWithNoUpperTimeBound);
|
|
|
|
for (uint_fast64_t const &objIndex : storm::utility::vector::getSortedIndices(weightVector)) {
|
|
auto const& obj = this->objectives[objIndex];
|
|
if (objectivesWithNoUpperTimeBound.get(objIndex)) {
|
|
offsetsToUnderApproximation[objIndex] = storm::utility::zero<ValueType>();
|
|
offsetsToOverApproximation[objIndex] = storm::utility::zero<ValueType>();
|
|
storm::utility::vector::selectVectorValues(deterministicStateRewards, this->optimalChoices, transitionMatrix.getRowGroupIndices(), actionRewards[objIndex]);
|
|
storm::storage::BitVector statesWithRewards = ~storm::utility::vector::filterZero(deterministicStateRewards);
|
|
// As maybestates we pick the states from which a state with reward is reachable
|
|
storm::storage::BitVector maybeStates = storm::utility::graph::performProbGreater0(deterministicBackwardTransitions, storm::storage::BitVector(deterministicMatrix.getRowCount(), true), statesWithRewards);
|
|
|
|
// Compute the estimate for this objective
|
|
if (!storm::utility::isZero(weightVector[objIndex])) {
|
|
objectiveResults[objIndex] = weightedSumOfUncheckedObjectives;
|
|
ValueType scalingFactor = storm::utility::one<ValueType>() / sumOfWeightsOfUncheckedObjectives;
|
|
if (storm::solver::minimize(obj.formula->getOptimalityType())) {
|
|
scalingFactor *= -storm::utility::one<ValueType>();
|
|
}
|
|
storm::utility::vector::scaleVectorInPlace(objectiveResults[objIndex], scalingFactor);
|
|
storm::utility::vector::clip(objectiveResults[objIndex], obj.lowerResultBound, obj.upperResultBound);
|
|
}
|
|
// Make sure that the objectiveResult is initialized correctly
|
|
objectiveResults[objIndex].resize(transitionMatrix.getRowGroupCount(), storm::utility::zero<ValueType>());
|
|
|
|
if (!maybeStates.empty()) {
|
|
storm::storage::SparseMatrix<ValueType> submatrix = deterministicMatrix.getSubmatrix(
|
|
true, maybeStates, maybeStates, true);
|
|
// Converting the matrix from the fixpoint notation to the form needed for the equation
|
|
// system. That is, we go from x = A*x + b to (I-A)x = b.
|
|
submatrix.convertToEquationSystem();
|
|
|
|
// Prepare solution vector and rhs of the equation system.
|
|
std::vector<ValueType> x = storm::utility::vector::filterVector(objectiveResults[objIndex], maybeStates);
|
|
std::vector<ValueType> b = storm::utility::vector::filterVector(deterministicStateRewards, maybeStates);
|
|
|
|
// Now solve the resulting equation system.
|
|
std::unique_ptr<storm::solver::LinearEquationSolver<ValueType>> solver = linearEquationSolverFactory.create(std::move(submatrix));
|
|
auto req = solver->getRequirements();
|
|
if (obj.lowerResultBound) {
|
|
req.clearLowerBounds();
|
|
solver->setLowerBound(*obj.lowerResultBound);
|
|
}
|
|
if (obj.upperResultBound) {
|
|
solver->setUpperBound(*obj.upperResultBound);
|
|
req.clearUpperBounds();
|
|
}
|
|
STORM_LOG_THROW(req.empty(), storm::exceptions::UncheckedRequirementException, "At least one requirement of the LinearEquationSolver was not met.");
|
|
solver->solveEquations(x, b);
|
|
|
|
// Set the result for this objective accordingly
|
|
storm::utility::vector::setVectorValues<ValueType>(objectiveResults[objIndex], maybeStates, x);
|
|
}
|
|
storm::utility::vector::setVectorValues<ValueType>(objectiveResults[objIndex], ~maybeStates, storm::utility::zero<ValueType>());
|
|
|
|
// Update the estimate for the next objectives.
|
|
if (!storm::utility::isZero(weightVector[objIndex])) {
|
|
storm::utility::vector::addScaledVector(weightedSumOfUncheckedObjectives, objectiveResults[objIndex], -weightVector[objIndex]);
|
|
sumOfWeightsOfUncheckedObjectives -= weightVector[objIndex];
|
|
}
|
|
} else {
|
|
objectiveResults[objIndex] = std::vector<ValueType>(transitionMatrix.getRowGroupCount(), storm::utility::zero<ValueType>());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class SparseModelType>
|
|
void SparsePcaaWeightVectorChecker<SparseModelType>::updateEcQuotient(std::vector<ValueType> const& weightedRewardVector) {
|
|
// Check whether we need to update the currently cached ecElimResult
|
|
storm::storage::BitVector newReward0Choices = storm::utility::vector::filterZero(weightedRewardVector);
|
|
if (!ecQuotient || ecQuotient->origReward0Choices != newReward0Choices) {
|
|
|
|
// It is sufficient to consider the states from which a transition with non-zero reward is reachable. (The remaining states always have reward zero).
|
|
storm::storage::BitVector nonZeroRewardStates(transitionMatrix.getRowGroupCount(), false);
|
|
for (uint_fast64_t state = 0; state < transitionMatrix.getRowGroupCount(); ++state){
|
|
if (newReward0Choices.getNextUnsetIndex(transitionMatrix.getRowGroupIndices()[state]) < transitionMatrix.getRowGroupIndices()[state+1]) {
|
|
nonZeroRewardStates.set(state);
|
|
}
|
|
}
|
|
storm::storage::BitVector subsystemStates = storm::utility::graph::performProbGreater0E(transitionMatrix.transpose(true), storm::storage::BitVector(transitionMatrix.getRowGroupCount(), true), nonZeroRewardStates);
|
|
|
|
// Remove neutral end components, i.e., ECs in which no reward is earned.
|
|
auto ecElimResult = storm::transformer::EndComponentEliminator<ValueType>::transform(transitionMatrix, subsystemStates, ecChoicesHint & newReward0Choices, reward0EStates);
|
|
|
|
ecQuotient = EcQuotient();
|
|
ecQuotient->matrix = std::move(ecElimResult.matrix);
|
|
ecQuotient->ecqToOriginalChoiceMapping = std::move(ecElimResult.newToOldRowMapping);
|
|
ecQuotient->originalToEcqStateMapping = std::move(ecElimResult.oldToNewStateMapping);
|
|
ecQuotient->origReward0Choices = std::move(newReward0Choices);
|
|
ecQuotient->auxStateValues.reserve(transitionMatrix.getRowGroupCount());
|
|
ecQuotient->auxStateValues.resize(ecQuotient->matrix.getRowGroupCount());
|
|
ecQuotient->auxChoiceValues.reserve(transitionMatrix.getRowCount());
|
|
ecQuotient->auxChoiceValues.resize(ecQuotient->matrix.getRowCount());
|
|
}
|
|
}
|
|
|
|
|
|
template <class SparseModelType>
|
|
void SparsePcaaWeightVectorChecker<SparseModelType>::transformReducedSolutionToOriginalModel(storm::storage::SparseMatrix<ValueType> const& reducedMatrix,
|
|
std::vector<ValueType> const& reducedSolution,
|
|
std::vector<uint_fast64_t> const& reducedOptimalChoices,
|
|
std::vector<uint_fast64_t> const& reducedToOriginalChoiceMapping,
|
|
std::vector<uint_fast64_t> const& originalToReducedStateMapping,
|
|
std::vector<ValueType>& originalSolution,
|
|
std::vector<uint_fast64_t>& originalOptimalChoices) const {
|
|
|
|
storm::storage::BitVector bottomStates(transitionMatrix.getRowGroupCount(), false);
|
|
storm::storage::BitVector statesThatShouldStayInTheirEC(transitionMatrix.getRowGroupCount(), false);
|
|
storm::storage::BitVector statesWithUndefSched(transitionMatrix.getRowGroupCount(), false);
|
|
|
|
// Handle all the states for which the choice in the original model is uniquely given by the choice in the reduced model
|
|
// Also store some information regarding the remaining states
|
|
for (uint_fast64_t state = 0; state < transitionMatrix.getRowGroupCount(); ++state) {
|
|
// Check if the state exists in the reduced model, i.e., the mapping retrieves a valid index
|
|
uint_fast64_t stateInReducedModel = originalToReducedStateMapping[state];
|
|
if (stateInReducedModel < reducedMatrix.getRowGroupCount()) {
|
|
originalSolution[state] = reducedSolution[stateInReducedModel];
|
|
uint_fast64_t chosenRowInReducedModel = reducedMatrix.getRowGroupIndices()[stateInReducedModel] + reducedOptimalChoices[stateInReducedModel];
|
|
uint_fast64_t chosenRowInOriginalModel = reducedToOriginalChoiceMapping[chosenRowInReducedModel];
|
|
// Check if the state is a bottom state, i.e., the chosen row stays inside its EC.
|
|
bool stateIsBottom = reward0EStates.get(state);
|
|
for (auto const& entry : transitionMatrix.getRow(chosenRowInOriginalModel)) {
|
|
stateIsBottom &= originalToReducedStateMapping[entry.getColumn()] == stateInReducedModel;
|
|
}
|
|
if (stateIsBottom) {
|
|
bottomStates.set(state);
|
|
statesThatShouldStayInTheirEC.set(state);
|
|
} else {
|
|
// Check if the chosen row originaly belonged to the current state (and not to another state of the EC)
|
|
if (chosenRowInOriginalModel >= transitionMatrix.getRowGroupIndices()[state] &&
|
|
chosenRowInOriginalModel < transitionMatrix.getRowGroupIndices()[state+1]) {
|
|
originalOptimalChoices[state] = chosenRowInOriginalModel - transitionMatrix.getRowGroupIndices()[state];
|
|
} else {
|
|
statesWithUndefSched.set(state);
|
|
statesThatShouldStayInTheirEC.set(state);
|
|
}
|
|
}
|
|
} else {
|
|
// if the state does not exist in the reduced model, it means that the (weighted) result is always zero, independent of the scheduler.
|
|
originalSolution[state] = storm::utility::zero<ValueType>();
|
|
// However, it might be the case that infinite reward is induced for an objective with weight 0.
|
|
// To avoid this, all possible bottom states are made bottom and the remaining states have to reach a bottom state with prob. one
|
|
if (reward0EStates.get(state)) {
|
|
bottomStates.set(state);
|
|
} else {
|
|
statesWithUndefSched.set(state);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle bottom states
|
|
for (auto state : bottomStates) {
|
|
bool foundRowForState = false;
|
|
// Find a row with zero rewards that only leads to bottom states.
|
|
// If the state should stay in its EC, we also need to make sure that all successors map to the same state in the reduced model
|
|
uint_fast64_t stateInReducedModel = originalToReducedStateMapping[state];
|
|
for (uint_fast64_t row = transitionMatrix.getRowGroupIndices()[state]; row < transitionMatrix.getRowGroupIndices()[state+1]; ++row) {
|
|
bool rowOnlyLeadsToBottomStates = true;
|
|
bool rowStaysInEC = true;
|
|
for ( auto const& entry : transitionMatrix.getRow(row)) {
|
|
rowOnlyLeadsToBottomStates &= bottomStates.get(entry.getColumn());
|
|
rowStaysInEC &= originalToReducedStateMapping[entry.getColumn()] == stateInReducedModel;
|
|
}
|
|
if (rowOnlyLeadsToBottomStates && (rowStaysInEC || !statesThatShouldStayInTheirEC.get(state)) && actionsWithoutRewardInUnboundedPhase.get(row)) {
|
|
foundRowForState = true;
|
|
originalOptimalChoices[state] = row - transitionMatrix.getRowGroupIndices()[state];
|
|
break;
|
|
}
|
|
}
|
|
STORM_LOG_ASSERT(foundRowForState, "Could not find a suitable choice for a bottom state.");
|
|
}
|
|
|
|
// Handle remaining states with still undef. scheduler (either EC states or non-subsystem states)
|
|
while(!statesWithUndefSched.empty()) {
|
|
for (auto state : statesWithUndefSched) {
|
|
// Iteratively Try to find a choice such that at least one successor has a defined scheduler.
|
|
uint_fast64_t stateInReducedModel = originalToReducedStateMapping[state];
|
|
for (uint_fast64_t row = transitionMatrix.getRowGroupIndices()[state]; row < transitionMatrix.getRowGroupIndices()[state+1]; ++row) {
|
|
bool rowStaysInEC = true;
|
|
bool rowLeadsToDefinedScheduler = false;
|
|
for (auto const& entry : transitionMatrix.getRow(row)) {
|
|
rowStaysInEC &= ( stateInReducedModel == originalToReducedStateMapping[entry.getColumn()]);
|
|
rowLeadsToDefinedScheduler |= !statesWithUndefSched.get(entry.getColumn());
|
|
}
|
|
if (rowLeadsToDefinedScheduler && (rowStaysInEC || !statesThatShouldStayInTheirEC.get(state))) {
|
|
originalOptimalChoices[state] = row - transitionMatrix.getRowGroupIndices()[state];
|
|
statesWithUndefSched.set(state, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
template class SparsePcaaWeightVectorChecker<storm::models::sparse::Mdp<double>>;
|
|
template class SparsePcaaWeightVectorChecker<storm::models::sparse::MarkovAutomaton<double>>;
|
|
#ifdef STORM_HAVE_CARL
|
|
template class SparsePcaaWeightVectorChecker<storm::models::sparse::Mdp<storm::RationalNumber>>;
|
|
template class SparsePcaaWeightVectorChecker<storm::models::sparse::MarkovAutomaton<storm::RationalNumber>>;
|
|
#endif
|
|
|
|
}
|
|
}
|
|
}
|