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