Storm
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
StandardMaPcaaWeightVectorChecker.cpp
Go to the documentation of this file.
2
3#include <cmath>
4
14
17
22
23namespace storm {
24namespace modelchecker {
25namespace multiobjective {
26
27template<class SparseMaModelType>
33
34template<class SparseMaModelType>
36 markovianStates = model.getMarkovianStates();
37 exitRates = model.getExitRates();
38
39 // Set the (discretized) state action rewards.
40 this->actionRewards.assign(this->objectives.size(), {});
41 this->stateRewards.assign(this->objectives.size(), {});
42 for (uint64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
43 auto const& formula = *this->objectives[objIndex].formula;
44 STORM_LOG_THROW(formula.isRewardOperatorFormula() && formula.asRewardOperatorFormula().hasRewardModelName(), storm::exceptions::UnexpectedException,
45 "Unexpected type of operator formula: " << formula);
46 typename SparseMaModelType::RewardModelType const& rewModel = model.getRewardModel(formula.asRewardOperatorFormula().getRewardModelName());
47 STORM_LOG_ASSERT(!rewModel.hasTransitionRewards(), "Preprocessed Reward model has transition rewards which is not expected.");
48 this->actionRewards[objIndex] = rewModel.hasStateActionRewards()
49 ? rewModel.getStateActionRewardVector()
50 : std::vector<ValueType>(model.getTransitionMatrix().getRowCount(), storm::utility::zero<ValueType>());
51 if (formula.getSubformula().isTotalRewardFormula()) {
52 if (rewModel.hasStateRewards()) {
53 // Note that state rewards are earned over time and thus play no role for probabilistic states
54 for (auto markovianState : markovianStates) {
55 this->actionRewards[objIndex][model.getTransitionMatrix().getRowGroupIndices()[markovianState]] +=
56 rewModel.getStateReward(markovianState) / exitRates[markovianState];
57 }
58 }
59 } else if (formula.getSubformula().isLongRunAverageRewardFormula()) {
60 // The LRA methods for MA require keeping track of state- and action rewards separately
61 if (rewModel.hasStateRewards()) {
62 this->stateRewards[objIndex] = rewModel.getStateRewardVector();
63 }
64 } else {
65 STORM_LOG_THROW(formula.getSubformula().isCumulativeRewardFormula() &&
66 formula.getSubformula().asCumulativeRewardFormula().getTimeBoundReference().isTimeBound(),
67 storm::exceptions::UnexpectedException, "Unexpected type of sub-formula: " << formula.getSubformula());
68 STORM_LOG_THROW(!rewModel.hasStateRewards(), storm::exceptions::InvalidPropertyException,
69 "Found state rewards for time bounded objective " << this->objectives[objIndex].originalFormula << ". This is not supported.");
71 this->objectives[objIndex].originalFormula->isProbabilityOperatorFormula() &&
72 this->objectives[objIndex].originalFormula->asProbabilityOperatorFormula().getSubformula().isBoundedUntilFormula(),
73 "Objective " << this->objectives[objIndex].originalFormula
74 << " was simplified to a cumulative reward formula. Correctness of the algorithm is unknown for this type of property.");
75 }
76 }
77 // Print some statistics (if requested)
79 STORM_PRINT_AND_LOG("Final preprocessed model has " << markovianStates.getNumberOfSetBits() << " Markovian states.\n");
80 }
81}
82
83template<class SparseMdpModelType>
86 STORM_LOG_ASSERT(transitions.getRowGroupCount() == this->transitionMatrix.getRowGroupCount(), "Unexpected size of given matrix.");
87 return storm::modelchecker::helper::SparseNondeterministicInfiniteHorizonHelper<ValueType>(transitions, this->markovianStates, this->exitRates);
88}
89
90template<class SparseMdpModelType>
93 STORM_LOG_ASSERT(transitions.getRowGroupCount() == this->transitionMatrix.getRowGroupCount(), "Unexpected size of given matrix.");
94 // TODO: Right now, there is no dedicated support for "deterministic" Markov automata so we have to pick the nondeterministic one.
95 auto result = storm::modelchecker::helper::SparseNondeterministicInfiniteHorizonHelper<ValueType>(transitions, this->markovianStates, this->exitRates);
96 result.setOptimizationDirection(storm::solver::OptimizationDirection::Maximize);
97 return result;
98}
99
100template<class SparseMaModelType>
101void StandardMaPcaaWeightVectorChecker<SparseMaModelType>::boundedPhase(Environment const& env, std::vector<ValueType> const& weightVector,
102 std::vector<ValueType>& weightedRewardVector) {
103 // Split the preprocessed model into transitions from/to probabilistic/Markovian states.
104 SubModel MS = createSubModel(true, weightedRewardVector);
105 SubModel PS = createSubModel(false, weightedRewardVector);
106
107 // Apply digitization to Markovian transitions
108 ValueType digitizationConstant = getDigitizationConstant(weightVector);
109 digitize(MS, digitizationConstant);
110
111 // Get for each occurring (digitized) timeBound the indices of the objectives with that bound.
112 TimeBoundMap upperTimeBounds;
113 digitizeTimeBounds(upperTimeBounds, digitizationConstant);
114
115 // Check whether there is a cycle in of probabilistic states
116 bool acyclic = !storm::utility::graph::hasCycle(PS.toPS);
117
118 // Initialize a minMaxSolver to compute an optimal scheduler (w.r.t. PS) for each epoch
119 // No EC elimination is necessary as we assume non-zenoness
120 std::unique_ptr<MinMaxSolverData> minMax = initMinMaxSolver(env, PS, acyclic, weightVector);
121
122 // create a linear equation solver for the model induced by the optimal choice vector.
123 // the solver will be updated whenever the optimal choice vector has changed.
124 std::unique_ptr<LinEqSolverData> linEq = initLinEqSolver(env, PS, acyclic);
125
126 // Store the optimal choices of PS as computed by the minMax solver.
127 std::vector<uint_fast64_t> optimalChoicesAtCurrentEpoch(PS.getNumberOfStates(), std::numeric_limits<uint_fast64_t>::max());
128
129 // Stores the objectives for which we need to compute values in the current time epoch.
130 storm::storage::BitVector consideredObjectives = this->objectivesWithNoUpperTimeBound & ~this->lraObjectives;
131
132 auto upperTimeBoundIt = upperTimeBounds.begin();
133 uint_fast64_t currentEpoch = upperTimeBounds.empty() ? 0 : upperTimeBoundIt->first;
135 // Update the objectives that are considered at the current time epoch as well as the (weighted) reward vectors.
136 updateDataToCurrentEpoch(MS, PS, *minMax, consideredObjectives, currentEpoch, weightVector, upperTimeBoundIt, upperTimeBounds);
137
138 // Compute the values that can be obtained at probabilistic states in the current time epoch
139 performPSStep(env, PS, MS, *minMax, *linEq, optimalChoicesAtCurrentEpoch, consideredObjectives, weightVector);
140
141 // Compute values that can be obtained at Markovian states after letting one (digitized) time unit pass.
142 // Only perform such a step if there is time left.
143 if (currentEpoch > 0) {
144 performMSStep(env, MS, PS, consideredObjectives, weightVector);
145 --currentEpoch;
146 } else {
147 break;
148 }
149 }
150 STORM_LOG_WARN_COND(!storm::utility::resources::isTerminate(), "Time-bounded reachability computation aborted.");
151
152 // compose the results from MS and PS
153 storm::utility::vector::setVectorValues(this->weightedResult, MS.states, MS.weightedSolutionVector);
154 storm::utility::vector::setVectorValues(this->weightedResult, PS.states, PS.weightedSolutionVector);
155 for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
156 storm::utility::vector::setVectorValues(this->objectiveResults[objIndex], MS.states, MS.objectiveSolutionVectors[objIndex]);
157 storm::utility::vector::setVectorValues(this->objectiveResults[objIndex], PS.states, PS.objectiveSolutionVectors[objIndex]);
158 }
159}
160
161template<class SparseMaModelType>
162typename StandardMaPcaaWeightVectorChecker<SparseMaModelType>::SubModel StandardMaPcaaWeightVectorChecker<SparseMaModelType>::createSubModel(
163 bool createMS, std::vector<ValueType> const& weightedRewardVector) const {
164 SubModel result;
165
166 storm::storage::BitVector probabilisticStates = ~markovianStates;
167 result.states = createMS ? markovianStates : probabilisticStates;
168 result.choices = this->transitionMatrix.getRowFilter(result.states);
169 STORM_LOG_ASSERT(!createMS || result.states.getNumberOfSetBits() == result.choices.getNumberOfSetBits(),
170 "row groups for Markovian states should consist of exactly one row");
171
172 // We need to add diagonal entries for selfloops on Markovian states.
173 result.toMS = this->transitionMatrix.getSubmatrix(true, result.states, markovianStates, createMS);
174 result.toPS = this->transitionMatrix.getSubmatrix(true, result.states, probabilisticStates, false);
175 STORM_LOG_ASSERT(result.getNumberOfStates() == result.states.getNumberOfSetBits() && result.getNumberOfStates() == result.toMS.getRowGroupCount() &&
176 result.getNumberOfStates() == result.toPS.getRowGroupCount(),
177 "Invalid state count for subsystem");
178 STORM_LOG_ASSERT(result.getNumberOfChoices() == result.choices.getNumberOfSetBits() && result.getNumberOfChoices() == result.toMS.getRowCount() &&
179 result.getNumberOfChoices() == result.toPS.getRowCount(),
180 "Invalid choice count for subsystem");
181
182 result.weightedRewardVector.resize(result.getNumberOfChoices());
183 storm::utility::vector::selectVectorValues(result.weightedRewardVector, result.choices, weightedRewardVector);
184 for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
185 std::vector<ValueType> const& objRewards = this->actionRewards[objIndex];
186 std::vector<ValueType> subModelObjRewards;
187 subModelObjRewards.reserve(result.getNumberOfChoices());
188 for (auto choice : result.choices) {
189 subModelObjRewards.push_back(objRewards[choice]);
190 }
191 result.objectiveRewardVectors.push_back(std::move(subModelObjRewards));
192 }
193
194 result.weightedSolutionVector.resize(result.getNumberOfStates());
195 storm::utility::vector::selectVectorValues(result.weightedSolutionVector, result.states, this->weightedResult);
196 result.objectiveSolutionVectors.resize(this->objectives.size());
197 for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
198 result.objectiveSolutionVectors[objIndex].resize(result.weightedSolutionVector.size());
199 storm::utility::vector::selectVectorValues(result.objectiveSolutionVectors[objIndex], result.states, this->objectiveResults[objIndex]);
200 }
201
202 result.auxChoiceValues.resize(result.getNumberOfChoices());
203
204 return result;
205}
206
207template<class SparseMaModelType>
208template<typename VT, typename std::enable_if<storm::NumberTraits<VT>::SupportsExponential, int>::type>
209VT StandardMaPcaaWeightVectorChecker<SparseMaModelType>::getDigitizationConstant(std::vector<ValueType> const& weightVector) const {
210 STORM_LOG_DEBUG("Retrieving digitization constant");
211 // We need to find a delta such that for each objective it holds that lowerbound/delta , upperbound/delta are natural numbers and
212 // sum_{obj_i} (
213 // If obj_i has a lower and an upper bound:
214 // weightVector_i * (1 - e^(-maxRate lowerbound) * (1 + maxRate delta) ^ (lowerbound / delta) + 1-e^(-maxRate upperbound) * (1 + maxRate delta) ^
215 // (upperbound / delta) + (1-e^(-maxRate delta)))
216 // If there is only an upper bound:
217 // weightVector_i * ( 1-e^(-maxRate upperbound) * (1 + maxRate delta) ^ (upperbound / delta))
218 // ) <= this->maximumLowerUpperDistance
219
220 // Initialize some data for fast and easy access
221 VT const maxRate = storm::utility::vector::max_if(exitRates, markovianStates);
222 std::vector<VT> timeBounds;
223 std::vector<VT> eToPowerOfMinusMaxRateTimesBound;
224 VT smallestNonZeroBound = storm::utility::zero<VT>();
225 for (auto const& obj : this->objectives) {
226 if (obj.formula->getSubformula().isCumulativeRewardFormula()) {
227 timeBounds.push_back(obj.formula->getSubformula().asCumulativeRewardFormula().template getBound<VT>());
228 STORM_LOG_THROW(!storm::utility::isZero(timeBounds.back()), storm::exceptions::InvalidPropertyException,
229 "Got zero-valued upper time bound. This is not suppoted.");
230 eToPowerOfMinusMaxRateTimesBound.push_back(std::exp(-maxRate * timeBounds.back()));
231 smallestNonZeroBound = storm::utility::isZero(smallestNonZeroBound) ? timeBounds.back() : std::min(smallestNonZeroBound, timeBounds.back());
232 } else {
233 timeBounds.push_back(storm::utility::zero<VT>());
234 eToPowerOfMinusMaxRateTimesBound.push_back(storm::utility::zero<VT>());
235 }
236 }
237 if (storm::utility::isZero(smallestNonZeroBound)) {
238 // There are no time bounds. In this case, one is a valid digitization constant.
239 return storm::utility::one<VT>();
240 }
241 VT goalPrecisionTimesNorm = this->weightedPrecision * storm::utility::sqrt(storm::utility::vector::dotProduct(weightVector, weightVector));
242
243 // We brute-force a delta, since a direct computation is apparently not easy.
244 // Also note that the number of times this loop runs is a lower bound for the number of minMaxSolver invocations.
245 // Hence, this brute-force approach will most likely not be a bottleneck.
246 storm::storage::BitVector objectivesWithTimeBound = ~this->objectivesWithNoUpperTimeBound;
247 uint_fast64_t smallestStepBound = 1;
248 VT delta = smallestNonZeroBound / smallestStepBound;
249 while (true) {
250 bool deltaValid = true;
251 for (auto objIndex : objectivesWithTimeBound) {
252 auto const& timeBound = timeBounds[objIndex];
253 if (timeBound / delta != std::floor(timeBound / delta)) {
254 deltaValid = false;
255 break;
256 }
257 }
258 if (deltaValid) {
259 VT weightedPrecisionForCurrentDelta = storm::utility::zero<VT>();
260 for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
261 VT precisionOfObj = storm::utility::zero<VT>();
262 if (objectivesWithTimeBound.get(objIndex)) {
263 precisionOfObj +=
264 storm::utility::one<VT>() - (eToPowerOfMinusMaxRateTimesBound[objIndex] *
265 storm::utility::pow(storm::utility::one<VT>() + maxRate * delta, timeBounds[objIndex] / delta));
266 }
267 weightedPrecisionForCurrentDelta += weightVector[objIndex] * precisionOfObj;
268 }
269 deltaValid &= weightedPrecisionForCurrentDelta <= goalPrecisionTimesNorm;
270 }
271 if (deltaValid) {
272 break;
273 }
274 ++smallestStepBound;
275 STORM_LOG_ASSERT(delta > smallestNonZeroBound / smallestStepBound, "Digitization constant is expected to become smaller in every iteration");
276 delta = smallestNonZeroBound / smallestStepBound;
277 }
278 STORM_LOG_DEBUG("Found digitization constant: " << delta << ". At least " << smallestStepBound << " digitization steps will be necessarry");
279 return delta;
280}
281
282template<class SparseMaModelType>
283template<typename VT, typename std::enable_if<!storm::NumberTraits<VT>::SupportsExponential, int>::type>
284VT StandardMaPcaaWeightVectorChecker<SparseMaModelType>::getDigitizationConstant(std::vector<ValueType> const& /*weightVector*/) const {
285 STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Computing bounded probabilities of MAs is unsupported for this value type.");
286}
287
288template<class SparseMaModelType>
289template<typename VT, typename std::enable_if<storm::NumberTraits<VT>::SupportsExponential, int>::type>
290void StandardMaPcaaWeightVectorChecker<SparseMaModelType>::digitize(SubModel& MS, VT const& digitizationConstant) const {
291 std::vector<VT> rateVector(MS.getNumberOfChoices());
292 storm::utility::vector::selectVectorValues(rateVector, MS.states, exitRates);
293 for (uint_fast64_t row = 0; row < rateVector.size(); ++row) {
294 VT const eToMinusRateTimesDelta = std::exp(-rateVector[row] * digitizationConstant);
295 for (auto& entry : MS.toMS.getRow(row)) {
296 entry.setValue((storm::utility::one<VT>() - eToMinusRateTimesDelta) * entry.getValue());
297 if (entry.getColumn() == row) {
298 entry.setValue(entry.getValue() + eToMinusRateTimesDelta);
299 }
300 }
301 for (auto& entry : MS.toPS.getRow(row)) {
302 entry.setValue((storm::utility::one<VT>() - eToMinusRateTimesDelta) * entry.getValue());
303 }
304 MS.weightedRewardVector[row] *= storm::utility::one<VT>() - eToMinusRateTimesDelta;
305 for (auto& objVector : MS.objectiveRewardVectors) {
306 objVector[row] *= storm::utility::one<VT>() - eToMinusRateTimesDelta;
307 }
308 }
309}
310
311template<class SparseMaModelType>
312template<typename VT, typename std::enable_if<!storm::NumberTraits<VT>::SupportsExponential, int>::type>
313void StandardMaPcaaWeightVectorChecker<SparseMaModelType>::digitize(SubModel& /*subModel*/, VT const& /*digitizationConstant*/) const {
314 STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Computing bounded probabilities of MAs is unsupported for this value type.");
315}
316
317template<class SparseMaModelType>
318template<typename VT, typename std::enable_if<storm::NumberTraits<VT>::SupportsExponential, int>::type>
319void StandardMaPcaaWeightVectorChecker<SparseMaModelType>::digitizeTimeBounds(TimeBoundMap& upperTimeBounds, VT const& digitizationConstant) {
320 VT const maxRate = storm::utility::vector::max_if(exitRates, markovianStates);
321 for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
322 auto const& obj = this->objectives[objIndex];
323 VT errorTowardsZero = storm::utility::zero<VT>();
324 VT errorAwayFromZero = storm::utility::zero<VT>();
325 if (obj.formula->getSubformula().isCumulativeRewardFormula()) {
326 VT timeBound = obj.formula->getSubformula().asCumulativeRewardFormula().template getBound<VT>();
327 uint_fast64_t digitizedBound = storm::utility::convertNumber<uint_fast64_t>(timeBound / digitizationConstant);
328 auto timeBoundIt = upperTimeBounds.insert(std::make_pair(digitizedBound, storm::storage::BitVector(this->objectives.size(), false))).first;
329 timeBoundIt->second.set(objIndex);
330 VT digitizationError = storm::utility::one<VT>();
331 digitizationError -=
332 std::exp(-maxRate * timeBound) * storm::utility::pow(storm::utility::one<VT>() + maxRate * digitizationConstant, digitizedBound);
333 errorAwayFromZero += digitizationError;
334 }
335 if (storm::solver::maximize(obj.formula->getOptimalityType())) {
336 this->offsetsToUnderApproximation[objIndex] = -errorTowardsZero;
337 this->offsetsToOverApproximation[objIndex] = errorAwayFromZero;
338 } else {
339 this->offsetsToUnderApproximation[objIndex] = errorAwayFromZero;
340 this->offsetsToOverApproximation[objIndex] = -errorTowardsZero;
341 }
342 }
343}
344
345template<class SparseMaModelType>
346template<typename VT, typename std::enable_if<!storm::NumberTraits<VT>::SupportsExponential, int>::type>
347void StandardMaPcaaWeightVectorChecker<SparseMaModelType>::digitizeTimeBounds(TimeBoundMap& /*upperTimeBounds*/, VT const& /*digitizationConstant*/) {
348 STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Computing bounded probabilities of MAs is unsupported for this value type.");
349}
350
351template<class SparseMaModelType>
352std::unique_ptr<typename StandardMaPcaaWeightVectorChecker<SparseMaModelType>::MinMaxSolverData>
353StandardMaPcaaWeightVectorChecker<SparseMaModelType>::initMinMaxSolver(Environment const& env, SubModel const& PS, bool acyclic,
354 std::vector<ValueType> const& weightVector) const {
355 std::unique_ptr<MinMaxSolverData> result(new MinMaxSolverData());
356 result->env = std::make_unique<storm::Environment>(env);
357 // For acyclic models we switch to the more efficient acyclic solver (Unless the solver / method was explicitly specified)
358 if (acyclic) {
359 result->env->solver().minMax().setMethod(storm::solver::MinMaxMethod::Acyclic);
360 }
362 result->solver = minMaxSolverFactory.create(*result->env, PS.toPS);
363 result->solver->setHasUniqueSolution(true);
364 result->solver->setHasNoEndComponents(true); // Non-zeno MA
365 result->solver->setTrackScheduler(true);
366 result->solver->setCachingEnabled(true);
367 auto req = result->solver->getRequirements(*result->env, storm::solver::OptimizationDirection::Maximize, false);
368 boost::optional<ValueType> lowerBound = this->computeWeightedResultBound(true, weightVector, storm::storage::BitVector(weightVector.size(), true));
369 if (lowerBound) {
370 result->solver->setLowerBound(lowerBound.get());
371 req.clearLowerBounds();
372 }
373 boost::optional<ValueType> upperBound = this->computeWeightedResultBound(false, weightVector, storm::storage::BitVector(weightVector.size(), true));
374 if (upperBound) {
375 result->solver->setUpperBound(upperBound.get());
376 req.clearUpperBounds();
377 }
378 if (acyclic) {
379 req.clearAcyclic();
380 }
381 STORM_LOG_THROW(!req.hasEnabledCriticalRequirement(), storm::exceptions::UncheckedRequirementException,
382 "Solver requirements " + req.getEnabledRequirementsAsString() + " not checked.");
383 result->solver->setRequirementsChecked(true);
384 result->solver->setOptimizationDirection(storm::solver::OptimizationDirection::Maximize);
385
386 result->b.resize(PS.getNumberOfChoices());
387
388 return result;
389}
390
391template<class SparseMaModelType>
392template<typename VT, typename std::enable_if<storm::NumberTraits<VT>::SupportsExponential, int>::type>
393std::unique_ptr<typename StandardMaPcaaWeightVectorChecker<SparseMaModelType>::LinEqSolverData>
395 std::unique_ptr<LinEqSolverData> result(new LinEqSolverData());
396 result->env = std::make_unique<Environment>(env);
397 result->acyclic = acyclic;
398 // For acyclic models we switch to the more efficient acyclic solver (Unless the solver / method was explicitly specified)
399 if (acyclic) {
400 result->env->solver().setLinearEquationSolverType(storm::solver::EquationSolverType::Acyclic);
401 }
402 result->factory = std::make_unique<storm::solver::GeneralLinearEquationSolverFactory<ValueType>>();
403 result->b.resize(PS.getNumberOfStates());
404 return result;
405}
406
407template<class SparseMaModelType>
408template<typename VT, typename std::enable_if<!storm::NumberTraits<VT>::SupportsExponential, int>::type>
409std::unique_ptr<typename StandardMaPcaaWeightVectorChecker<SparseMaModelType>::LinEqSolverData>
410StandardMaPcaaWeightVectorChecker<SparseMaModelType>::initLinEqSolver(Environment const& /*env*/, SubModel const& /*PS*/, bool /*acyclic*/) const {
411 STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Computing bounded probabilities of MAs is unsupported for this value type.");
412}
413
414template<class SparseMaModelType>
415void StandardMaPcaaWeightVectorChecker<SparseMaModelType>::updateDataToCurrentEpoch(
416 SubModel& MS, SubModel& PS, MinMaxSolverData& minMax, storm::storage::BitVector& consideredObjectives, uint_fast64_t const& currentEpoch,
417 std::vector<ValueType> const& weightVector, TimeBoundMap::iterator& upperTimeBoundIt, TimeBoundMap const& upperTimeBounds) {
418 if (upperTimeBoundIt != upperTimeBounds.end() && currentEpoch == upperTimeBoundIt->first) {
419 consideredObjectives |= upperTimeBoundIt->second;
420 for (auto objIndex : upperTimeBoundIt->second) {
421 // This objective now plays a role in the weighted sum
422 ValueType factor =
423 storm::solver::minimize(this->objectives[objIndex].formula->getOptimalityType()) ? -weightVector[objIndex] : weightVector[objIndex];
424 storm::utility::vector::addScaledVector(MS.weightedRewardVector, MS.objectiveRewardVectors[objIndex], factor);
425 storm::utility::vector::addScaledVector(PS.weightedRewardVector, PS.objectiveRewardVectors[objIndex], factor);
426 }
427 ++upperTimeBoundIt;
428 }
429
430 // Update the solver data
431 PS.toMS.multiplyWithVector(MS.weightedSolutionVector, minMax.b);
432 storm::utility::vector::addVectors(minMax.b, PS.weightedRewardVector, minMax.b);
433}
434
435template<class SparseMaModelType>
436void StandardMaPcaaWeightVectorChecker<SparseMaModelType>::performPSStep(Environment const& env, SubModel& PS, SubModel const& MS, MinMaxSolverData& minMax,
437 LinEqSolverData& linEq, std::vector<uint_fast64_t>& optimalChoicesAtCurrentEpoch,
438 storm::storage::BitVector const& consideredObjectives,
439 std::vector<ValueType> const& weightVector) const {
440 // compute a choice vector for the probabilistic states that is optimal w.r.t. the weighted reward vector
441 minMax.solver->solveEquations(*minMax.env, PS.weightedSolutionVector, minMax.b);
442 auto const& newChoices = minMax.solver->getSchedulerChoices();
443 if (consideredObjectives.getNumberOfSetBits() == 1 && storm::utility::isOne(weightVector[*consideredObjectives.begin()])) {
444 // In this case there is no need to perform the computation on the individual objectives
445 optimalChoicesAtCurrentEpoch = newChoices;
446 PS.objectiveSolutionVectors[*consideredObjectives.begin()] = PS.weightedSolutionVector;
447 if (storm::solver::minimize(this->objectives[*consideredObjectives.begin()].formula->getOptimalityType())) {
448 storm::utility::vector::scaleVectorInPlace(PS.objectiveSolutionVectors[*consideredObjectives.begin()], -storm::utility::one<ValueType>());
449 }
450 } else {
451 // check whether the linEqSolver needs to be updated, i.e., whether the scheduler has changed
452 if (linEq.solver == nullptr || newChoices != optimalChoicesAtCurrentEpoch) {
453 optimalChoicesAtCurrentEpoch = newChoices;
454 linEq.solver = nullptr;
455 bool needEquationSystem = linEq.factory->getEquationProblemFormat(*linEq.env) == storm::solver::LinearEquationSolverProblemFormat::EquationSystem;
456 storm::storage::SparseMatrix<ValueType> linEqMatrix = PS.toPS.selectRowsFromRowGroups(optimalChoicesAtCurrentEpoch, needEquationSystem);
457 if (needEquationSystem) {
458 linEqMatrix.convertToEquationSystem();
459 }
460 linEq.solver = linEq.factory->create(*linEq.env, std::move(linEqMatrix));
461 linEq.solver->setCachingEnabled(true);
462 auto req = linEq.solver->getRequirements(*linEq.env);
463 if (linEq.acyclic) {
464 req.clearAcyclic();
465 }
466 STORM_LOG_THROW(!req.hasEnabledCriticalRequirement(), storm::exceptions::UncheckedRequirementException,
467 "Solver requirements " + req.getEnabledRequirementsAsString() + " not checked.");
468 }
469
470 // Get the results for the individual objectives.
471 // Note that we do not consider an estimate for each objective (as done in the unbounded phase) since the results from the previous epoch are already
472 // pretty close
473 for (auto objIndex : consideredObjectives) {
474 auto const& objectiveRewardVectorPS = PS.objectiveRewardVectors[objIndex];
475 auto const& objectiveSolutionVectorMS = MS.objectiveSolutionVectors[objIndex];
476 // compute rhs of equation system, i.e., PS.toMS * x + Rewards
477 // To safe some time, only do this for the obtained optimal choices
478 auto itGroupIndex = PS.toPS.getRowGroupIndices().begin();
479 auto itChoiceOffset = optimalChoicesAtCurrentEpoch.begin();
480 for (auto& bValue : linEq.b) {
481 uint_fast64_t row = (*itGroupIndex) + (*itChoiceOffset);
482 bValue = objectiveRewardVectorPS[row];
483 for (auto const& entry : PS.toMS.getRow(row)) {
484 bValue += entry.getValue() * objectiveSolutionVectorMS[entry.getColumn()];
485 }
486 ++itGroupIndex;
487 ++itChoiceOffset;
488 }
489 linEq.solver->solveEquations(*linEq.env, PS.objectiveSolutionVectors[objIndex], linEq.b);
490 }
491 }
492}
493
494template<class SparseMaModelType>
495void StandardMaPcaaWeightVectorChecker<SparseMaModelType>::performMSStep(Environment const& env, SubModel& MS, SubModel const& PS,
496 storm::storage::BitVector const& consideredObjectives,
497 std::vector<ValueType> const& weightVector) const {
498 MS.toMS.multiplyWithVector(MS.weightedSolutionVector, MS.auxChoiceValues);
499 storm::utility::vector::addVectors(MS.weightedRewardVector, MS.auxChoiceValues, MS.weightedSolutionVector);
500 MS.toPS.multiplyWithVector(PS.weightedSolutionVector, MS.auxChoiceValues);
501 storm::utility::vector::addVectors(MS.weightedSolutionVector, MS.auxChoiceValues, MS.weightedSolutionVector);
502 if (consideredObjectives.getNumberOfSetBits() == 1 && storm::utility::isOne(weightVector[*consideredObjectives.begin()])) {
503 // In this case there is no need to perform the computation on the individual objectives
504 MS.objectiveSolutionVectors[*consideredObjectives.begin()] = MS.weightedSolutionVector;
505 if (storm::solver::minimize(this->objectives[*consideredObjectives.begin()].formula->getOptimalityType())) {
506 storm::utility::vector::scaleVectorInPlace(MS.objectiveSolutionVectors[*consideredObjectives.begin()], -storm::utility::one<ValueType>());
507 }
508 } else {
509 for (auto objIndex : consideredObjectives) {
510 MS.toMS.multiplyWithVector(MS.objectiveSolutionVectors[objIndex], MS.auxChoiceValues);
511 storm::utility::vector::addVectors(MS.objectiveRewardVectors[objIndex], MS.auxChoiceValues, MS.objectiveSolutionVectors[objIndex]);
512 MS.toPS.multiplyWithVector(PS.objectiveSolutionVectors[objIndex], MS.auxChoiceValues);
513 storm::utility::vector::addVectors(MS.objectiveSolutionVectors[objIndex], MS.auxChoiceValues, MS.objectiveSolutionVectors[objIndex]);
514 }
515 }
516}
517
518template class StandardMaPcaaWeightVectorChecker<storm::models::sparse::MarkovAutomaton<double>>;
519template double StandardMaPcaaWeightVectorChecker<storm::models::sparse::MarkovAutomaton<double>>::getDigitizationConstant<double>(
520 std::vector<double> const& direction) const;
521template void StandardMaPcaaWeightVectorChecker<storm::models::sparse::MarkovAutomaton<double>>::digitize<double>(
522 StandardMaPcaaWeightVectorChecker<storm::models::sparse::MarkovAutomaton<double>>::SubModel& subModel, double const& digitizationConstant) const;
523template void StandardMaPcaaWeightVectorChecker<storm::models::sparse::MarkovAutomaton<double>>::digitizeTimeBounds<double>(
524 StandardMaPcaaWeightVectorChecker<storm::models::sparse::MarkovAutomaton<double>>::TimeBoundMap& upperTimeBounds, double const& digitizationConstant);
525template std::unique_ptr<typename StandardMaPcaaWeightVectorChecker<storm::models::sparse::MarkovAutomaton<double>>::LinEqSolverData>
526StandardMaPcaaWeightVectorChecker<storm::models::sparse::MarkovAutomaton<double>>::initLinEqSolver<double>(
527 Environment const& env, StandardMaPcaaWeightVectorChecker<storm::models::sparse::MarkovAutomaton<double>>::SubModel const& PS, bool acyclic) const;
528#ifdef STORM_HAVE_CARL
529template class StandardMaPcaaWeightVectorChecker<storm::models::sparse::MarkovAutomaton<storm::RationalNumber>>;
530template storm::RationalNumber StandardMaPcaaWeightVectorChecker<storm::models::sparse::MarkovAutomaton<storm::RationalNumber>>::getDigitizationConstant<
531 storm::RationalNumber>(std::vector<storm::RationalNumber> const& direction) const;
532template void StandardMaPcaaWeightVectorChecker<storm::models::sparse::MarkovAutomaton<storm::RationalNumber>>::digitize<storm::RationalNumber>(
533 StandardMaPcaaWeightVectorChecker<storm::models::sparse::MarkovAutomaton<storm::RationalNumber>>::SubModel& subModel,
534 storm::RationalNumber const& digitizationConstant) const;
535template void StandardMaPcaaWeightVectorChecker<storm::models::sparse::MarkovAutomaton<storm::RationalNumber>>::digitizeTimeBounds<storm::RationalNumber>(
536 StandardMaPcaaWeightVectorChecker<storm::models::sparse::MarkovAutomaton<storm::RationalNumber>>::TimeBoundMap& upperTimeBounds,
537 storm::RationalNumber const& digitizationConstant);
538template std::unique_ptr<typename StandardMaPcaaWeightVectorChecker<storm::models::sparse::MarkovAutomaton<storm::RationalNumber>>::LinEqSolverData>
539StandardMaPcaaWeightVectorChecker<storm::models::sparse::MarkovAutomaton<storm::RationalNumber>>::initLinEqSolver<storm::RationalNumber>(
540 Environment const& env, StandardMaPcaaWeightVectorChecker<storm::models::sparse::MarkovAutomaton<storm::RationalNumber>>::SubModel const& PS,
541 bool acyclic) const;
542#endif
543
544} // namespace multiobjective
545} // namespace modelchecker
546} // namespace storm
Helper class for model checking queries that depend on the long run behavior of the (nondeterministic...
Helper Class that takes preprocessed Pcaa data and a weight vector and ...
StandardMaPcaaWeightVectorChecker(preprocessing::SparseMultiObjectivePreprocessorResult< SparseMaModelType > const &preprocessorResult)
virtual void initializeModelTypeSpecificData(SparseMaModelType const &model) override
virtual storm::modelchecker::helper::SparseNondeterministicInfiniteHorizonHelper< ValueType > createNondetInfiniteHorizonHelper(storm::storage::SparseMatrix< ValueType > const &transitions) const override
virtual storm::modelchecker::helper::SparseNondeterministicInfiniteHorizonHelper< ValueType > createDetInfiniteHorizonHelper(storm::storage::SparseMatrix< ValueType > const &transitions) const override
Helper Class that takes preprocessed Pcaa data and a weight vector and ...
void initialize(preprocessing::SparseMultiObjectivePreprocessorResult< SparseMaModelType > const &preprocessorResult)
This class represents a Markov automaton.
virtual std::unique_ptr< MinMaxLinearEquationSolver< ValueType, SolutionType > > create(Environment const &env) const override
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:18
const_iterator begin() const
Returns an iterator to the indices of the set bits in the bit vector.
uint_fast64_t getNumberOfSetBits() const
Returns the number of bits that are set to true in this bit vector.
bool get(uint_fast64_t index) const
Retrieves the truth value of the bit at the given index and performs a bound check.
A class that holds a possibly non-square matrix in the compressed row storage format.
void convertToEquationSystem()
Transforms the matrix into an equation system.
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.
index_type getRowGroupCount() const
Returns the number of row groups in the matrix.
#define STORM_LOG_DEBUG(message)
Definition logging.h:23
#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
SettingsType const & getModule()
Get module.
bool constexpr maximize(OptimizationDirection d)
bool constexpr minimize(OptimizationDirection d)
bool hasCycle(storm::storage::SparseMatrix< T > const &transitionMatrix, boost::optional< storm::storage::BitVector > const &subsystem)
Returns true if the graph represented by the given matrix has a cycle.
Definition graph.cpp:143
bool isTerminate()
Check whether the program should terminate (due to some abort signal).
void addVectors(std::vector< InValueType1 > const &firstOperand, std::vector< InValueType2 > const &secondOperand, std::vector< OutValueType > &target)
Adds the two given vectors and writes the result to the target vector.
Definition vector.h:443
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:517
VT max_if(std::vector< VT > const &values, storm::storage::BitVector const &filter)
Computes the maximum of the entries from the values that are selected by the (non-empty) filter.
Definition vector.h:612
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:83
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:189
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:504
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:491
bool isOne(ValueType const &a)
Definition constants.cpp:36
bool isZero(ValueType const &a)
Definition constants.cpp:41
ValueType pow(ValueType const &value, int_fast64_t exponent)
ValueType sqrt(ValueType const &number)
LabParser.cpp.
Definition cli.cpp:18