31namespace modelchecker {
32namespace multiobjective {
34template<
class SparseModelType>
41template<
class SparseModelType>
46 "There is no Pareto optimal scheduler that yields finite reward for all objectives. This is not supported.");
48 "There might be infinite reward for some scheduler. Multi-objective model checking restricts to schedulers that yield finite reward "
49 "for all objectives. Be aware that solutions yielding infinite reward are discarded.");
50 STORM_LOG_THROW(rewardAnalysis.totalRewardLessInfinityEStates, storm::exceptions::UnexpectedException,
51 "The set of states with reward < infinity for some scheduler has not been computed during preprocessing.");
53 "At least one objective was not reduced to an expected (long run, total or cumulative) reward objective during preprocessing. This is not "
54 "supported by the considered weight vector checker.");
56 "The model has multiple initial states.");
62 rewardAnalysis.totalRewardLessInfinityEStates.get(), rewardAnalysis.totalRewardLessInfinityEStates.get());
63 std::set<std::string> relevantRewardModels;
64 for (
auto const& obj : this->objectives) {
65 obj.formula->gatherReferencedRewardModels(relevantRewardModels);
70 std::vector<std::string>(relevantRewardModels.begin(), relevantRewardModels.end()), finiteTotalRewardChoices);
71 goalStateMergerInputToReducedStateIndexMapping = std::move(mergerResult.oldToNewStateIndexMapping);
72 goalStateMergerReducedToInputChoiceMapping = mergerResult.keptChoices.getNumberOfSetBitsBeforeIndices();
74 initializeModelTypeSpecificData(*mergerResult.model);
77 transitionMatrix = std::move(mergerResult.model->getTransitionMatrix());
78 initialState = *mergerResult.model->getInitialStates().begin();
79 totalReward0EStates = rewardAnalysis.totalReward0EStates % maybeStates;
80 if (mergerResult.targetState) {
82 totalReward0EStates.
resize(totalReward0EStates.size() + 1,
true);
86 targetStateAsVector.set(*mergerResult.targetState,
true);
87 ecChoicesHint = transitionMatrix.getRowFilter(
90 ecChoicesHint.set(transitionMatrix.getRowGroupIndices()[*mergerResult.targetState],
true);
99 for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
100 auto const& formula = *this->objectives[objIndex].formula;
101 if (formula.getSubformula().isTotalRewardFormula()) {
102 objectivesWithNoUpperTimeBound.set(objIndex,
true);
105 if (formula.getSubformula().isLongRunAverageRewardFormula()) {
106 lraObjectives.set(objIndex,
true);
107 objectivesWithNoUpperTimeBound.set(objIndex,
true);
112 if (!lraObjectives.empty()) {
113 lraMecDecomposition = LraMecDecomposition();
115 transitionMatrix, transitionMatrix.transpose(
true),
storm::storage::BitVector(transitionMatrix.getRowGroupCount(),
true),
116 actionsWithoutRewardInUnboundedPhase);
117 lraMecDecomposition->auxMecValues.resize(lraMecDecomposition->mecs.size());
121 checkHasBeenCalled =
false;
122 objectiveResults.resize(this->objectives.size());
123 offsetsToAchievablePoint.resize(this->objectives.size(), storm::utility::zero<ValueType>());
124 offsetToWeightedSum = storm::utility::zero<ValueType>();
125 optimalChoices.resize(transitionMatrix.getRowGroupCount(), 0);
128 if (storm::settings::getModule<storm::settings::modules::CoreSettings>().isShowStatisticsSet()) {
130 STORM_PRINT_AND_LOG(
"Final preprocessed model has " << transitionMatrix.getRowGroupCount() <<
" states.\n");
131 STORM_PRINT_AND_LOG(
"Final preprocessed model has " << transitionMatrix.getRowCount() <<
" actions.\n");
132 if (lraMecDecomposition) {
133 STORM_PRINT_AND_LOG(
"Found " << lraMecDecomposition->mecs.size() <<
" end components that are relevant for LRA-analysis.\n");
134 uint64_t numLraMecStates = 0;
135 for (
auto const& mec : this->lraMecDecomposition->mecs) {
136 numLraMecStates += mec.size();
144template<
class SparseModelType>
149 STORM_LOG_THROW(std::any_of(weightVector.begin(), weightVector.end(), [](
auto const& w_i) { return !storm::utility::isZero(w_i); }),
150 storm::exceptions::InvalidOperationException,
"Weight vector must not be the zero vector.");
151 checkHasBeenCalled =
true;
155 storm::utility::vector::scaleVectorInPlace<ValueType, ValueType>(weightVector, storm::utility::one<ValueType>() / (inputWeightVectorLength));
158 std::vector<ValueType> weightedRewardVector(transitionMatrix.getRowCount(), storm::utility::zero<ValueType>());
159 if (!lraObjectives.empty()) {
160 boost::optional<std::vector<ValueType>> weightedStateRewardVector;
161 for (
auto objIndex : lraObjectives) {
163 storm::solver::minimize(this->objectives[objIndex].formula->getOptimalityType()) ? -weightVector[objIndex] : weightVector[objIndex];
165 if (!stateRewards.empty() && !stateRewards[objIndex].empty()) {
166 if (!weightedStateRewardVector) {
167 weightedStateRewardVector = std::vector<ValueType>(transitionMatrix.getRowGroupCount(), storm::utility::zero<ValueType>());
172 infiniteHorizonWeightedPhase(env, weightedRewardVector, weightedStateRewardVector, weightVector);
174 weightedRewardVector.assign(weightedRewardVector.size(), storm::utility::zero<ValueType>());
178 auto totalRewardObjectives = objectivesWithNoUpperTimeBound & ~lraObjectives;
179 for (
auto objIndex : totalRewardObjectives) {
186 unboundedWeightedPhase(env, weightedRewardVector, weightVector);
188 unboundedIndividualPhase(env, weightVector);
190 for (
auto const& obj : this->objectives) {
191 if (!obj.formula->getSubformula().isTotalRewardFormula() && !obj.formula->getSubformula().isLongRunAverageRewardFormula()) {
192 boundedPhase(env, weightVector, weightedRewardVector);
196 STORM_LOG_INFO(
"Weight vector check done. Lower bounds for results in initial state: "
199 ValueType weightedSum = storm::utility::zero<ValueType>();
200 for (uint64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
201 weightedSum += (
storm::solver::minimize(this->objectives[objIndex].formula->getOptimalityType()) ? -weightVector[objIndex] : weightVector[objIndex]) *
202 getAchievablePoint()[objIndex];
204 ValueType resultingWeightedPrecision = storm::utility::abs<ValueType>(getOptimalWeightedSum() - weightedSum);
206 STORM_LOG_WARN_COND(resultingWeightedPrecision <= this->getWeightedPrecision() + storm::utility::convertNumber<ValueType>(1e-10),
207 "The desired precision was not reached: resulting precision "
208 << resultingWeightedPrecision <<
" exceeds specified value " << this->getWeightedPrecision() <<
" by approx. "
209 << (storm::utility::convertNumber<double, ValueType>(resultingWeightedPrecision - this->getWeightedPrecision()))
214 storm::utility::vector::scaleVectorInPlace<ValueType, ValueType>(weightedResult, inputWeightVectorLength);
215 offsetToWeightedSum *= inputWeightVectorLength;
219template<
class SparseModelType>
221 STORM_LOG_THROW(checkHasBeenCalled, storm::exceptions::InvalidOperationException,
"Tried to retrieve results but check(..) has not been called before.");
222 std::vector<ValueType> res;
223 res.reserve(this->objectives.size());
224 for (uint64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
225 res.push_back(this->objectives[objIndex].clipResult(this->objectiveResults[objIndex][initialState] + this->offsetsToAchievablePoint[objIndex]));
230template<
class SparseModelType>
232 STORM_LOG_THROW(checkHasBeenCalled, storm::exceptions::InvalidOperationException,
"Tried to retrieve results but check(..) has not been called before.");
233 return this->weightedResult[initialState] + this->offsetToWeightedSum;
236template<
class SparseModelType>
239 STORM_LOG_THROW(this->checkHasBeenCalled, storm::exceptions::InvalidOperationException,
240 "Tried to retrieve results but check(..) has not been called before.");
241 for (
auto const& obj : this->objectives) {
242 STORM_LOG_THROW(obj.formula->getSubformula().isTotalRewardFormula() || obj.formula->getSubformula().isLongRunAverageRewardFormula(),
243 storm::exceptions::NotImplementedException,
"Scheduler retrival is only implemented for objectives without time-bound.");
245 auto const numStatesOfInputModel = goalStateMergerInputToReducedStateIndexMapping.size();
247 for (uint64_t inputModelState = 0; inputModelState < numStatesOfInputModel; ++inputModelState) {
248 auto const reducedModelState = goalStateMergerInputToReducedStateIndexMapping[inputModelState];
249 if (reducedModelState >= optimalChoices.size()) {
253 auto const reducedModelChoice = optimalChoices[reducedModelState];
254 auto const inputModelChoice = goalStateMergerReducedToInputChoiceMapping[reducedModelChoice];
255 result.
setChoice(inputModelChoice, inputModelState);
261template<
typename ValueType>
265 std::vector<uint64_t> stack;
267 stack.insert(stack.end(), processedStates.
begin(), processedStates.
end());
268 uint64_t currentState = 0;
270 while (!stack.empty()) {
271 currentState = stack.back();
274 for (
auto const& predecessorEntry : backwardTransitions.
getRow(currentState)) {
275 auto predecessor = predecessorEntry.getColumn();
276 if (consideredStates.
get(predecessor) && !processedStates.
get(predecessor)) {
280 uint64_t row = allowedChoices ? allowedChoices->getNextSetIndex(groupStart) : groupStart;
281 for (; row < groupEnd; row = allowedChoices ? allowedChoices->getNextSetIndex(row + 1) : row + 1) {
282 bool hasSuccessorInProcessedStates =
false;
283 for (
auto const& successorOfPredecessor : transitionMatrix.
getRow(row)) {
284 if (processedStates.
get(successorOfPredecessor.getColumn())) {
285 hasSuccessorInProcessedStates =
true;
289 if (hasSuccessorInProcessedStates) {
290 choices[predecessor] = row - groupStart;
291 processedStates.
set(predecessor,
true);
292 stack.push_back(predecessor);
297 "Unable to find choice at a predecessor of a processed state that leads to a processed state.");
304template<
typename ValueType>
308 for (
auto state : consideredStates) {
311 bool choiceFound =
false;
314 for (
auto const& element : transitionMatrix.
getRow(row)) {
315 if (statesToAvoid.
get(element.getColumn())) {
321 choices[state] = row - groupStart;
329template<
typename ValueType>
333 auto backwardsTransitions = matrix.
transpose(
true);
335 for (uint64_t state = 0; state < result.size(); ++state) {
336 if (rowsWithSumLessOne.
getNextSetIndex(groups[state]) < groups[state + 1]) {
337 result[state] = rowsWithSumLessOne.
getNextSetIndex(groups[state]) - groups[state];
338 processedStates.
set(state,
true);
351template<
typename ValueType>
355 auto badStates = transitionMatrix.
getRowGroupFilter(finitelyOftenChoices,
true) & ~safeStates;
359 transitionMatrix, transitionMatrix.
getRowGroupIndices(), backwardTransitions, ~safeStates, badStates,
false, 0, ~finitelyOftenChoices);
362 auto avoidBadStates = ~reachBadWithProbGreater0AStates & ~safeStates;
363 computeSchedulerProb0(transitionMatrix, backwardTransitions, avoidBadStates, reachBadWithProbGreater0AStates, ~finitelyOftenChoices, choices);
368 computeSchedulerProb1(transitionMatrix, backwardTransitions, reachBadWithProbGreater0AStates, avoidBadStates | safeStates, choices);
371template<
class SparseModelType>
373 std::vector<ValueType>
const& weightedActionRewardVector,
374 boost::optional<std::vector<ValueType>>
const& weightedStateRewardVector,
375 std::vector<ValueType>
const& weightVector) {
376 auto solverEnv = inputEnv;
379 storm::utility::convertNumber<ValueType>(2.0);
382 ValueType const offset = epsilon / storm::utility::convertNumber<ValueType>(4.0);
383 solverEnv.solver().lra().setPrecision(storm::utility::convertNumber<storm::RationalNumber>(offset));
384 solverEnv.solver().lra().setRelativeTerminationCriterion(
false);
386 STORM_LOG_ASSERT(lraMecDecomposition,
"Mec decomposition for lra computations not initialized.");
391 for (uint64_t mecIndex = 0; mecIndex < lraMecDecomposition->mecs.size(); ++mecIndex) {
392 auto const& mec = lraMecDecomposition->mecs[mecIndex];
393 auto actionValueGetter = [&weightedActionRewardVector](uint64_t
const& a) {
return weightedActionRewardVector[a]; };
395 if (weightedStateRewardVector) {
396 stateValueGetter = [&weightedStateRewardVector](uint64_t
const& s) {
return weightedStateRewardVector.get()[s]; };
398 stateValueGetter = [](uint64_t
const&) {
return storm::utility::zero<ValueType>(); };
400 lraMecDecomposition->auxMecValues[mecIndex] = helper.
computeLraForComponent(solverEnv, stateValueGetter, actionValueGetter, mec) + offset;
406template<
class SparseModelType>
408 std::vector<ValueType>
const& weightVector) {
410 auto solverEnv = inputEnv;
412 solverEnv.solver().lra().setRelativeTerminationCriterion(
false);
413 bool const requireSoundApproximation = !solverEnv.solver().isForceExact() && solverEnv.solver().isForceSoundness();
414 ValueType const two = storm::utility::convertNumber<ValueType>(2.0);
418 if (solverEnv.solver().isForceExact()) {
420 adjustedPrecision = storm::utility::zero<ValueType>();
421 }
else if (requireSoundApproximation) {
422 adjustedPrecision /= two;
424 if (lraObjectives.empty()) {
425 solverEnv.solver().minMax().setPrecision(storm::utility::convertNumber<storm::RationalNumber>(adjustedPrecision));
428 solverEnv.solver().minMax().setPrecision(storm::utility::convertNumber<storm::RationalNumber, ValueType>(adjustedPrecision / two));
429 solverEnv.solver().lra().setPrecision(storm::utility::convertNumber<storm::RationalNumber, ValueType>(adjustedPrecision / two));
433 if (this->objectivesWithNoUpperTimeBound.empty() ||
436 this->weightedResult.assign(transitionMatrix.getRowGroupCount(), storm::utility::zero<ValueType>());
438 if (this->lraMecDecomposition) {
439 for (
auto const& mec : this->lraMecDecomposition->mecs) {
440 for (
auto const& sc : mec) {
441 statesInLraMec.
set(sc.first,
true);
447 this->optimalChoices);
451 updateEcQuotient(weightedRewardVector);
455 std::map<uint64_t, uint64_t> ecqStateToOptimalMecMap;
456 if (!lraObjectives.empty()) {
461 for (uint64_t mecIndex = 0; mecIndex < lraMecDecomposition->mecs.size(); ++mecIndex) {
462 auto const& mec = lraMecDecomposition->mecs[mecIndex];
463 auto const& mecValue = lraMecDecomposition->auxMecValues[mecIndex];
464 uint64_t ecqState = ecQuotient->originalToEcqStateMapping[mec.begin()->first];
465 if (ecqState >= ecQuotient->matrix.getRowGroupCount()) {
470 uint64_t ecqChoice = ecQuotient->ecqStayInEcChoices.getNextSetIndex(ecQuotient->matrix.getRowGroupIndices()[ecqState]);
471 STORM_LOG_ASSERT(ecqChoice < ecQuotient->matrix.getRowGroupIndices()[ecqState + 1],
472 "Unable to find choice that represents staying inside the (eliminated) ec.");
473 auto& ecqChoiceValue = ecQuotient->auxChoiceValues[ecqChoice];
474 auto insertionRes = ecqStateToOptimalMecMap.emplace(ecqState, mecIndex);
475 if (insertionRes.second) {
478 "Expected a total reward of zero for choices that represent staying in an EC for ever.");
479 ecqChoiceValue = mecValue;
481 if (mecValue > ecqChoiceValue) {
482 ecqChoiceValue = mecValue;
483 insertionRes.first->second = mecIndex;
489 std::unique_ptr<storm::solver::MinMaxLinearEquationSolver<ValueType>> solver = solverFactory.
create(solverEnv, ecQuotient->matrix);
490 solver->setTrackScheduler(
true);
491 solver->setHasUniqueSolution(
true);
492 solver->setOptimizationDirection(storm::solver::OptimizationDirection::Maximize);
493 auto req = solver->getRequirements(solverEnv, storm::solver::OptimizationDirection::Maximize);
494 setBoundsToSolver(*solver, req.lowerBounds(), req.upperBounds(), weightVector, objectivesWithNoUpperTimeBound, ecQuotient->matrix,
495 ecQuotient->rowsWithSumLessOne, ecQuotient->auxChoiceValues);
496 if (solver->hasLowerBound()) {
497 req.clearLowerBounds();
499 if (solver->hasUpperBound()) {
500 req.clearUpperBounds();
502 if (req.validInitialScheduler()) {
504 req.clearValidInitialScheduler();
506 STORM_LOG_THROW(!req.hasEnabledCriticalRequirement(), storm::exceptions::UncheckedRequirementException,
507 "Solver requirements " + req.getEnabledRequirementsAsString() +
" not checked.");
508 solver->setRequirementsChecked(
true);
511 std::fill(ecQuotient->auxStateValues.begin(), ecQuotient->auxStateValues.end(), storm::utility::zero<ValueType>());
513 solver->solveEquations(solverEnv, ecQuotient->auxStateValues, ecQuotient->auxChoiceValues);
514 this->weightedResult = std::vector<ValueType>(transitionMatrix.getRowGroupCount());
516 transformEcqSolutionToOriginalModel(ecQuotient->auxStateValues, solver->getSchedulerChoices(), ecqStateToOptimalMecMap, this->weightedResult,
517 this->optimalChoices);
519 offsetToWeightedSum = storm::utility::zero<ValueType>();
520 if (requireSoundApproximation) {
522 offsetToWeightedSum += adjustedPrecision;
526template<
class SparseModelType>
528 auto solverEnv = inputEnv;
531 std::vector<ValueType> deterministicStateRewards(deterministicMatrix.
getRowCount());
533 bool const requireSoundApproximation = !solverEnv.solver().isForceExact() && solverEnv.solver().isForceSoundness();
535 ValueType const two = storm::utility::convertNumber<ValueType>(2.0);
537 if (solverEnv.solver().isForceExact()) {
539 epsilon = storm::utility::zero<ValueType>();
540 }
else if (requireSoundApproximation) {
544 auto infiniteHorizonHelper = createDetInfiniteHorizonHelper(deterministicMatrix);
545 infiniteHorizonHelper.provideBackwardTransitions(deterministicBackwardTransitions);
549 std::vector<ValueType> weightedSumOfUncheckedObjectives = weightedResult;
553 auto const& obj = this->objectives[objIndex];
554 if (objectivesWithNoUpperTimeBound.get(objIndex)) {
555 ValueType epsilon_j = epsilon / storm::utility::convertNumber<ValueType, uint64_t>(objectivesWithNoUpperTimeBound.getNumberOfSetBits());
559 solverEnv.solver().setLinearEquationSolverPrecision(storm::utility::convertNumber<RationalNumber>(epsilon_j),
false);
560 solverEnv.solver().lra().setPrecision(storm::utility::convertNumber<RationalNumber>(epsilon_j));
561 solverEnv.solver().lra().setRelativeTerminationCriterion(
false);
563 if (lraObjectives.get(objIndex)) {
564 auto actionValueGetter = [&](uint64_t
const& a) {
565 return actionRewards[objIndex][transitionMatrix.getRowGroupIndices()[a] + this->optimalChoices[a]];
568 if (stateRewards.empty() || stateRewards[objIndex].empty()) {
569 stateValueGetter = [](uint64_t
const&) {
return storm::utility::zero<ValueType>(); };
571 stateValueGetter = [&](uint64_t
const& s) {
return stateRewards[objIndex][s]; };
576 actionRewards[objIndex]);
584 objectiveResults[objIndex] = weightedSumOfUncheckedObjectives;
585 ValueType scalingFactor = storm::utility::one<ValueType>() / sumOfWeightsOfUncheckedObjectives;
587 scalingFactor *= -storm::utility::one<ValueType>();
593 objectiveResults[objIndex].resize(transitionMatrix.getRowGroupCount(), storm::utility::zero<ValueType>());
595 if (!maybeStates.
empty()) {
596 bool needEquationSystem =
599 if (needEquationSystem) {
610 std::unique_ptr<storm::solver::LinearEquationSolver<ValueType>> solver = linearEquationSolverFactory.
create(solverEnv, submatrix);
611 auto req = solver->getRequirements(solverEnv);
612 solver->clearBounds();
615 this->setBoundsToSolver(*solver, req.lowerBounds(), req.upperBounds(), objIndex, submatrix, submatrixRowsWithSumLessOne, b);
616 if (solver->hasLowerBound()) {
617 req.clearLowerBounds();
619 if (solver->hasUpperBound()) {
620 req.clearUpperBounds();
622 STORM_LOG_THROW(!req.hasEnabledCriticalRequirement(), storm::exceptions::UncheckedRequirementException,
623 "Solver requirements " + req.getEnabledRequirementsAsString() +
" not checked.");
624 solver->solveEquations(solverEnv, x, b);
625 if (requireSoundApproximation) {
627 offsetsToAchievablePoint[objIndex] =
631 storm::utility::vector::setVectorValues<ValueType>(objectiveResults[objIndex], maybeStates, x);
633 storm::utility::vector::setVectorValues<ValueType>(objectiveResults[objIndex], ~maybeStates, storm::utility::zero<ValueType>());
638 sumOfWeightsOfUncheckedObjectives -= weightVector[objIndex];
642 objectiveResults[objIndex] = std::vector<ValueType>(transitionMatrix.getRowGroupCount(), storm::utility::zero<ValueType>());
647template<
class SparseModelType>
652 if (lraMecDecomposition) {
653 for (uint64_t mecIndex = 0; mecIndex < lraMecDecomposition->mecs.size(); ++mecIndex) {
656 auto const& mec = lraMecDecomposition->mecs[mecIndex];
657 for (
auto const& stateChoices : mec) {
658 for (
auto const& choice : stateChoices.second) {
659 zeroLraRewardChoices.
set(choice,
false);
666 if (!ecQuotient || ecQuotient->origReward0Choices != newReward0Choices) {
668 auto nonZeroRewardStates = transitionMatrix.getRowGroupFilter(newReward0Choices,
true);
671 transitionMatrix.transpose(
true),
storm::storage::BitVector(transitionMatrix.getRowGroupCount(),
true), nonZeroRewardStates);
676 ecChoicesHint & newTotalReward0Choices, totalReward0EStates);
679 for (uint64_t row = 0; row < rowsWithSumLessOne.
size(); ++row) {
680 if (ecElimResult.matrix.getRow(row).getNumberOfEntries() == 0) {
681 rowsWithSumLessOne.
set(row,
true);
683 for (
auto const& entry : transitionMatrix.getRow(ecElimResult.newToOldRowMapping[row])) {
684 if (!subsystemStates.
get(entry.getColumn())) {
685 rowsWithSumLessOne.
set(row,
true);
693 ecQuotient->matrix = std::move(ecElimResult.matrix);
694 ecQuotient->ecqToOriginalChoiceMapping = std::move(ecElimResult.newToOldRowMapping);
695 ecQuotient->originalToEcqStateMapping = std::move(ecElimResult.oldToNewStateMapping);
696 ecQuotient->ecqToOriginalStateMapping.resize(ecQuotient->matrix.getRowGroupCount());
697 for (uint64_t state = 0; state < ecQuotient->originalToEcqStateMapping.size(); ++state) {
698 uint64_t ecqState = ecQuotient->originalToEcqStateMapping[state];
699 if (ecqState < ecQuotient->matrix.getRowGroupCount()) {
700 ecQuotient->ecqToOriginalStateMapping[ecqState].insert(state);
703 ecQuotient->ecqStayInEcChoices = std::move(ecElimResult.sinkRows);
704 ecQuotient->origReward0Choices = std::move(newReward0Choices);
705 ecQuotient->origTotalReward0Choices = std::move(newTotalReward0Choices);
706 ecQuotient->rowsWithSumLessOne = std::move(rowsWithSumLessOne);
707 ecQuotient->auxStateValues.resize(ecQuotient->matrix.getRowGroupCount());
708 ecQuotient->auxChoiceValues.resize(ecQuotient->matrix.getRowCount());
712template<
class SparseModelType>
714 bool requiresUpper, uint64_t objIndex,
717 std::vector<ValueType>
const& rewards)
const {
719 if (this->objectives[objIndex].lowerResultBound) {
720 solver.
setLowerBound(this->objectives[objIndex].lowerResultBound.get());
722 if (this->objectives[objIndex].upperResultBound) {
723 solver.
setUpperBound(this->objectives[objIndex].upperResultBound.get());
727 computeAndSetBoundsToSolver(solver, requiresLower, requiresUpper, transitions, rowsWithSumLessOne, rewards);
731template<
class SparseModelType>
733 bool requiresUpper, std::vector<ValueType>
const& weightVector,
737 std::vector<ValueType>
const& rewards)
const {
739 boost::optional<ValueType> lowerBound = this->computeWeightedResultBound(
true, weightVector, objectiveFilter & ~lraObjectives);
741 if (!lraObjectives.empty()) {
742 auto min = std::min_element(lraMecDecomposition->auxMecValues.begin(), lraMecDecomposition->auxMecValues.end());
743 if (min != lraMecDecomposition->auxMecValues.end()) {
744 lowerBound.get() += *min;
749 boost::optional<ValueType> upperBound = this->computeWeightedResultBound(
false, weightVector, objectiveFilter);
751 if (!lraObjectives.empty()) {
752 auto max = std::max_element(lraMecDecomposition->auxMecValues.begin(), lraMecDecomposition->auxMecValues.end());
753 if (max != lraMecDecomposition->auxMecValues.end()) {
754 upperBound.get() += *max;
761 computeAndSetBoundsToSolver(solver, requiresLower, requiresUpper, transitions, rowsWithSumLessOne, rewards);
765template<
class SparseModelType>
770 std::vector<ValueType>
const& rewards)
const {
772 std::vector<ValueType> oneStepTargetProbs(transitions.
getRowCount(), storm::utility::zero<ValueType>());
773 for (
auto row : rowsWithSumLessOne) {
774 oneStepTargetProbs[row] = storm::utility::one<ValueType>() - transitions.
getRowSum(row);
779 std::vector<ValueType> negativeRewards;
780 negativeRewards.reserve(transitions.
getRowCount());
782 for (
auto const& rew : rewards) {
783 if (rew < storm::utility::zero<ValueType>()) {
784 negativeRewards.resize(row, storm::utility::zero<ValueType>());
785 negativeRewards.push_back(-rew);
789 if (!negativeRewards.empty()) {
790 negativeRewards.resize(row, storm::utility::zero<ValueType>());
791 std::vector<ValueType> lowerBounds =
803 std::vector<ValueType> positiveRewards;
804 positiveRewards.reserve(transitions.
getRowCount());
806 for (
auto const& rew : rewards) {
807 if (rew > storm::utility::zero<ValueType>()) {
808 positiveRewards.resize(row, storm::utility::zero<ValueType>());
809 positiveRewards.push_back(rew);
813 if (!positiveRewards.empty()) {
814 positiveRewards.resize(row, storm::utility::zero<ValueType>());
823template<
class SparseModelType>
825 std::vector<uint_fast64_t>
const& ecqOptimalChoices,
826 std::map<uint64_t, uint64_t>
const& ecqStateToOptimalMecMap,
827 std::vector<ValueType>& originalSolution,
828 std::vector<uint_fast64_t>& originalOptimalChoices)
const {
829 auto backwardsTransitions = transitionMatrix.transpose(
true);
840 for (uint64_t ecqState = 0; ecqState < ecqSolution.size(); ++ecqState) {
841 uint64_t ecqChoice = ecQuotient->matrix.getRowGroupIndices()[ecqState] + ecqOptimalChoices[ecqState];
842 uint_fast64_t origChoice = ecQuotient->ecqToOriginalChoiceMapping[ecqChoice];
843 auto const& origStates = ecQuotient->ecqToOriginalStateMapping[ecqState];
844 STORM_LOG_ASSERT(!origStates.empty(),
"Unexpected empty set of original states.");
845 if (ecQuotient->ecqStayInEcChoices.get(ecqChoice)) {
848 if (!ecqStateToOptimalMecMap.empty()) {
851 STORM_LOG_ASSERT(ecqStateToOptimalMecMap.count(ecqState) > 0,
"No Lra Mec associated to given eliminated EC");
852 auto const& lraMec = lraMecDecomposition->mecs[ecqStateToOptimalMecMap.at(ecqState)];
853 if (lraMec.size() == origStates.size()) {
855 for (
auto const& state : origStates) {
856 STORM_LOG_ASSERT(lraMec.containsState(state),
"Expected state to be contained in the lra mec.");
858 unprocessedStates.
set(state,
false);
859 originalSolution[state] = ecqSolution[ecqState];
863 STORM_LOG_ASSERT(lraMec.size() < origStates.size(),
"Lra Mec (" << lraMec.size()
864 <<
" states) should be a proper subset of the eliminated ec ("
865 << origStates.size() <<
" states).");
866 for (
auto const& state : origStates) {
867 if (lraMec.containsState(state)) {
868 ecStatesToReach.
set(state,
true);
871 ecStatesToProcess.
set(state,
true);
873 unprocessedStates.
set(state,
false);
874 originalSolution[state] = ecqSolution[ecqState];
876 computeSchedulerProb1(transitionMatrix, backwardsTransitions, ecStatesToProcess, ecStatesToReach, originalOptimalChoices,
877 &ecQuotient->origTotalReward0Choices);
879 ecStatesToProcess.
clear();
880 ecStatesToReach.
clear();
886 bool needSchedulerComputation =
false;
888 "Solution for state that stays inside EC must be zero. Got " << ecqSolution[ecqState] <<
" instead.");
889 for (
auto const& state : origStates) {
890 originalSolution[state] = storm::utility::zero<ValueType>();
891 ecStatesToProcess.
set(state,
true);
893 auto validChoices = transitionMatrix.getRowFilter(ecStatesToProcess, ecStatesToProcess);
894 auto valid0RewardChoices = validChoices & actionsWithoutRewardInUnboundedPhase;
895 for (
auto const& state : origStates) {
896 auto groupStart = transitionMatrix.getRowGroupIndices()[state];
897 auto groupEnd = transitionMatrix.getRowGroupIndices()[state + 1];
898 auto nextValidChoice = valid0RewardChoices.getNextSetIndex(groupStart);
899 if (nextValidChoice < groupEnd) {
900 originalOptimalChoices[state] = nextValidChoice - groupStart;
903 ecStatesToAvoid.
set(state,
true);
904 needSchedulerComputation =
true;
907 if (needSchedulerComputation) {
909 auto ecStatesThatCanAvoid =
911 ecStatesToProcess, ecStatesToAvoid,
false, 0, valid0RewardChoices);
912 ecStatesThatCanAvoid.complement();
914 computeSchedulerProb0(transitionMatrix, backwardsTransitions, ecStatesThatCanAvoid, ecStatesToAvoid, valid0RewardChoices,
915 originalOptimalChoices);
917 computeSchedulerProb1(transitionMatrix, backwardsTransitions, ecStatesToProcess & ~ecStatesToAvoid, ecStatesToAvoid, originalOptimalChoices,
920 ecStatesToAvoid.
clear();
921 ecStatesToProcess.
clear();
927 if (origStates.size() > 1) {
928 for (
auto const& state : origStates) {
930 auto groupStart = transitionMatrix.getRowGroupIndices()[state];
931 auto groupEnd = transitionMatrix.getRowGroupIndices()[state + 1];
932 if (origChoice >= groupStart && origChoice < groupEnd) {
933 originalOptimalChoices[state] = origChoice - groupStart;
934 ecStatesToReach.
set(state,
true);
936 STORM_LOG_ASSERT(origStates.size() > 1,
"Multiple original states expected.");
937 ecStatesToProcess.
set(state,
true);
939 unprocessedStates.
set(state,
false);
940 originalSolution[state] = ecqSolution[ecqState];
942 auto validChoices = transitionMatrix.getRowFilter(ecStatesToProcess, ecStatesToProcess | ecStatesToReach);
943 computeSchedulerProb1(transitionMatrix, backwardsTransitions, ecStatesToProcess, ecStatesToReach, originalOptimalChoices, &validChoices);
945 ecStatesToProcess.
clear();
946 ecStatesToReach.
clear();
949 auto state = *origStates.begin();
950 auto groupStart = transitionMatrix.getRowGroupIndices()[state];
952 origChoice >= groupStart && origChoice < transitionMatrix.getRowGroupIndices()[state + 1],
953 "Invalid choice: " << originalOptimalChoices[state] <<
" at a state with " << transitionMatrix.getRowGroupSize(state) <<
" choices.");
954 originalOptimalChoices[state] = origChoice - groupStart;
955 originalSolution[state] = ecqSolution[ecqState];
956 unprocessedStates.
set(state,
false);
965 if (this->lraMecDecomposition) {
967 for (
auto const& mec : this->lraMecDecomposition->mecs) {
968 for (
auto const& sc : mec) {
969 if (unprocessedStates.
get(sc.first)) {
970 ecStatesToReach.
set(sc.first,
true);
975 ecStatesToReach = unprocessedStates & totalReward0EStates;
977 computeSchedulerProb0(transitionMatrix, backwardsTransitions, ecStatesToReach, ~unprocessedStates | ~totalReward0EStates,
978 actionsWithoutRewardInUnboundedPhase, originalOptimalChoices);
980 unprocessedStates &= ~ecStatesToReach;
982 computeSchedulerProb1(transitionMatrix, backwardsTransitions, unprocessedStates, ecStatesToReach, originalOptimalChoices);
SolverEnvironment & solver()
void setRelativeTerminationCriterion(bool value)
MinMaxSolverEnvironment & minMax()
ValueType computeUpperBound()
Computes an upper bound on the expected rewards.
std::vector< ValueType > computeUpperBounds()
Computes upper bounds on the expected rewards.
void setProduceScheduler(bool value)
Sets whether an optimal scheduler shall be constructed during the computation.
void setOptimizationDirection(storm::solver::OptimizationDirection const &direction)
Sets the optimization direction, i.e., whether we want to minimize or maximize the value for each sta...
void provideLongRunComponentDecomposition(storm::storage::Decomposition< LongRunComponentType > const &decomposition)
Provides the decomposition into long run components (BSCCs/MECs) that can be used during the computat...
std::vector< ValueType > computeLongRunAverageValues(Environment const &env, std::vector< ValueType > const *stateValues=nullptr, std::vector< ValueType > const *actionValues=nullptr)
Computes the long run average value given the provided state and action-based rewards.
Helper class for model checking queries that depend on the long run behavior of the (nondeterministic...
SparseInfiniteHorizonHelper< ValueType, true >::ValueGetter ValueGetter
Function mapping from indices to values.
std::vector< uint64_t > const & getProducedOptimalChoices() const
virtual ValueType computeLraForComponent(Environment const &env, ValueGetter const &stateValuesGetter, ValueGetter const &actionValuesGetter, storm::storage::MaximalEndComponent const &component) override
Helper Class that takes a weight vector and ...
Helper Class that takes preprocessed Pcaa data and a weight vector and ...
ValueType getOptimalWeightedSum() const override
Retrieves the optimal weighted sum of the objective values (or an upper bound thereof).
void unboundedWeightedPhase(Environment const &env, std::vector< ValueType > const &weightedRewardVector, std::vector< ValueType > const &weightVector)
Determines the scheduler that optimizes the weighted reward vector of the unbounded objectives.
void computeAndSetBoundsToSolver(storm::solver::AbstractEquationSolver< ValueType > &solver, bool requiresLower, bool requiresUpper, storm::storage::SparseMatrix< ValueType > const &transitions, storm::storage::BitVector const &rowsWithSumLessOne, std::vector< ValueType > const &rewards) const
virtual std::vector< ValueType > getAchievablePoint() const override
Retrieves the result of the individual objectives at the initial state of the given model.
void infiniteHorizonWeightedPhase(Environment const &env, std::vector< ValueType > const &weightedActionRewardVector, boost::optional< std::vector< ValueType > > const &weightedStateRewardVector, std::vector< ValueType > const &weightVector)
StandardPcaaWeightVectorChecker(preprocessing::SparseMultiObjectivePreprocessorResult< SparseModelType > const &preprocessorResult)
SparseModelType::ValueType ValueType
virtual void check(Environment const &env, std::vector< ValueType > weightVector) override
void transformEcqSolutionToOriginalModel(std::vector< ValueType > const &ecqSolution, std::vector< uint_fast64_t > const &ecqOptimalChoices, std::map< uint64_t, uint64_t > const &ecqStateToOptimalMecMap, std::vector< ValueType > &originalSolution, std::vector< uint_fast64_t > &originalOptimalChoices) const
Transforms the results of a min-max-solver that considers a reduced model (without end components) to...
virtual storm::storage::Scheduler< ValueType > computeScheduler() const override
Retrieves a scheduler that induces the current values Note that check(..) has to be called before ret...
void updateEcQuotient(std::vector< ValueType > const &weightedRewardVector)
void initialize(preprocessing::SparseMultiObjectivePreprocessorResult< SparseModelType > const &preprocessorResult)
void unboundedIndividualPhase(Environment const &env, std::vector< ValueType > const &weightVector)
Computes the values of the objectives that do not have a stepBound w.r.t.
void setBoundsToSolver(storm::solver::AbstractEquationSolver< ValueType > &solver, bool requiresLower, bool requiresUpper, uint64_t objIndex, storm::storage::SparseMatrix< ValueType > const &transitions, storm::storage::BitVector const &rowsWithSumLessOne, std::vector< ValueType > const &rewards) const
static ReturnType analyze(storm::modelchecker::multiobjective::preprocessing::SparseMultiObjectivePreprocessorResult< SparseModelType > const &preprocessorResult)
Analyzes the reward objectives of the multi objective query.
void setUpperBound(ValueType const &value)
Sets an upper bound for the solution that can potentially be used by the solver.
bool hasUpperBound(BoundType const &type=BoundType::Any) const
Retrieves whether this solver has an upper bound.
bool hasLowerBound(BoundType const &type=BoundType::Any) const
Retrieves whether this solver has a lower bound.
void setLowerBound(ValueType const &value)
Sets a lower bound for the solution that can potentially be used by the solver.
void setLowerBounds(std::vector< ValueType > const &values)
Sets lower bounds for the solution that can potentially be used by the solver.
virtual std::unique_ptr< LinearEquationSolver< ValueType > > create(Environment const &env) const override
Creates an equation solver with the current settings, but without a matrix.
virtual std::unique_ptr< MinMaxLinearEquationSolver< ValueType, SolutionType > > create(Environment const &env) const override
virtual LinearEquationSolverProblemFormat getEquationProblemFormat(Environment const &env) const
Retrieves the problem format that the solver expects if it was created with the current settings.
A bit vector that is internally represented as a vector of 64-bit values.
void complement()
Negates all bits in the bit vector.
uint64_t getNextSetIndex(uint64_t startingIndex) const
Retrieves the index of the bit that is the next bit set to true in the bit vector.
const_iterator end() const
Returns an iterator pointing at the element past the back of the bit vector.
bool empty() const
Retrieves whether no bits are set to true in this bit vector.
void clear()
Removes all set bits from the bit vector.
bool isSubsetOf(BitVector const &other) const
Checks whether all bits that are set in the current bit vector are also set in the given bit vector.
void set(uint64_t index, bool value=true)
Sets the given truth value at the given index.
const_iterator begin() const
Returns an iterator to the indices of the set bits in the bit vector.
size_t size() const
Retrieves the number of bits this bit vector can store.
void resize(uint64_t newLength, bool init=false)
Resizes the bit vector to hold the given new number of bits.
bool get(uint64_t index) const
Retrieves the truth value of the bit at the given index and performs a bound check.
This class represents the decomposition of a nondeterministic model into its maximal end components.
This class defines which action is chosen in a particular state of a non-deterministic model.
void setChoice(SchedulerChoice< ValueType > const &choice, uint_fast64_t modelState, uint_fast64_t memoryState=0)
Sets the choice defined by the scheduler for the given state.
A class that holds a possibly non-square matrix in the compressed row storage format.
void convertToEquationSystem()
Transforms the matrix into an equation system.
const_rows getRow(index_type row) const
Returns an object representing the given row.
SparseMatrix selectRowsFromRowGroups(std::vector< index_type > const &rowGroupToRowIndexMapping, bool insertDiagonalEntries=true) const
Selects exactly one row from each row group of this matrix and returns the resulting matrix.
SparseMatrix getSubmatrix(bool useGroups, storm::storage::BitVector const &rowConstraint, storm::storage::BitVector const &columnConstraint, bool insertDiagonalEntries=false, storm::storage::BitVector const &makeZeroColumns=storm::storage::BitVector()) const
Creates a submatrix of the current matrix by dropping all rows and columns whose bits are not set to ...
value_type getRowSum(index_type row) const
Computes the sum of the entries in a given row.
index_type getRowGroupCount() const
Returns the number of row groups in the matrix.
storm::storage::BitVector getRowGroupFilter(storm::storage::BitVector const &rowConstraint, bool setIfForAllRowsInGroup) const
Returns the indices of all row groups selected by the row constraints.
std::vector< index_type > const & getRowGroupIndices() const
Returns the grouping of rows of this matrix.
storm::storage::SparseMatrix< value_type > transpose(bool joinGroups=false, bool keepZeros=false) const
Transposes the matrix.
index_type getRowCount() const
Returns the number of rows of the matrix.
storm::storage::BitVector getRowFilter(storm::storage::BitVector const &groupConstraint) const
Returns a bitvector representing the set of rows, with all indices set that correspond to one of the ...
#define STORM_LOG_INFO(message)
#define STORM_LOG_ASSERT(cond, message)
#define STORM_LOG_WARN_COND(cond, message)
#define STORM_LOG_THROW(cond, exception, message)
#define STORM_PRINT_AND_LOG(message)
std::vector< uint64_t > computeValidInitialScheduler(storm::storage::SparseMatrix< ValueType > const &matrix, storm::storage::BitVector const &rowsWithSumLessOne)
void computeSchedulerFinitelyOften(storm::storage::SparseMatrix< ValueType > const &transitionMatrix, storm::storage::SparseMatrix< ValueType > const &backwardTransitions, storm::storage::BitVector const &finitelyOftenChoices, storm::storage::BitVector safeStates, std::vector< uint64_t > &choices)
Computes a scheduler taking the choices from the given set only finitely often.
void computeSchedulerProb1(storm::storage::SparseMatrix< ValueType > const &transitionMatrix, storm::storage::SparseMatrix< ValueType > const &backwardTransitions, storm::storage::BitVector const &consideredStates, storm::storage::BitVector const &statesToReach, std::vector< uint64_t > &choices, storm::storage::BitVector const *allowedChoices=nullptr)
void computeSchedulerProb0(storm::storage::SparseMatrix< ValueType > const &transitionMatrix, storm::storage::SparseMatrix< ValueType > const &backwardTransitions, storm::storage::BitVector const &consideredStates, storm::storage::BitVector const &statesToAvoid, storm::storage::BitVector const &allowedChoices, std::vector< uint64_t > &choices)
bool constexpr maximize(OptimizationDirection d)
bool constexpr minimize(OptimizationDirection d)
storm::storage::BitVector performProbGreater0(storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates, bool useStepBound, uint_fast64_t maximalSteps)
Performs a backward depth-first search trough the underlying graph structure of the given model to de...
storm::storage::BitVector performProbGreater0A(storm::storage::SparseMatrix< T > const &transitionMatrix, std::vector< uint_fast64_t > const &nondeterministicChoiceIndices, storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates, bool useStepBound, uint_fast64_t maximalSteps, boost::optional< storm::storage::BitVector > const &choiceConstraint)
Computes the sets of states that have probability greater 0 of satisfying phi until psi under any pos...
storm::storage::BitVector performProbGreater0E(storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates, bool useStepBound, uint_fast64_t maximalSteps)
Computes the sets of states that have probability greater 0 of satisfying phi until psi under at leas...
storm::storage::BitVector performProb0E(storm::models::sparse::NondeterministicModel< T, RM > const &model, storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates)
Computes the sets of states that have probability 0 of satisfying phi until psi under at least one po...
T dotProduct(std::vector< T > const &firstOperand, std::vector< T > const &secondOperand)
Computes the dot product (aka scalar product) and returns the result.
void setVectorValues(std::vector< T > &vector, storm::storage::BitVector const &positions, std::vector< T > const &values)
Sets the provided values at the provided positions in the given vector.
void selectVectorValues(std::vector< T > &vector, storm::storage::BitVector const &positions, std::vector< T > const &values)
Selects the elements from a vector at the specified positions and writes them consecutively into anot...
void addScaledVector(std::vector< InValueType1 > &firstOperand, std::vector< InValueType2 > const &secondOperand, InValueType3 const &factor)
Computes x:= x + a*y, i.e., adds each element of the first vector and (the corresponding element of t...
std::string toString(std::vector< ValueType > const &vector)
Output vector as string.
void clip(std::vector< ValueType > &x, boost::optional< ValueType > const &lowerBound, boost::optional< ValueType > const &upperBound)
Takes the input vector and ensures that all entries conform to the bounds.
VT sum_if(std::vector< VT > const &values, storm::storage::BitVector const &filter)
Sum the entries from values that are set to one in the filter vector.
std::vector< uint_fast64_t > getSortedIndices(std::vector< T > const &v)
Returns a list of indices such that the first index refers to the highest entry of the given vector,...
storm::storage::BitVector filterZero(std::vector< T > const &values)
Retrieves a bit vector containing all the indices for which the value at this position is equal to ze...
void scaleVectorInPlace(std::vector< ValueType1 > &target, ValueType2 const &factor)
Multiplies each element of the given vector with the given factor and writes the result into the vect...
bool hasNonZeroEntry(std::vector< T > const &v)
std::vector< Type > filterVector(std::vector< Type > const &in, storm::storage::BitVector const &filter)
bool isOne(ValueType const &a)
bool isZero(ValueType const &a)
ValueType abs(ValueType const &number)
ValueType sqrt(ValueType const &number)
std::shared_ptr< SparseModelType > preprocessedModel
bool containsRewardBoundedObjective() const