Storm 1.11.1.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
StandardPcaaWeightVectorChecker.cpp
Go to the documentation of this file.
2
3#include <map>
4#include <set>
5
23#include "storm/utility/graph.h"
26
27namespace storm {
28namespace modelchecker {
29namespace multiobjective {
30
31template<class SparseModelType>
34 : PcaaWeightVectorChecker<SparseModelType>(preprocessorResult.objectives) {
35 // Intantionally left empty
36}
37
38template<class SparseModelType>
42 STORM_LOG_THROW(rewardAnalysis.rewardFinitenessType != preprocessing::RewardFinitenessType::Infinite, storm::exceptions::NotSupportedException,
43 "There is no Pareto optimal scheduler that yields finite reward for all objectives. This is not supported.");
44 STORM_LOG_WARN_COND(rewardAnalysis.rewardFinitenessType == preprocessing::RewardFinitenessType::AllFinite,
45 "There might be infinite reward for some scheduler. Multi-objective model checking restricts to schedulers that yield finite reward "
46 "for all objectives. Be aware that solutions yielding infinite reward are discarded.");
47 STORM_LOG_THROW(rewardAnalysis.totalRewardLessInfinityEStates, storm::exceptions::UnexpectedException,
48 "The set of states with reward < infinity for some scheduler has not been computed during preprocessing.");
49 STORM_LOG_THROW(preprocessorResult.containsOnlyTrivialObjectives(), storm::exceptions::NotSupportedException,
50 "At least one objective was not reduced to an expected (long run, total or cumulative) reward objective during preprocessing. This is not "
51 "supported by the considered weight vector checker.");
52 STORM_LOG_THROW(preprocessorResult.preprocessedModel->getInitialStates().getNumberOfSetBits() == 1, storm::exceptions::NotSupportedException,
53 "The model has multiple initial states.");
54
55 // Build a subsystem of the preprocessor result model that discards states that yield infinite reward for all schedulers.
56 // We can also merge the states that will have reward zero anyway.
57 storm::storage::BitVector maybeStates = rewardAnalysis.totalRewardLessInfinityEStates.get() & ~rewardAnalysis.reward0AStates;
58 storm::storage::BitVector finiteTotalRewardChoices = preprocessorResult.preprocessedModel->getTransitionMatrix().getRowFilter(
59 rewardAnalysis.totalRewardLessInfinityEStates.get(), rewardAnalysis.totalRewardLessInfinityEStates.get());
60 std::set<std::string> relevantRewardModels;
61 for (auto const& obj : this->objectives) {
62 obj.formula->gatherReferencedRewardModels(relevantRewardModels);
63 }
64 storm::transformer::GoalStateMerger<SparseModelType> merger(*preprocessorResult.preprocessedModel);
65 auto mergerResult =
66 merger.mergeTargetAndSinkStates(maybeStates, rewardAnalysis.reward0AStates, storm::storage::BitVector(maybeStates.size(), false),
67 std::vector<std::string>(relevantRewardModels.begin(), relevantRewardModels.end()), finiteTotalRewardChoices);
68 goalStateMergerInputToReducedStateIndexMapping = std::move(mergerResult.oldToNewStateIndexMapping);
69 goalStateMergerReducedToInputChoiceMapping = mergerResult.keptChoices.getNumberOfSetBitsBeforeIndices();
70 // Initialize data specific for the considered model type
71 initializeModelTypeSpecificData(*mergerResult.model);
72
73 // Initilize general data of the model
74 transitionMatrix = std::move(mergerResult.model->getTransitionMatrix());
75 initialState = *mergerResult.model->getInitialStates().begin();
76 totalReward0EStates = rewardAnalysis.totalReward0EStates % maybeStates;
77 if (mergerResult.targetState) {
78 // There is an additional state in the result
79 totalReward0EStates.resize(totalReward0EStates.size() + 1, true);
80
81 // 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.
82 storm::storage::BitVector targetStateAsVector(transitionMatrix.getRowGroupCount(), false);
83 targetStateAsVector.set(*mergerResult.targetState, true);
84 ecChoicesHint = transitionMatrix.getRowFilter(
85 storm::utility::graph::performProb0E(transitionMatrix, transitionMatrix.getRowGroupIndices(), transitionMatrix.transpose(true),
86 storm::storage::BitVector(targetStateAsVector.size(), true), targetStateAsVector));
87 ecChoicesHint.set(transitionMatrix.getRowGroupIndices()[*mergerResult.targetState], true);
88 } else {
89 ecChoicesHint = storm::storage::BitVector(transitionMatrix.getRowCount(), true);
90 }
92 // set data for unbounded objectives
93 lraObjectives = storm::storage::BitVector(this->objectives.size(), false);
94 objectivesWithNoUpperTimeBound = storm::storage::BitVector(this->objectives.size(), false);
95 actionsWithoutRewardInUnboundedPhase = storm::storage::BitVector(transitionMatrix.getRowCount(), true);
96 for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
97 auto const& formula = *this->objectives[objIndex].formula;
98 if (formula.getSubformula().isTotalRewardFormula()) {
99 objectivesWithNoUpperTimeBound.set(objIndex, true);
100 actionsWithoutRewardInUnboundedPhase &= storm::utility::vector::filterZero(actionRewards[objIndex]);
101 }
102 if (formula.getSubformula().isLongRunAverageRewardFormula()) {
103 lraObjectives.set(objIndex, true);
104 objectivesWithNoUpperTimeBound.set(objIndex, true);
105 }
107
108 // Set data for LRA objectives (if available)
109 if (!lraObjectives.empty()) {
110 lraMecDecomposition = LraMecDecomposition();
112 transitionMatrix, transitionMatrix.transpose(true), storm::storage::BitVector(transitionMatrix.getRowGroupCount(), true),
113 actionsWithoutRewardInUnboundedPhase);
114 lraMecDecomposition->auxMecValues.resize(lraMecDecomposition->mecs.size());
115 }
116
117 // initialize data for the results
118 checkHasBeenCalled = false;
119 objectiveResults.resize(this->objectives.size());
120 offsetsToUnderApproximation.resize(this->objectives.size(), storm::utility::zero<ValueType>());
121 offsetsToOverApproximation.resize(this->objectives.size(), storm::utility::zero<ValueType>());
122 optimalChoices.resize(transitionMatrix.getRowGroupCount(), 0);
123
124 // Print some statistics (if requested)
125 if (storm::settings::getModule<storm::settings::modules::CoreSettings>().isShowStatisticsSet()) {
126 STORM_PRINT_AND_LOG("Weight Vector Checker Statistics:\n");
127 STORM_PRINT_AND_LOG("Final preprocessed model has " << transitionMatrix.getRowGroupCount() << " states.\n");
128 STORM_PRINT_AND_LOG("Final preprocessed model has " << transitionMatrix.getRowCount() << " actions.\n");
129 if (lraMecDecomposition) {
130 STORM_PRINT_AND_LOG("Found " << lraMecDecomposition->mecs.size() << " end components that are relevant for LRA-analysis.\n");
131 uint64_t numLraMecStates = 0;
132 for (auto const& mec : this->lraMecDecomposition->mecs) {
133 numLraMecStates += mec.size();
134 }
135 STORM_PRINT_AND_LOG(numLraMecStates << " states lie on such an end component.\n");
136 }
138 }
139}
140
141template<class SparseModelType>
142void StandardPcaaWeightVectorChecker<SparseModelType>::check(Environment const& env, std::vector<ValueType> const& weightVector) {
143 checkHasBeenCalled = true;
144 STORM_LOG_INFO("Invoked WeightVectorChecker with weights \n"
145 << "\t" << storm::utility::vector::toString(storm::utility::vector::convertNumericVector<double>(weightVector)));
146
147 // Prepare and invoke weighted infinite horizon (long run average) phase
148 std::vector<ValueType> weightedRewardVector(transitionMatrix.getRowCount(), storm::utility::zero<ValueType>());
149 if (!lraObjectives.empty()) {
150 boost::optional<std::vector<ValueType>> weightedStateRewardVector;
151 for (auto objIndex : lraObjectives) {
152 ValueType weight =
153 storm::solver::minimize(this->objectives[objIndex].formula->getOptimalityType()) ? -weightVector[objIndex] : weightVector[objIndex];
154 storm::utility::vector::addScaledVector(weightedRewardVector, actionRewards[objIndex], weight);
155 if (!stateRewards.empty() && !stateRewards[objIndex].empty()) {
156 if (!weightedStateRewardVector) {
157 weightedStateRewardVector = std::vector<ValueType>(transitionMatrix.getRowGroupCount(), storm::utility::zero<ValueType>());
158 }
159 storm::utility::vector::addScaledVector(weightedStateRewardVector.get(), stateRewards[objIndex], weight);
160 }
161 }
162 infiniteHorizonWeightedPhase(env, weightedRewardVector, weightedStateRewardVector);
163 // Clear all values of the weighted reward vector
164 weightedRewardVector.assign(weightedRewardVector.size(), storm::utility::zero<ValueType>());
165 }
166
167 // Prepare and invoke weighted indefinite horizon (unbounded total reward) phase
168 auto totalRewardObjectives = objectivesWithNoUpperTimeBound & ~lraObjectives;
169 for (auto objIndex : totalRewardObjectives) {
170 if (storm::solver::minimize(this->objectives[objIndex].formula->getOptimalityType())) {
171 storm::utility::vector::addScaledVector(weightedRewardVector, actionRewards[objIndex], -weightVector[objIndex]);
172 } else {
173 storm::utility::vector::addScaledVector(weightedRewardVector, actionRewards[objIndex], weightVector[objIndex]);
174 }
175 }
176 unboundedWeightedPhase(env, weightedRewardVector, weightVector);
177
178 unboundedIndividualPhase(env, weightVector);
179 // Only invoke boundedPhase if necessarry, i.e., if there is at least one objective with a time bound
180 for (auto const& obj : this->objectives) {
181 if (!obj.formula->getSubformula().isTotalRewardFormula() && !obj.formula->getSubformula().isLongRunAverageRewardFormula()) {
182 boundedPhase(env, weightVector, weightedRewardVector);
183 break;
184 }
185 }
186 STORM_LOG_INFO("Weight vector check done. Lower bounds for results in initial state: "
187 << storm::utility::vector::toString(storm::utility::vector::convertNumericVector<double>(getUnderApproximationOfInitialStateResults())));
188 // Validate that the results are sufficiently precise
189 ValueType resultingWeightedPrecision =
190 storm::utility::abs<ValueType>(storm::utility::vector::dotProduct(getOverApproximationOfInitialStateResults(), weightVector) -
191 storm::utility::vector::dotProduct(getUnderApproximationOfInitialStateResults(), weightVector));
192 resultingWeightedPrecision /= storm::utility::sqrt(storm::utility::vector::dotProduct(weightVector, weightVector));
193 STORM_LOG_THROW(resultingWeightedPrecision <= this->getWeightedPrecision(), storm::exceptions::UnexpectedException,
194 "The desired precision was not reached");
195}
196
197template<class SparseModelType>
198std::vector<typename StandardPcaaWeightVectorChecker<SparseModelType>::ValueType>
200 STORM_LOG_THROW(checkHasBeenCalled, storm::exceptions::IllegalFunctionCallException, "Tried to retrieve results but check(..) has not been called before.");
201 std::vector<ValueType> res;
202 res.reserve(this->objectives.size());
203 for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
204 res.push_back(this->objectiveResults[objIndex][initialState] + this->offsetsToUnderApproximation[objIndex]);
205 }
206 return res;
207}
208
209template<class SparseModelType>
210std::vector<typename StandardPcaaWeightVectorChecker<SparseModelType>::ValueType>
212 STORM_LOG_THROW(checkHasBeenCalled, storm::exceptions::IllegalFunctionCallException, "Tried to retrieve results but check(..) has not been called before.");
213 std::vector<ValueType> res;
214 res.reserve(this->objectives.size());
215 for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
216 res.push_back(this->objectiveResults[objIndex][initialState] + this->offsetsToOverApproximation[objIndex]);
217 }
218 return res;
219}
220
221template<class SparseModelType>
224 STORM_LOG_THROW(this->checkHasBeenCalled, storm::exceptions::IllegalFunctionCallException,
225 "Tried to retrieve results but check(..) has not been called before.");
226 for (auto const& obj : this->objectives) {
227 STORM_LOG_THROW(obj.formula->getSubformula().isTotalRewardFormula() || obj.formula->getSubformula().isLongRunAverageRewardFormula(),
228 storm::exceptions::NotImplementedException, "Scheduler retrival is only implemented for objectives without time-bound.");
229 }
230 auto const numStatesOfInputModel = goalStateMergerInputToReducedStateIndexMapping.size();
231 storm::storage::Scheduler<ValueType> result(numStatesOfInputModel);
232 for (uint64_t inputModelState = 0; inputModelState < numStatesOfInputModel; ++inputModelState) {
233 auto const reducedModelState = goalStateMergerInputToReducedStateIndexMapping[inputModelState];
234 if (reducedModelState >= optimalChoices.size()) {
235 // This state is a "reward0AState", i.e., it has no reward for any scheduler. We can set an arbitrary choice here.
236 result.setChoice(0, inputModelState);
237 } else {
238 auto const reducedModelChoice = optimalChoices[reducedModelState];
239 auto const inputModelChoice = goalStateMergerReducedToInputChoiceMapping[reducedModelChoice];
240 result.setChoice(inputModelChoice, inputModelState);
241 }
242 }
243 return result;
244}
245
246template<typename ValueType>
248 storm::storage::BitVector const& consideredStates, storm::storage::BitVector const& statesToReach, std::vector<uint64_t>& choices,
249 storm::storage::BitVector const* allowedChoices = nullptr) {
250 std::vector<uint64_t> stack;
251 storm::storage::BitVector processedStates = statesToReach;
252 stack.insert(stack.end(), processedStates.begin(), processedStates.end());
253 uint64_t currentState = 0;
254
255 while (!stack.empty()) {
256 currentState = stack.back();
257 stack.pop_back();
258
259 for (auto const& predecessorEntry : backwardTransitions.getRow(currentState)) {
260 auto predecessor = predecessorEntry.getColumn();
261 if (consideredStates.get(predecessor) && !processedStates.get(predecessor)) {
262 // Find a choice leading to an already processed state (such a choice has to exist since this is a predecessor of the currentState)
263 auto const& groupStart = transitionMatrix.getRowGroupIndices()[predecessor];
264 auto const& groupEnd = transitionMatrix.getRowGroupIndices()[predecessor + 1];
265 uint64_t row = allowedChoices ? allowedChoices->getNextSetIndex(groupStart) : groupStart;
266 for (; row < groupEnd; row = allowedChoices ? allowedChoices->getNextSetIndex(row + 1) : row + 1) {
267 bool hasSuccessorInProcessedStates = false;
268 for (auto const& successorOfPredecessor : transitionMatrix.getRow(row)) {
269 if (processedStates.get(successorOfPredecessor.getColumn())) {
270 hasSuccessorInProcessedStates = true;
271 break;
272 }
273 }
274 if (hasSuccessorInProcessedStates) {
275 choices[predecessor] = row - groupStart;
276 processedStates.set(predecessor, true);
277 stack.push_back(predecessor);
278 break;
279 }
280 }
281 STORM_LOG_ASSERT(allowedChoices || row < groupEnd,
282 "Unable to find choice at a predecessor of a processed state that leads to a processed state.");
283 }
284 }
285 }
286 STORM_LOG_ASSERT(consideredStates.isSubsetOf(processedStates), "Not all states have been processed.");
287}
288
289template<typename ValueType>
291 storm::storage::BitVector const& consideredStates, storm::storage::BitVector const& statesToAvoid,
292 storm::storage::BitVector const& allowedChoices, std::vector<uint64_t>& choices) {
293 for (auto state : consideredStates) {
294 auto const& groupStart = transitionMatrix.getRowGroupIndices()[state];
295 auto const& groupEnd = transitionMatrix.getRowGroupIndices()[state + 1];
296 bool choiceFound = false;
297 for (uint64_t row = allowedChoices.getNextSetIndex(groupStart); row < groupEnd; row = allowedChoices.getNextSetIndex(row + 1)) {
298 choiceFound = true;
299 for (auto const& element : transitionMatrix.getRow(row)) {
300 if (statesToAvoid.get(element.getColumn())) {
301 choiceFound = false;
302 break;
303 }
304 }
305 if (choiceFound) {
306 choices[state] = row - groupStart;
307 break;
308 }
309 }
310 STORM_LOG_ASSERT(choiceFound, "Unable to find choice for a state.");
311 }
312}
313
314template<typename ValueType>
315std::vector<uint64_t> computeValidInitialScheduler(storm::storage::SparseMatrix<ValueType> const& matrix, storm::storage::BitVector const& rowsWithSumLessOne) {
316 std::vector<uint64_t> result(matrix.getRowGroupCount());
317 auto const& groups = matrix.getRowGroupIndices();
318 auto backwardsTransitions = matrix.transpose(true);
319 storm::storage::BitVector processedStates(result.size(), false);
320 for (uint64_t state = 0; state < result.size(); ++state) {
321 if (rowsWithSumLessOne.getNextSetIndex(groups[state]) < groups[state + 1]) {
322 result[state] = rowsWithSumLessOne.getNextSetIndex(groups[state]) - groups[state];
323 processedStates.set(state, true);
324 }
325 }
326
327 computeSchedulerProb1(matrix, backwardsTransitions, ~processedStates, processedStates, result);
328 return result;
329}
330
336template<typename ValueType>
338 storm::storage::SparseMatrix<ValueType> const& backwardTransitions, storm::storage::BitVector const& finitelyOftenChoices,
339 storm::storage::BitVector safeStates, std::vector<uint64_t>& choices) {
340 auto badStates = transitionMatrix.getRowGroupFilter(finitelyOftenChoices, true) & ~safeStates;
341 // badStates shall only be reached finitely often
342
343 auto reachBadWithProbGreater0AStates = storm::utility::graph::performProbGreater0A(
344 transitionMatrix, transitionMatrix.getRowGroupIndices(), backwardTransitions, ~safeStates, badStates, false, 0, ~finitelyOftenChoices);
345 // States in ~reachBadWithProbGreater0AStates can avoid bad states forever by only taking ~finitelyOftenChoices.
346 // We compute a scheduler for these states achieving exactly this (but we exclude the safe states)
347 auto avoidBadStates = ~reachBadWithProbGreater0AStates & ~safeStates;
348 computeSchedulerProb0(transitionMatrix, backwardTransitions, avoidBadStates, reachBadWithProbGreater0AStates, ~finitelyOftenChoices, choices);
349
350 // We need to take care of states that will reach a bad state with prob greater 0 (including the bad states themselves).
351 // due to the precondition, we know that it has to be possible to eventually avoid the bad states for ever.
352 // Perform a backwards search from the avoid states and store choices with prob. 1
353 computeSchedulerProb1(transitionMatrix, backwardTransitions, reachBadWithProbGreater0AStates, avoidBadStates | safeStates, choices);
354}
355
356template<class SparseModelType>
358 std::vector<ValueType> const& weightedActionRewardVector,
359 boost::optional<std::vector<ValueType>> const& weightedStateRewardVector) {
360 // Compute the optimal (weighted) lra value for each mec, keeping track of the optimal choices
361 STORM_LOG_ASSERT(lraMecDecomposition, "Mec decomposition for lra computations not initialized.");
362 storm::modelchecker::helper::SparseNondeterministicInfiniteHorizonHelper<ValueType> helper = createNondetInfiniteHorizonHelper(this->transitionMatrix);
363 helper.provideLongRunComponentDecomposition(lraMecDecomposition->mecs);
364 helper.setOptimizationDirection(storm::solver::OptimizationDirection::Maximize);
365 helper.setProduceScheduler(true);
366 for (uint64_t mecIndex = 0; mecIndex < lraMecDecomposition->mecs.size(); ++mecIndex) {
367 auto const& mec = lraMecDecomposition->mecs[mecIndex];
368 auto actionValueGetter = [&weightedActionRewardVector](uint64_t const& a) { return weightedActionRewardVector[a]; };
370 if (weightedStateRewardVector) {
371 stateValueGetter = [&weightedStateRewardVector](uint64_t const& s) { return weightedStateRewardVector.get()[s]; };
372 } else {
373 stateValueGetter = [](uint64_t const&) { return storm::utility::zero<ValueType>(); };
374 }
375 lraMecDecomposition->auxMecValues[mecIndex] = helper.computeLraForComponent(env, stateValueGetter, actionValueGetter, mec);
376 }
377 // Extract the produced optimal choices for the MECs
378 this->optimalChoices = std::move(helper.getProducedOptimalChoices());
379}
380
381template<class SparseModelType>
382void StandardPcaaWeightVectorChecker<SparseModelType>::unboundedWeightedPhase(Environment const& env, std::vector<ValueType> const& weightedRewardVector,
383 std::vector<ValueType> const& weightVector) {
384 // Catch the case where all values on the RHS of the MinMax equation system are zero.
385 if (this->objectivesWithNoUpperTimeBound.empty() ||
386 ((this->lraObjectives.empty() || !storm::utility::vector::hasNonZeroEntry(lraMecDecomposition->auxMecValues)) &&
387 !storm::utility::vector::hasNonZeroEntry(weightedRewardVector))) {
388 this->weightedResult.assign(transitionMatrix.getRowGroupCount(), storm::utility::zero<ValueType>());
389 storm::storage::BitVector statesInLraMec(transitionMatrix.getRowGroupCount(), false);
390 if (this->lraMecDecomposition) {
391 for (auto const& mec : this->lraMecDecomposition->mecs) {
392 for (auto const& sc : mec) {
393 statesInLraMec.set(sc.first, true);
394 }
395 }
396 }
397 // Get an arbitrary scheduler that yields finite reward for all objectives
398 computeSchedulerFinitelyOften(transitionMatrix, transitionMatrix.transpose(true), ~actionsWithoutRewardInUnboundedPhase, statesInLraMec,
399 this->optimalChoices);
400 return;
401 }
402
403 updateEcQuotient(weightedRewardVector);
404
405 // Set up the choice values
406 storm::utility::vector::selectVectorValues(ecQuotient->auxChoiceValues, ecQuotient->ecqToOriginalChoiceMapping, weightedRewardVector);
407 std::map<uint64_t, uint64_t> ecqStateToOptimalMecMap;
408 if (!lraObjectives.empty()) {
409 // We also need to assign a value for each ecQuotientChoice that corresponds to "staying" in the eliminated EC. (at this point these choices should all
410 // have a value of zero). Since each of the eliminated ECs has to contain *at least* one LRA EC, we need to find the largest value among the contained
411 // LRA ECs
412 storm::storage::BitVector foundEcqChoices(ecQuotient->matrix.getRowCount(), false); // keeps track of choices we have already seen before
413 for (uint64_t mecIndex = 0; mecIndex < lraMecDecomposition->mecs.size(); ++mecIndex) {
414 auto const& mec = lraMecDecomposition->mecs[mecIndex];
415 auto const& mecValue = lraMecDecomposition->auxMecValues[mecIndex];
416 uint64_t ecqState = ecQuotient->originalToEcqStateMapping[mec.begin()->first];
417 if (ecqState >= ecQuotient->matrix.getRowGroupCount()) {
418 // The mec was not part of the ecquotient. This means that it must have value 0.
419 // No further processing is needed.
420 continue;
421 }
422 uint64_t ecqChoice = ecQuotient->ecqStayInEcChoices.getNextSetIndex(ecQuotient->matrix.getRowGroupIndices()[ecqState]);
423 STORM_LOG_ASSERT(ecqChoice < ecQuotient->matrix.getRowGroupIndices()[ecqState + 1],
424 "Unable to find choice that represents staying inside the (eliminated) ec.");
425 auto& ecqChoiceValue = ecQuotient->auxChoiceValues[ecqChoice];
426 auto insertionRes = ecqStateToOptimalMecMap.emplace(ecqState, mecIndex);
427 if (insertionRes.second) {
428 // We have seen this ecqState for the first time.
430 "Expected a total reward of zero for choices that represent staying in an EC for ever.");
431 ecqChoiceValue = mecValue;
432 } else {
433 if (mecValue > ecqChoiceValue) { // found a larger value
434 ecqChoiceValue = mecValue;
435 insertionRes.first->second = mecIndex;
436 }
437 }
438 }
439 }
440
442 std::unique_ptr<storm::solver::MinMaxLinearEquationSolver<ValueType>> solver = solverFactory.create(env, ecQuotient->matrix);
443 solver->setTrackScheduler(true);
444 solver->setHasUniqueSolution(true);
445 solver->setOptimizationDirection(storm::solver::OptimizationDirection::Maximize);
446 auto req = solver->getRequirements(env, storm::solver::OptimizationDirection::Maximize);
447 setBoundsToSolver(*solver, req.lowerBounds(), req.upperBounds(), weightVector, objectivesWithNoUpperTimeBound, ecQuotient->matrix,
448 ecQuotient->rowsWithSumLessOne, ecQuotient->auxChoiceValues);
449 if (solver->hasLowerBound()) {
450 req.clearLowerBounds();
451 }
452 if (solver->hasUpperBound()) {
453 req.clearUpperBounds();
454 }
455 if (req.validInitialScheduler()) {
456 solver->setInitialScheduler(computeValidInitialScheduler(ecQuotient->matrix, ecQuotient->rowsWithSumLessOne));
457 req.clearValidInitialScheduler();
458 }
459 STORM_LOG_THROW(!req.hasEnabledCriticalRequirement(), storm::exceptions::UncheckedRequirementException,
460 "Solver requirements " + req.getEnabledRequirementsAsString() + " not checked.");
461 solver->setRequirementsChecked(true);
462
463 // Use the (0...0) vector as initial guess for the solution.
464 std::fill(ecQuotient->auxStateValues.begin(), ecQuotient->auxStateValues.end(), storm::utility::zero<ValueType>());
465
466 solver->solveEquations(env, ecQuotient->auxStateValues, ecQuotient->auxChoiceValues);
467 this->weightedResult = std::vector<ValueType>(transitionMatrix.getRowGroupCount());
468
469 transformEcqSolutionToOriginalModel(ecQuotient->auxStateValues, solver->getSchedulerChoices(), ecqStateToOptimalMecMap, this->weightedResult,
470 this->optimalChoices);
471}
472
473template<class SparseModelType>
474void StandardPcaaWeightVectorChecker<SparseModelType>::unboundedIndividualPhase(Environment const& env, std::vector<ValueType> const& weightVector) {
475 if (objectivesWithNoUpperTimeBound.getNumberOfSetBits() == 1 && storm::utility::isOne(weightVector[*objectivesWithNoUpperTimeBound.begin()])) {
476 uint_fast64_t objIndex = *objectivesWithNoUpperTimeBound.begin();
477 objectiveResults[objIndex] = weightedResult;
478 if (storm::solver::minimize(this->objectives[objIndex].formula->getOptimalityType())) {
479 storm::utility::vector::scaleVectorInPlace(objectiveResults[objIndex], -storm::utility::one<ValueType>());
480 }
481 for (uint_fast64_t objIndex2 = 0; objIndex2 < this->objectives.size(); ++objIndex2) {
482 if (objIndex != objIndex2) {
483 objectiveResults[objIndex2] = std::vector<ValueType>(transitionMatrix.getRowGroupCount(), storm::utility::zero<ValueType>());
484 }
485 }
486 } else {
487 storm::storage::SparseMatrix<ValueType> deterministicMatrix = transitionMatrix.selectRowsFromRowGroups(this->optimalChoices, false);
488 storm::storage::SparseMatrix<ValueType> deterministicBackwardTransitions = deterministicMatrix.transpose();
489 std::vector<ValueType> deterministicStateRewards(deterministicMatrix.getRowCount()); // allocate here
491
492 auto infiniteHorizonHelper = createDetInfiniteHorizonHelper(deterministicMatrix);
493 infiniteHorizonHelper.provideBackwardTransitions(deterministicBackwardTransitions);
494
495 // We compute an estimate for the results of the individual objectives which is obtained from the weighted result and the result of the objectives
496 // computed so far. Note that weightedResult = Sum_{i=1}^{n} w_i * objectiveResult_i.
497 std::vector<ValueType> weightedSumOfUncheckedObjectives = weightedResult;
498 ValueType sumOfWeightsOfUncheckedObjectives = storm::utility::vector::sum_if(weightVector, objectivesWithNoUpperTimeBound);
499
500 for (uint_fast64_t const& objIndex : storm::utility::vector::getSortedIndices(weightVector)) {
501 auto const& obj = this->objectives[objIndex];
502 if (objectivesWithNoUpperTimeBound.get(objIndex)) {
503 offsetsToUnderApproximation[objIndex] = storm::utility::zero<ValueType>();
504 offsetsToOverApproximation[objIndex] = storm::utility::zero<ValueType>();
505 if (lraObjectives.get(objIndex)) {
506 auto actionValueGetter = [&](uint64_t const& a) {
507 return actionRewards[objIndex][transitionMatrix.getRowGroupIndices()[a] + this->optimalChoices[a]];
508 };
510 if (stateRewards.empty() || stateRewards[objIndex].empty()) {
511 stateValueGetter = [](uint64_t const&) { return storm::utility::zero<ValueType>(); };
512 } else {
513 stateValueGetter = [&](uint64_t const& s) { return stateRewards[objIndex][s]; };
514 }
515 objectiveResults[objIndex] = infiniteHorizonHelper.computeLongRunAverageValues(env, stateValueGetter, actionValueGetter);
516 } else { // i.e. a total reward objective
517 storm::utility::vector::selectVectorValues(deterministicStateRewards, this->optimalChoices, transitionMatrix.getRowGroupIndices(),
518 actionRewards[objIndex]);
519 storm::storage::BitVector statesWithRewards = ~storm::utility::vector::filterZero(deterministicStateRewards);
520 // As maybestates we pick the states from which a state with reward is reachable
522 deterministicBackwardTransitions, storm::storage::BitVector(deterministicMatrix.getRowCount(), true), statesWithRewards);
523
524 // Compute the estimate for this objective
525 if (!storm::utility::isZero(weightVector[objIndex]) && !storm::utility::isZero(sumOfWeightsOfUncheckedObjectives)) {
526 objectiveResults[objIndex] = weightedSumOfUncheckedObjectives;
527 ValueType scalingFactor = storm::utility::one<ValueType>() / sumOfWeightsOfUncheckedObjectives;
528 if (storm::solver::minimize(obj.formula->getOptimalityType())) {
529 scalingFactor *= -storm::utility::one<ValueType>();
530 }
531 storm::utility::vector::scaleVectorInPlace(objectiveResults[objIndex], scalingFactor);
532 storm::utility::vector::clip(objectiveResults[objIndex], obj.lowerResultBound, obj.upperResultBound);
533 }
534 // Make sure that the objectiveResult is initialized correctly
535 objectiveResults[objIndex].resize(transitionMatrix.getRowGroupCount(), storm::utility::zero<ValueType>());
536
537 if (!maybeStates.empty()) {
538 bool needEquationSystem =
541 deterministicMatrix.getSubmatrix(true, maybeStates, maybeStates, needEquationSystem);
542 if (needEquationSystem) {
543 // Converting the matrix from the fixpoint notation to the form needed for the equation
544 // system. That is, we go from x = A*x + b to (I-A)x = b.
545 submatrix.convertToEquationSystem();
546 }
547
548 // Prepare solution vector and rhs of the equation system.
549 std::vector<ValueType> x = storm::utility::vector::filterVector(objectiveResults[objIndex], maybeStates);
550 std::vector<ValueType> b = storm::utility::vector::filterVector(deterministicStateRewards, maybeStates);
551
552 // Now solve the resulting equation system.
553 std::unique_ptr<storm::solver::LinearEquationSolver<ValueType>> solver = linearEquationSolverFactory.create(env, submatrix);
554 auto req = solver->getRequirements(env);
555 solver->clearBounds();
556 storm::storage::BitVector submatrixRowsWithSumLessOne = deterministicMatrix.getRowFilter(maybeStates, maybeStates) % maybeStates;
557 submatrixRowsWithSumLessOne.complement();
558 this->setBoundsToSolver(*solver, req.lowerBounds(), req.upperBounds(), objIndex, submatrix, submatrixRowsWithSumLessOne, b);
559 if (solver->hasLowerBound()) {
560 req.clearLowerBounds();
561 }
562 if (solver->hasUpperBound()) {
563 req.clearUpperBounds();
564 }
565 STORM_LOG_THROW(!req.hasEnabledCriticalRequirement(), storm::exceptions::UncheckedRequirementException,
566 "Solver requirements " + req.getEnabledRequirementsAsString() + " not checked.");
567 solver->solveEquations(env, x, b);
568 // Set the result for this objective accordingly
569 storm::utility::vector::setVectorValues<ValueType>(objectiveResults[objIndex], maybeStates, x);
570 }
571 storm::utility::vector::setVectorValues<ValueType>(objectiveResults[objIndex], ~maybeStates, storm::utility::zero<ValueType>());
572 }
573 // Update the estimate for the next objectives.
574 if (!storm::utility::isZero(weightVector[objIndex])) {
575 storm::utility::vector::addScaledVector(weightedSumOfUncheckedObjectives, objectiveResults[objIndex], -weightVector[objIndex]);
576 sumOfWeightsOfUncheckedObjectives -= weightVector[objIndex];
577 }
578 } else {
579 objectiveResults[objIndex] = std::vector<ValueType>(transitionMatrix.getRowGroupCount(), storm::utility::zero<ValueType>());
580 }
581 }
582 }
583}
584
585template<class SparseModelType>
586void StandardPcaaWeightVectorChecker<SparseModelType>::updateEcQuotient(std::vector<ValueType> const& weightedRewardVector) {
587 // Check whether we need to update the currently cached ecElimResult
588 storm::storage::BitVector newTotalReward0Choices = storm::utility::vector::filterZero(weightedRewardVector);
589 storm::storage::BitVector zeroLraRewardChoices(weightedRewardVector.size(), true);
590 if (lraMecDecomposition) {
591 for (uint64_t mecIndex = 0; mecIndex < lraMecDecomposition->mecs.size(); ++mecIndex) {
592 if (!storm::utility::isZero(lraMecDecomposition->auxMecValues[mecIndex])) {
593 // The mec has a non-zero value, so flag all its choices as non-zero
594 auto const& mec = lraMecDecomposition->mecs[mecIndex];
595 for (auto const& stateChoices : mec) {
596 for (auto const& choice : stateChoices.second) {
597 zeroLraRewardChoices.set(choice, false);
598 }
599 }
600 }
601 }
602 }
603 storm::storage::BitVector newReward0Choices = newTotalReward0Choices & zeroLraRewardChoices;
604 if (!ecQuotient || ecQuotient->origReward0Choices != newReward0Choices) {
605 // It is sufficient to consider the states from which a transition with non-zero reward is reachable. (The remaining states always have reward zero).
606 auto nonZeroRewardStates = transitionMatrix.getRowGroupFilter(newReward0Choices, true);
607 nonZeroRewardStates.complement();
609 transitionMatrix.transpose(true), storm::storage::BitVector(transitionMatrix.getRowGroupCount(), true), nonZeroRewardStates);
610
611 // Remove neutral end components, i.e., ECs in which no total reward is earned.
612 // Note that such ECs contain one (or maybe more) LRA ECs.
613 auto ecElimResult = storm::transformer::EndComponentEliminator<ValueType>::transform(transitionMatrix, subsystemStates,
614 ecChoicesHint & newTotalReward0Choices, totalReward0EStates);
615
616 storm::storage::BitVector rowsWithSumLessOne(ecElimResult.matrix.getRowCount(), false);
617 for (uint64_t row = 0; row < rowsWithSumLessOne.size(); ++row) {
618 if (ecElimResult.matrix.getRow(row).getNumberOfEntries() == 0) {
619 rowsWithSumLessOne.set(row, true);
620 } else {
621 for (auto const& entry : transitionMatrix.getRow(ecElimResult.newToOldRowMapping[row])) {
622 if (!subsystemStates.get(entry.getColumn())) {
623 rowsWithSumLessOne.set(row, true);
624 break;
625 }
626 }
627 }
628 }
629
630 ecQuotient = EcQuotient();
631 ecQuotient->matrix = std::move(ecElimResult.matrix);
632 ecQuotient->ecqToOriginalChoiceMapping = std::move(ecElimResult.newToOldRowMapping);
633 ecQuotient->originalToEcqStateMapping = std::move(ecElimResult.oldToNewStateMapping);
634 ecQuotient->ecqToOriginalStateMapping.resize(ecQuotient->matrix.getRowGroupCount());
635 for (uint64_t state = 0; state < ecQuotient->originalToEcqStateMapping.size(); ++state) {
636 uint64_t ecqState = ecQuotient->originalToEcqStateMapping[state];
637 if (ecqState < ecQuotient->matrix.getRowGroupCount()) {
638 ecQuotient->ecqToOriginalStateMapping[ecqState].insert(state);
639 }
640 }
641 ecQuotient->ecqStayInEcChoices = std::move(ecElimResult.sinkRows);
642 ecQuotient->origReward0Choices = std::move(newReward0Choices);
643 ecQuotient->origTotalReward0Choices = std::move(newTotalReward0Choices);
644 ecQuotient->rowsWithSumLessOne = std::move(rowsWithSumLessOne);
645 ecQuotient->auxStateValues.resize(ecQuotient->matrix.getRowGroupCount());
646 ecQuotient->auxChoiceValues.resize(ecQuotient->matrix.getRowCount());
647 }
648}
649
650template<class SparseModelType>
652 bool requiresUpper, uint64_t objIndex,
654 storm::storage::BitVector const& rowsWithSumLessOne,
655 std::vector<ValueType> const& rewards) const {
656 // Check whether bounds are already available
657 if (this->objectives[objIndex].lowerResultBound) {
658 solver.setLowerBound(this->objectives[objIndex].lowerResultBound.get());
659 }
660 if (this->objectives[objIndex].upperResultBound) {
661 solver.setUpperBound(this->objectives[objIndex].upperResultBound.get());
662 }
663
664 if ((requiresLower && !solver.hasLowerBound()) || (requiresUpper && !solver.hasUpperBound())) {
665 computeAndSetBoundsToSolver(solver, requiresLower, requiresUpper, transitions, rowsWithSumLessOne, rewards);
666 }
667}
668
669template<class SparseModelType>
671 bool requiresUpper, std::vector<ValueType> const& weightVector,
672 storm::storage::BitVector const& objectiveFilter,
674 storm::storage::BitVector const& rowsWithSumLessOne,
675 std::vector<ValueType> const& rewards) const {
676 // Check whether bounds are already available
677 boost::optional<ValueType> lowerBound = this->computeWeightedResultBound(true, weightVector, objectiveFilter & ~lraObjectives);
678 if (lowerBound) {
679 if (!lraObjectives.empty()) {
680 auto min = std::min_element(lraMecDecomposition->auxMecValues.begin(), lraMecDecomposition->auxMecValues.end());
681 if (min != lraMecDecomposition->auxMecValues.end()) {
682 lowerBound.get() += *min;
683 }
684 }
685 solver.setLowerBound(lowerBound.get());
686 }
687 boost::optional<ValueType> upperBound = this->computeWeightedResultBound(false, weightVector, objectiveFilter);
688 if (upperBound) {
689 if (!lraObjectives.empty()) {
690 auto max = std::max_element(lraMecDecomposition->auxMecValues.begin(), lraMecDecomposition->auxMecValues.end());
691 if (max != lraMecDecomposition->auxMecValues.end()) {
692 upperBound.get() += *max;
693 }
694 }
695 solver.setUpperBound(upperBound.get());
696 }
697
698 if ((requiresLower && !solver.hasLowerBound()) || (requiresUpper && !solver.hasUpperBound())) {
699 computeAndSetBoundsToSolver(solver, requiresLower, requiresUpper, transitions, rowsWithSumLessOne, rewards);
700 }
701}
702
703template<class SparseModelType>
705 bool requiresUpper,
707 storm::storage::BitVector const& rowsWithSumLessOne,
708 std::vector<ValueType> const& rewards) const {
709 // Compute the one step target probs
710 std::vector<ValueType> oneStepTargetProbs(transitions.getRowCount(), storm::utility::zero<ValueType>());
711 for (auto row : rowsWithSumLessOne) {
712 oneStepTargetProbs[row] = storm::utility::one<ValueType>() - transitions.getRowSum(row);
713 }
714
715 if (requiresLower && !solver.hasLowerBound()) {
716 // Compute lower bounds
717 std::vector<ValueType> negativeRewards;
718 negativeRewards.reserve(transitions.getRowCount());
719 uint64_t row = 0;
720 for (auto const& rew : rewards) {
721 if (rew < storm::utility::zero<ValueType>()) {
722 negativeRewards.resize(row, storm::utility::zero<ValueType>());
723 negativeRewards.push_back(-rew);
724 }
725 ++row;
726 }
727 if (!negativeRewards.empty()) {
728 negativeRewards.resize(row, storm::utility::zero<ValueType>());
729 std::vector<ValueType> lowerBounds =
730 storm::modelchecker::helper::DsMpiMdpUpperRewardBoundsComputer<ValueType>(transitions, negativeRewards, oneStepTargetProbs)
732 storm::utility::vector::scaleVectorInPlace(lowerBounds, -storm::utility::one<ValueType>());
733 solver.setLowerBounds(std::move(lowerBounds));
734 } else {
735 solver.setLowerBound(storm::utility::zero<ValueType>());
736 }
737 }
738
739 // Compute upper bounds
740 if (requiresUpper && !solver.hasUpperBound()) {
741 std::vector<ValueType> positiveRewards;
742 positiveRewards.reserve(transitions.getRowCount());
743 uint64_t row = 0;
744 for (auto const& rew : rewards) {
745 if (rew > storm::utility::zero<ValueType>()) {
746 positiveRewards.resize(row, storm::utility::zero<ValueType>());
747 positiveRewards.push_back(rew);
748 }
749 ++row;
750 }
751 if (!positiveRewards.empty()) {
752 positiveRewards.resize(row, storm::utility::zero<ValueType>());
753 solver.setUpperBound(
754 storm::modelchecker::helper::BaierUpperRewardBoundsComputer<ValueType>(transitions, positiveRewards, oneStepTargetProbs).computeUpperBound());
755 } else {
756 solver.setUpperBound(storm::utility::zero<ValueType>());
757 }
758 }
759}
760
761template<class SparseModelType>
763 std::vector<uint_fast64_t> const& ecqOptimalChoices,
764 std::map<uint64_t, uint64_t> const& ecqStateToOptimalMecMap,
765 std::vector<ValueType>& originalSolution,
766 std::vector<uint_fast64_t>& originalOptimalChoices) const {
767 auto backwardsTransitions = transitionMatrix.transpose(true);
768
769 // Keep track of states for which no choice has been set yet.
770 storm::storage::BitVector unprocessedStates(transitionMatrix.getRowGroupCount(), true);
771
772 // For each eliminated ec, keep track of the states (within the ec) that we want to reach and the states for which a choice needs to be set
773 // (Declared already at this point to avoid expensive allocations in each loop iteration)
774 storm::storage::BitVector ecStatesToReach(transitionMatrix.getRowGroupCount(), false);
775 storm::storage::BitVector ecStatesToProcess(transitionMatrix.getRowGroupCount(), false);
776
777 // Run through each state of the ec quotient as well as the associated state(s) of the original model
778 for (uint64_t ecqState = 0; ecqState < ecqSolution.size(); ++ecqState) {
779 uint64_t ecqChoice = ecQuotient->matrix.getRowGroupIndices()[ecqState] + ecqOptimalChoices[ecqState];
780 uint_fast64_t origChoice = ecQuotient->ecqToOriginalChoiceMapping[ecqChoice];
781 auto const& origStates = ecQuotient->ecqToOriginalStateMapping[ecqState];
782 STORM_LOG_ASSERT(!origStates.empty(), "Unexpected empty set of original states.");
783 if (ecQuotient->ecqStayInEcChoices.get(ecqChoice)) {
784 // We stay in the current state(s) forever (End component)
785 // We need to set choices in a way that (i) the optimal LRA Mec is reached (if there is any) and (ii) 0 total reward is collected.
786 if (!ecqStateToOptimalMecMap.empty()) {
787 // The current ecqState represents an elimnated EC and we need to stay in this EC and we need to make sure that optimal MEC decisions are
788 // performed within this EC.
789 STORM_LOG_ASSERT(ecqStateToOptimalMecMap.count(ecqState) > 0, "No Lra Mec associated to given eliminated EC");
790 auto const& lraMec = lraMecDecomposition->mecs[ecqStateToOptimalMecMap.at(ecqState)];
791 if (lraMec.size() == origStates.size()) {
792 // LRA mec and eliminated EC coincide
793 for (auto const& state : origStates) {
794 STORM_LOG_ASSERT(lraMec.containsState(state), "Expected state to be contained in the lra mec.");
795 // Note that the optimal choice for this state has already been set in the infinite horizon phase.
796 unprocessedStates.set(state, false);
797 originalSolution[state] = ecqSolution[ecqState];
798 }
799 } else {
800 // LRA mec is proper subset of eliminated ec. There are also other states for which we have to set choices leading to the LRA MEC inside.
801 STORM_LOG_ASSERT(lraMec.size() < origStates.size(), "Lra Mec (" << lraMec.size()
802 << " states) should be a proper subset of the eliminated ec ("
803 << origStates.size() << " states).");
804 for (auto const& state : origStates) {
805 if (lraMec.containsState(state)) {
806 ecStatesToReach.set(state, true);
807 // Note that the optimal choice for this state has already been set in the infinite horizon phase.
808 } else {
809 ecStatesToProcess.set(state, true);
810 }
811 unprocessedStates.set(state, false);
812 originalSolution[state] = ecqSolution[ecqState];
813 }
814 computeSchedulerProb1(transitionMatrix, backwardsTransitions, ecStatesToProcess, ecStatesToReach, originalOptimalChoices,
815 &ecQuotient->origTotalReward0Choices);
816 // Clear bitvectors for next ecqState.
817 ecStatesToProcess.clear();
818 ecStatesToReach.clear();
819 }
820 } else {
821 // If there is no LRA Mec to reach, we just need to make sure that finite total reward is collected for all objectives
822 // In this branch our BitVectors have a slightly different meaning, so we create more readable aliases
823 storm::storage::BitVector& ecStatesToAvoid = ecStatesToReach;
824 bool needSchedulerComputation = false;
825 STORM_LOG_ASSERT(storm::utility::isZero(ecqSolution[ecqState]),
826 "Solution for state that stays inside EC must be zero. Got " << ecqSolution[ecqState] << " instead.");
827 for (auto const& state : origStates) {
828 originalSolution[state] = storm::utility::zero<ValueType>(); // i.e. ecqSolution[ecqState];
829 ecStatesToProcess.set(state, true);
830 }
831 auto validChoices = transitionMatrix.getRowFilter(ecStatesToProcess, ecStatesToProcess);
832 auto valid0RewardChoices = validChoices & actionsWithoutRewardInUnboundedPhase;
833 for (auto const& state : origStates) {
834 auto groupStart = transitionMatrix.getRowGroupIndices()[state];
835 auto groupEnd = transitionMatrix.getRowGroupIndices()[state + 1];
836 auto nextValidChoice = valid0RewardChoices.getNextSetIndex(groupStart);
837 if (nextValidChoice < groupEnd) {
838 originalOptimalChoices[state] = nextValidChoice - groupStart;
839 } else {
840 // this state should not be reached infinitely often
841 ecStatesToAvoid.set(state, true);
842 needSchedulerComputation = true;
843 }
844 }
845 if (needSchedulerComputation) {
846 // There are ec states which we should not visit infinitely often
847 auto ecStatesThatCanAvoid =
848 storm::utility::graph::performProbGreater0A(transitionMatrix, transitionMatrix.getRowGroupIndices(), backwardsTransitions,
849 ecStatesToProcess, ecStatesToAvoid, false, 0, valid0RewardChoices);
850 ecStatesThatCanAvoid.complement();
851 // Set the choice for all states that can achieve value 0
852 computeSchedulerProb0(transitionMatrix, backwardsTransitions, ecStatesThatCanAvoid, ecStatesToAvoid, valid0RewardChoices,
853 originalOptimalChoices);
854 // Set the choice for all remaining states
855 computeSchedulerProb1(transitionMatrix, backwardsTransitions, ecStatesToProcess & ~ecStatesToAvoid, ecStatesToAvoid, originalOptimalChoices,
856 &validChoices);
857 }
858 ecStatesToAvoid.clear();
859 ecStatesToProcess.clear();
860 }
861 } else {
862 // We eventually leave the current state(s)
863 // In this case, we can safely take the origChoice at the corresponding state (say 's').
864 // For all other origStates associated with ecqState (if there are any), we make sure that the state 's' is reached almost surely.
865 if (origStates.size() > 1) {
866 for (auto const& state : origStates) {
867 // Check if the orig choice originates from this state
868 auto groupStart = transitionMatrix.getRowGroupIndices()[state];
869 auto groupEnd = transitionMatrix.getRowGroupIndices()[state + 1];
870 if (origChoice >= groupStart && origChoice < groupEnd) {
871 originalOptimalChoices[state] = origChoice - groupStart;
872 ecStatesToReach.set(state, true);
873 } else {
874 STORM_LOG_ASSERT(origStates.size() > 1, "Multiple original states expected.");
875 ecStatesToProcess.set(state, true);
876 }
877 unprocessedStates.set(state, false);
878 originalSolution[state] = ecqSolution[ecqState];
879 }
880 auto validChoices = transitionMatrix.getRowFilter(ecStatesToProcess, ecStatesToProcess | ecStatesToReach);
881 computeSchedulerProb1(transitionMatrix, backwardsTransitions, ecStatesToProcess, ecStatesToReach, originalOptimalChoices, &validChoices);
882 // Clear bitvectors for next ecqState.
883 ecStatesToProcess.clear();
884 ecStatesToReach.clear();
885 } else {
886 // There is just one state so we take the associated choice.
887 auto state = *origStates.begin();
888 auto groupStart = transitionMatrix.getRowGroupIndices()[state];
890 origChoice >= groupStart && origChoice < transitionMatrix.getRowGroupIndices()[state + 1],
891 "Invalid choice: " << originalOptimalChoices[state] << " at a state with " << transitionMatrix.getRowGroupSize(state) << " choices.");
892 originalOptimalChoices[state] = origChoice - groupStart;
893 originalSolution[state] = ecqSolution[ecqState];
894 unprocessedStates.set(state, false);
895 }
896 }
897 }
898
899 // The states that still not have been processed, there is no associated state of the ec quotient.
900 // This is because the value for these states will be 0 under all (lra optimal-) schedulers.
901 storm::utility::vector::setVectorValues(originalSolution, unprocessedStates, storm::utility::zero<ValueType>());
902 // Get a set of states for which we know that no reward (for all objectives) will be collected
903 if (this->lraMecDecomposition) {
904 // In this case, all unprocessed non-lra mec states should reach an (unprocessed) lra mec
905 for (auto const& mec : this->lraMecDecomposition->mecs) {
906 for (auto const& sc : mec) {
907 if (unprocessedStates.get(sc.first)) {
908 ecStatesToReach.set(sc.first, true);
909 }
910 }
911 }
912 } else {
913 ecStatesToReach = unprocessedStates & totalReward0EStates;
914 // Set a scheduler for the ecStates that we want to reach
915 computeSchedulerProb0(transitionMatrix, backwardsTransitions, ecStatesToReach, ~unprocessedStates | ~totalReward0EStates,
916 actionsWithoutRewardInUnboundedPhase, originalOptimalChoices);
917 }
918 unprocessedStates &= ~ecStatesToReach;
919 // Set a scheduler for the remaining states
920 computeSchedulerProb1(transitionMatrix, backwardsTransitions, unprocessedStates, ecStatesToReach, originalOptimalChoices);
921}
922
925
928
929} // namespace multiobjective
930} // namespace modelchecker
931} // namespace storm
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.
virtual ValueType computeLraForComponent(Environment const &env, ValueGetter const &stateValuesGetter, ValueGetter const &actionValuesGetter, storm::storage::MaximalEndComponent const &component) override
Helper Class that takes preprocessed Pcaa data and a weight vector and ...
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.
virtual std::vector< ValueType > getOverApproximationOfInitialStateResults() const override
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
StandardPcaaWeightVectorChecker(preprocessing::SparseMultiObjectivePreprocessorResult< SparseModelType > const &preprocessorResult)
virtual std::vector< ValueType > getUnderApproximationOfInitialStateResults() const override
Retrieves the results of the individual objectives at the initial state of the given model.
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...
void infiniteHorizonWeightedPhase(Environment const &env, std::vector< ValueType > const &weightedActionRewardVector, boost::optional< std::vector< ValueType > > const &weightedStateRewardVector)
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...
virtual void check(Environment const &env, std::vector< ValueType > const &weightVector) override
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.
Definition BitVector.h:16
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.
Definition Scheduler.h:18
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.
Definition Scheduler.cpp:38
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 ...
static EndComponentEliminatorReturnType transform(storm::storage::SparseMatrix< ValueType > const &originalMatrix, storm::storage::MaximalEndComponentDecomposition< ValueType > ecs, storm::storage::BitVector const &subsystemStates, storm::storage::BitVector const &addSinkRowStates, bool addSelfLoopAtSinkStates=false)
#define STORM_LOG_INFO(message)
Definition logging.h:24
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:11
#define STORM_LOG_WARN_COND(cond, message)
Definition macros.h:38
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
#define STORM_PRINT_AND_LOG(message)
Definition macros.h:68
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 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...
Definition graph.cpp:315
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...
Definition graph.cpp:841
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...
Definition graph.cpp:673
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...
Definition graph.cpp:960
T dotProduct(std::vector< T > const &firstOperand, std::vector< T > const &secondOperand)
Computes the dot product (aka scalar product) and returns the result.
Definition vector.h:473
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.
Definition vector.h:78
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...
Definition vector.h:184
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...
Definition vector.h:460
std::string toString(std::vector< ValueType > const &vector)
Output vector as string.
Definition vector.h:1145
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.
Definition vector.h:888
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.
Definition vector.h:552
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,...
Definition vector.h:144
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...
Definition vector.h:519
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...
Definition vector.h:447
bool hasNonZeroEntry(std::vector< T > const &v)
Definition vector.h:1099
std::vector< Type > filterVector(std::vector< Type > const &in, storm::storage::BitVector const &filter)
Definition vector.h:1060
bool isOne(ValueType const &a)
Definition constants.cpp:34
bool isZero(ValueType const &a)
Definition constants.cpp:39
ValueType sqrt(ValueType const &number)