Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
RobustParameterLifter.cpp
Go to the documentation of this file.
2
3#include <carl/core/rootfinder/RootFinder.h>
4#include <algorithm>
5#include <cmath>
6#include <map>
7#include <memory>
8#include <optional>
9#include <set>
10#include <vector>
11
12#include <boost/functional/hash.hpp>
13
28
29std::unordered_map<storm::RationalFunction, storm::transformer::Annotation> storm::transformer::BigStep::lastSavedAnnotations;
30
31namespace storm {
32namespace transformer {
33
35
36template<typename ParametricType, typename ConstantType>
38 std::vector<ParametricType> const& pVector,
39 storm::storage::BitVector const& selectedRows,
40 storm::storage::BitVector const& selectedColumns, bool generateRowLabels,
41 bool useMonotonicity) {
42 oldToNewColumnIndexMapping = std::vector<uint64_t>(selectedColumns.size(), selectedColumns.size());
43 uint64_t newIndexColumns = 0;
44 for (uint64_t oldColumn : selectedColumns) {
45 oldToNewColumnIndexMapping[oldColumn] = newIndexColumns++;
46 }
47
48 oldToNewRowIndexMapping = std::vector<uint64_t>(selectedRows.size(), selectedRows.size());
49 uint64_t newIndexRows = 0;
50 for (uint64_t oldRow : selectedRows) {
51 oldToNewRowIndexMapping[oldRow] = newIndexRows++;
52 }
53
54 // Stores which entries of the original matrix/vector are non-constant. Entries for non-selected rows/columns are omitted
55 auto nonConstMatrixEntries = storm::storage::BitVector(pMatrix.getEntryCount(), false); // this vector has to be resized later
56 auto nonConstVectorEntries = storm::storage::BitVector(selectedRows.getNumberOfSetBits(), false);
57 // Counters for selected entries in the pMatrix and the pVector
58 uint64_t pMatrixEntryCount = 0;
59 uint64_t pVectorEntryCount = 0;
60
61 // The matrix builder for the new matrix. The correct number of rows and entries is not known yet.
62 storm::storage::SparseMatrixBuilder<Interval> builder(newIndexRows, newIndexColumns, 0, true, false);
63
64 this->occurringVariablesAtState.resize(pMatrix.getRowCount());
65
66 for (uint64_t row = 0; row < pMatrix.getRowCount(); row++) {
67 if (!selectedRows.get(row)) {
68 continue;
69 }
70 std::set<VariableType> occurringVariables;
71 for (auto const& entry : pMatrix.getRow(row)) {
72 auto column = entry.getColumn();
73 if (!selectedColumns.get(column)) {
74 continue;
75 }
76
77 auto transition = entry.getValue();
78
79 auto variables = transition.gatherVariables();
80 occurringVariables.insert(variables.begin(), variables.end());
81
82 if (storm::utility::isConstant(transition)) {
83 builder.addNextValue(oldToNewColumnIndexMapping[row], oldToNewColumnIndexMapping[column], utility::convertNumber<double>(transition));
84 } else {
85 nonConstMatrixEntries.set(pMatrixEntryCount, true);
86 auto valuation = RobustAbstractValuation(transition);
87 builder.addNextValue(oldToNewColumnIndexMapping[row], oldToNewColumnIndexMapping[column], Interval());
88 Interval& placeholder = functionValuationCollector.add(valuation);
89 matrixAssignment.push_back(std::pair<typename storm::storage::SparseMatrix<Interval>::iterator, Interval&>(
91 }
92 pMatrixEntryCount++;
93 }
94
95 // Save the occuringVariables of a state, needed if we want to use monotonicity
96 for (auto& var : occurringVariables) {
97 occuringStatesAtVariable[var].insert(row);
98 }
99 occurringVariablesAtState[row] = std::move(occurringVariables);
100 }
101
102 for (uint64_t i = 0; i < pVector.size(); i++) {
103 auto const transition = pVector[i];
104 if (!selectedRows.get(i)) {
105 continue;
106 }
107 if (storm::utility::isConstant(transition)) {
108 vector.push_back(utility::convertNumber<double>(transition));
109 } else {
110 nonConstVectorEntries.set(pVectorEntryCount, true);
111 auto valuation = RobustAbstractValuation(transition);
112 vector.push_back(Interval());
113 Interval& placeholder = functionValuationCollector.add(valuation);
114 vectorAssignment.push_back(std::pair<typename std::vector<Interval>::iterator, Interval&>(typename std::vector<Interval>::iterator(), placeholder));
115 for (auto const& var : valuation.getParameters()) {
116 occuringStatesAtVariable[var].insert(i);
117 occurringVariablesAtState[i].emplace(var);
118 }
119 }
120 pVectorEntryCount++;
121 }
122
123 matrix = builder.build();
124 vector.shrink_to_fit();
125 matrixAssignment.shrink_to_fit();
126 vectorAssignment.shrink_to_fit();
127 nonConstMatrixEntries.resize(pMatrixEntryCount);
128
129 // Now insert the correct iterators for the matrix and vector assignment
130 auto matrixAssignmentIt = matrixAssignment.begin();
131 uint64_t startEntryOfRow = 0;
132 for (uint64_t group = 0; group < matrix.getRowGroupCount(); ++group) {
133 uint64_t startEntryOfNextRow = startEntryOfRow + matrix.getRow(group, 0).getNumberOfEntries();
134 for (uint64_t matrixRow = matrix.getRowGroupIndices()[group]; matrixRow < matrix.getRowGroupIndices()[group + 1]; ++matrixRow) {
135 auto matrixEntryIt = matrix.getRow(matrixRow).begin();
136 for (uint64_t nonConstEntryIndex = nonConstMatrixEntries.getNextSetIndex(startEntryOfRow); nonConstEntryIndex < startEntryOfNextRow;
137 nonConstEntryIndex = nonConstMatrixEntries.getNextSetIndex(nonConstEntryIndex + 1)) {
138 matrixAssignmentIt->first = matrixEntryIt + (nonConstEntryIndex - startEntryOfRow);
139 ++matrixAssignmentIt;
140 }
141 }
142 startEntryOfRow = startEntryOfNextRow;
143 }
144 STORM_LOG_ASSERT(matrixAssignmentIt == matrixAssignment.end(), "Unexpected number of entries in the matrix assignment.");
145
146 auto vectorAssignmentIt = vectorAssignment.begin();
147 for (uint64_t nonConstVectorEntry : nonConstVectorEntries) {
148 for (uint64_t vectorIndex = matrix.getRowGroupIndices()[nonConstVectorEntry]; vectorIndex != matrix.getRowGroupIndices()[nonConstVectorEntry + 1];
149 ++vectorIndex) {
150 vectorAssignmentIt->first = vector.begin() + vectorIndex;
151 ++vectorAssignmentIt;
152 }
153 }
154 STORM_LOG_ASSERT(vectorAssignmentIt == vectorAssignment.end(), "Unexpected number of entries in the vector assignment.");
155}
156
157template<typename ParametricType, typename ConstantType>
159 storm::solver::OptimizationDirection const& dirForParameters) {
160 // write the evaluation result of each function,evaluation pair into the placeholders
161 this->currentRegionAllIllDefined = functionValuationCollector.evaluateCollectedFunctions(region, dirForParameters);
162
163 // TODO Return if currentRegionAllIllDefined? Or write to matrix?
164
165 // apply the matrix and vector assignments to write the contents of the placeholder into the matrix/vector
166 for (auto& assignment : matrixAssignment) {
167 assignment.first->setValue(assignment.second);
168 }
169
170 for (auto& assignment : vectorAssignment) {
171 *assignment.first = assignment.second;
172 }
173}
174
175template<typename ParametricType, typename ConstantType>
176const std::vector<std::set<typename RobustParameterLifter<ParametricType, ConstantType>::VariableType>>&
180
181template<typename ParametricType, typename ConstantType>
182std::map<typename RobustParameterLifter<ParametricType, ConstantType>::VariableType, std::set<uint_fast64_t>> const&
186
187template<typename ParametricType, typename ConstantType>
188std::optional<std::set<typename storm::utility::parametric::CoefficientType<ParametricType>::type>>
189RobustParameterLifter<ParametricType, ConstantType>::RobustAbstractValuation::zeroesSMT(
191 std::shared_ptr<storm::expressions::ExpressionManager> expressionManager = std::make_shared<storm::expressions::ExpressionManager>();
192
194 auto smtSolver = factory.create(*expressionManager);
195
197
198 auto expression = rfte.toExpression(function) == expressionManager->rational(0);
199
200 auto variables = expressionManager->getVariables();
201 // Sum the summands together directly in the expression so we pass this info to the solver
202 expressions::Expression exprBounds = expressionManager->boolean(true);
203 for (auto const& var : variables) {
204 exprBounds = exprBounds && expressionManager->rational(0) <= var && var <= expressionManager->rational(1);
205 }
206
207 smtSolver->setTimeout(50);
208
209 smtSolver->add(exprBounds);
210 smtSolver->add(expression);
211
212 std::set<CoefficientType> zeroes = {};
213
214 while (true) {
215 auto checkResult = smtSolver->check();
216
217 if (checkResult == solver::SmtSolver::CheckResult::Sat) {
218 auto model = smtSolver->getModel();
219
220 STORM_LOG_ERROR_COND(variables.size() == 1, "Should be one variable.");
221 if (variables.size() != 1) {
222 return {};
223 }
224 auto const var = *variables.begin();
225
226 double value = model->getRationalValue(var);
227
228 zeroes.emplace(utility::convertNumber<CoefficientType>(value));
229
230 // Add new constraint so we search for the next zero in the polynomial
231 // Get another model (or unsat)
232 // For some reason, this only really works when we then make a new
233 smtSolver->addNotCurrentModel();
234 } else if (checkResult == solver::SmtSolver::CheckResult::Unknown) {
235 return std::nullopt;
236 break;
237 } else {
238 // Unsat => found all zeroes :)
239 break;
240 }
241 }
242 return zeroes;
243}
244
245template<typename ParametricType, typename ConstantType>
246std::optional<std::set<typename storm::utility::parametric::CoefficientType<ParametricType>::type>>
250 auto const& carlRoots = carl::rootfinder::realRoots<CoefficientType, CoefficientType>(
251 polynomial, carl::Interval<CoefficientType>(utility::zero<CoefficientType>(), utility::one<CoefficientType>()),
252 carl::rootfinder::SplittingStrategy::ABERTH);
253 std::set<CoefficientType> zeroes = {};
254 for (carl::RealAlgebraicNumber<CoefficientType> const& root : carlRoots) {
255 CoefficientType rootCoefficient;
256 if (root.isNumeric()) {
257 rootCoefficient = CoefficientType(root.value());
258 } else {
259 rootCoefficient = CoefficientType((root.upper() + root.lower()) / 2);
260 }
261 zeroes.emplace(rootCoefficient);
262 }
263 return zeroes;
264}
265
266template<typename ParametricType, typename ConstantType>
267std::set<typename storm::utility::parametric::CoefficientType<ParametricType>::type>
270 if (polynomial.isConstant()) {
271 return {};
272 }
273 STORM_LOG_ERROR_COND(polynomial.gatherVariables().size() == 1, "Multi-variate polynomials currently not supported");
274 // Polynomial is a*p^3 + b*p^2 + c*p + d
275
276 // Recover factors from polynomial
277 CoefficientType a = utility::zero<CoefficientType>(), b = a, c = a, d = a;
279 for (auto const& term : polynomial.getTerms()) {
280 STORM_LOG_ASSERT(term.getNrVariables() <= 1, "No terms with more than one variable allowed but " << term << " has " << term.getNrVariables());
281 if (!term.isConstant() && term.getSingleVariable() != parameter) {
282 continue;
283 }
284 CoefficientType coefficient = term.coeff();
285 switch (term.tdeg()) {
286 case 0:
287 d = coefficient;
288 break;
289 case 1:
290 c = coefficient;
291 break;
292 case 2:
293 b = coefficient;
294 break;
295 case 3:
296 a = coefficient;
297 break;
298 default:
299 STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Transitions are only allowed to have have a maximum degree of four.");
300 break;
301 }
302 }
303 // Translated from https://stackoverflow.com/questions/27176423/function-to-solve-cubic-equation-analytically
304
305 // Quadratic case
306 if (utility::isZero(a)) {
307 a = b;
308 b = c;
309 c = d;
310 // Linear case
311 if (utility::isZero(a)) {
312 a = b;
313 b = c;
314 // Constant case
315 if (utility::isZero(a)) {
316 return {};
317 }
318 return {-b / a};
319 }
320
321 CoefficientType D = b * b - 4 * a * c;
322 if (utility::isZero(D)) {
323 return {-b / (2 * a)};
324 } else if (D > 0) {
325 return {(-b + utility::sqrt(D)) / (2 * a), (-b - utility::sqrt(D)) / (2 * a)};
326 }
327 return {};
328 }
329 std::set<CoefficientType> roots;
330
331 // Convert to depressed cubic t^3+pt+q = 0 (subst x = t - b/3a)
332 CoefficientType p = (3 * a * c - b * b) / (3 * a * a);
333 CoefficientType q = (2 * b * b * b - 9 * a * b * c + 27 * a * a * d) / (27 * a * a * a);
334 double pDouble = utility::convertNumber<ConstantType>(p);
335 double qDouble = utility::convertNumber<ConstantType>(q);
336
337 if (utility::isZero(p)) { // p = 0 -> t^3 = -q -> t = -q^1/3
338 roots = {utility::convertNumber<CoefficientType>(std::cbrt(-qDouble))};
339 } else if (utility::isZero(q)) { // q = 0 -> t^3 + pt = 0 -> t(t^2+p)=0
340 roots = {0};
341 if (p < 0) {
344 }
345 } else {
346 // These are all coefficients (we also plug the values into RationalFunctions later), i.e., they are rational numbers,
347 // but some of these operations are strictly real, so we convert to double and back (i.e., approximate).
348 CoefficientType D = q * q / 4 + p * p * p / 27;
349 if (utility::isZero(D)) { // D = 0 -> two roots
350 roots = {-3 * q / (p * 2), 3 * q / p};
351 } else if (D > 0) { // Only one real root
352 double Ddouble = utility::convertNumber<ConstantType>(D);
353 CoefficientType u = utility::convertNumber<CoefficientType>(std::cbrt(-qDouble / 2 - utility::sqrt(Ddouble)));
354 roots = {u - p / (3 * u)};
355 } else { // D < 0, three roots, but needs to use complex numbers/trigonometric solution
356 double u = 2 * utility::sqrt(-pDouble / 3);
357 double t = std::acos(3 * qDouble / pDouble / u) / 3; // D < 0 implies p < 0 and acos argument in [-1..1]
358 double k = 2 * M_PI / 3;
359
360 roots = {utility::convertNumber<CoefficientType>(u * std::cos(t)), utility::convertNumber<CoefficientType>(u * std::cos(t - k)),
361 utility::convertNumber<CoefficientType>(u * std::cos(t - 2 * k))};
362 }
363 }
364
365 return roots;
366}
367
368template<typename ParametricType, typename ConstantType>
370 : transition(transition) {
371 STORM_LOG_ERROR_COND(transition.denominator().isConstant(), "Robust PLA only supports transitions with constant denominators.");
372 transition.simplify();
373 std::set<VariableType> occurringVariables;
374 storm::utility::parametric::gatherOccurringVariables(transition, occurringVariables);
375 for (auto const& var : occurringVariables) {
376 parameters.emplace(var);
377 }
378}
379
380template<typename ParametricType, typename ConstantType>
384
385template<typename ParametricType, typename ConstantType>
387 return vector;
388}
389
390template<typename ParametricType, typename ConstantType>
392 return currentRegionAllIllDefined;
393}
394
395template<typename ParametricType, typename ConstantType>
397 return this->transition == other.transition;
398}
399
400template<typename ParametricType, typename ConstantType>
401std::set<typename RobustParameterLifter<ParametricType, ConstantType>::VariableType> const&
405
406template<typename ParametricType, typename ConstantType>
410
411template<typename ParametricType, typename ConstantType>
413 // TODO This function is a mess
414 if (this->extrema || this->annotation) {
415 // Extrema already initialized
416 return;
417 }
418
419 if (BigStep::lastSavedAnnotations.count(transition)) {
420 auto& annotation = BigStep::lastSavedAnnotations.at(transition);
421
422 auto const& terms = annotation.getTerms();
423
424 // Try to find all zeroes of all derivatives with the SMT solver.
425 // TODO: Are we even using that this is a sum of terms?
426
427 std::optional<std::set<CoefficientType>> carlResult;
428
429 if (terms.size() < 5) {
430 carlResult = zeroesCarl(annotation.getProbability().derivative(), annotation.getParameter());
431 }
432
433 if (carlResult) {
434 // Hooray, we found the zeroes with the SMT solver / CARL
435 this->extrema = std::map<VariableType, std::set<CoefficientType>>();
436 (*this->extrema)[annotation.getParameter()];
437 for (auto const& root : *carlResult) {
438 (*this->extrema).at(annotation.getParameter()).emplace(utility::convertNumber<CoefficientType>(root));
439 }
440 } else {
441 // TODO make evaluation depth configurable
442 annotation.computeDerivative(4);
443 }
444 this->annotation.emplace(annotation);
445 } else {
446 this->extrema = std::map<VariableType, std::set<CoefficientType>>();
447
448 for (auto const& p : transition.gatherVariables()) {
449 (*this->extrema)[p] = {};
450
451 auto const& derivative = transition.derivative(p);
452
453 if (derivative.isConstant()) {
454 continue;
455 }
456
457 // There is no annotation for this transition:
458 auto nominatorAsUnivariate = derivative.nominator().toUnivariatePolynomial();
459 // Constant denominator is now distributed in the factors, not in the denominator of the rational function
460 nominatorAsUnivariate /= derivative.denominator().coefficient();
461
462 // Compute zeros of derivative (= maxima/minima of function) and emplace those between 0 and 1 into the maxima set
463 std::optional<std::set<CoefficientType>> zeroes;
464 // Find zeroes with straight-forward method for degrees <4, find them with SMT for degrees above that
465 if (derivative.nominator().totalDegree() < 4) {
466 zeroes = cubicEquationZeroes(RawPolynomial(derivative.nominator()), p);
467 } else {
468 zeroes = zeroesSMT(derivative, p);
469 }
470 STORM_LOG_ERROR_COND(zeroes, "Zeroes of " << derivative << " could not be found.");
471 for (auto const& zero : *zeroes) {
473 this->extrema->at(p).emplace(zero);
474 }
475 }
476 }
477 }
478}
479
480template<typename ParametricType, typename ConstantType>
481std::optional<std::map<typename RobustParameterLifter<ParametricType, ConstantType>::VariableType,
482 std::set<typename storm::utility::parametric::CoefficientType<ParametricType>::type>>> const&
486
487template<typename ParametricType, typename ConstantType>
489 return this->annotation;
490}
491
492template<typename ParametricType, typename ConstantType>
494 std::size_t seed = 0;
495 boost::hash_combine(seed, transition);
496 return seed;
497}
498
499template<typename ParametricType, typename ConstantType>
500Interval& RobustParameterLifter<ParametricType, ConstantType>::FunctionValuationCollector::add(RobustAbstractValuation& valuation) {
501 // If no valuation like this is present in the collectedValuations, initialize the extrema
502 if (!collectedValuations.count(valuation)) {
503 valuation.initialize();
504 this->regionsAndBounds.emplace(valuation, std::vector<std::pair<Interval, Interval>>());
505 }
506 // insert the function and the valuation
507 // Note that references to elements of an unordered map remain valid after calling unordered_map::insert.
508 auto insertionRes = collectedValuations.insert(std::pair<RobustAbstractValuation, Interval>(std::move(valuation), storm::Interval(0, 1)));
509 return insertionRes.first->second;
510}
511
512Interval evaluateExtremaAnnotations(std::map<UniPoly, std::set<double>> extremaAnnotations, Interval input) {
513 Interval sumOfTerms(0.0, 0.0);
514 for (auto const& [poly, roots] : extremaAnnotations) {
515 std::set<double> potentialExtrema = {input.lower(), input.upper()};
516 for (auto const& root : roots) {
517 if (root >= input.lower() && root <= input.upper()) {
518 potentialExtrema.emplace(root);
519 }
520 }
521
524
525 for (auto const& potentialExtremum : potentialExtrema) {
526 auto value = utility::convertNumber<double>(poly.evaluate(utility::convertNumber<RationalFunctionCoefficient>(potentialExtremum)));
527 maxValue &= value;
528 minValue &= value;
529 }
530 STORM_LOG_ASSERT(!minValue.empty(), "Expected at least one potential extremum.");
531 sumOfTerms += Interval(*minValue, *maxValue);
532 }
533 return sumOfTerms;
534}
535
536template<typename ParametricType, typename ConstantType>
537bool RobustParameterLifter<ParametricType, ConstantType>::FunctionValuationCollector::evaluateCollectedFunctions(
538 storm::storage::ParameterRegion<ParametricType> const& region, storm::solver::OptimizationDirection const& dirForUnspecifiedParameters) {
539 std::unordered_map<RobustAbstractValuation, Interval, RobustAbstractValuationHash> insertThese;
540 for (auto& [abstrValuation, placeholder] : collectedValuations) {
541 // Results of our computations go here, we use different methods
542 ConstantType lowerBound = utility::zero<ConstantType>();
543 ConstantType upperBound = utility::zero<ConstantType>();
544
545 if (abstrValuation.getExtrema()) {
546 // We know the extrema of this abstract valuation => we can get the exact bounds easily
547
548 // If an annotation exists:
549 // Evaluating the annotation is cheaper than evaluating the RationalFunction, which isn't prime-factorized
550 // If no annotation exists:
551 // The RationalFunction is hopefully prime-factorized
552 auto const& maybeAnnotation = abstrValuation.getAnnotation();
553
554 if (maybeAnnotation) {
555 // We only have one parameter and can evaluate the annotation directly
556 auto p = maybeAnnotation->getParameter();
557
558 CoefficientType lowerP = region.getLowerBoundary(p);
559 CoefficientType upperP = region.getUpperBoundary(p);
560 std::set<CoefficientType> potentialExtrema = {lowerP, upperP};
561 for (auto const& maximum : abstrValuation.getExtrema()->at(p)) {
562 if (maximum >= lowerP && maximum <= upperP) {
563 potentialExtrema.emplace(maximum);
564 }
565 }
566
569 for (auto const& potentialExtremum : potentialExtrema) {
570 // Possible optimization: evaluate all transitions together, keeping track of intermediate results
571 auto value = maybeAnnotation->evaluate(utility::convertNumber<double>(potentialExtremum));
572 maximum &= value;
573 minimum &= value;
574 }
575 STORM_LOG_ASSERT(!minimum.empty(), "Expected at least one potential extremum.");
576 lowerBound = *minimum;
577 upperBound = *maximum;
578 } else {
579 // We may have multiple parameters, but the derivatives w.r.t. each parameter only contain that parameter
580 // We first figure out the positions of the lower and upper bounds per parameter
581 // Lower/upper bound of every parameter is independent because the transitions are sums of terms with one parameter each
582 // At the end, we compute the value
583 std::map<VariableType, CoefficientType> lowerPositions;
584 std::map<VariableType, CoefficientType> upperPositions;
585
586 for (auto const& p : abstrValuation.getParameters()) {
587 CoefficientType lowerP = region.getLowerBoundary(p);
588 CoefficientType upperP = region.getUpperBoundary(p);
589
590 std::set<CoefficientType> potentialExtrema = {lowerP, upperP};
591 for (auto const& maximum : abstrValuation.getExtrema()->at(p)) {
592 if (maximum >= lowerP && maximum <= upperP) {
593 potentialExtrema.emplace(maximum);
594 }
595 }
596
597 CoefficientType minPosP;
598 CoefficientType maxPosP;
601
602 auto instantiation = std::map<VariableType, CoefficientType>(region.getLowerBoundaries());
603
604 for (auto const& potentialExtremum : potentialExtrema) {
605 // We modify the instantiation to have value potentialExtremum at p, keeping other parameters the same
606 instantiation[p] = potentialExtremum;
607 auto value = abstrValuation.getTransition().evaluate(instantiation);
608 if (maxValue &= value) {
609 maxPosP = potentialExtremum;
610 }
611 if (minValue &= value) {
612 minPosP = potentialExtremum;
613 }
614 }
615 STORM_LOG_ASSERT(!minValue.empty(), "Expected at least one potential extremum.");
616
617 lowerPositions[p] = minPosP;
618 upperPositions[p] = maxPosP;
619 }
620
621 // Compute function values at left and right ends
622 lowerBound = utility::convertNumber<ConstantType>(abstrValuation.getTransition().evaluate(lowerPositions));
623 upperBound = utility::convertNumber<ConstantType>(abstrValuation.getTransition().evaluate(upperPositions));
624 }
625
626 if (upperBound < utility::zero<ConstantType>() || lowerBound > utility::one<ConstantType>()) {
627 // Current region is entirely ill-defined (partially ill-defined is fine:)
628 return true;
629 }
630 } else {
631 STORM_LOG_ASSERT(abstrValuation.getAnnotation(), "Needs to have annotation if no zeroes.");
632 auto& regionsAndBounds = this->regionsAndBounds.at(abstrValuation);
633 auto const& annotation = *abstrValuation.getAnnotation();
634
635 auto plaRegion = Interval(region.getLowerBoundary(annotation.getParameter()), region.getUpperBoundary(annotation.getParameter()));
636
637 bool refine = false;
638 do {
639 lowerBound = 1.0;
640 upperBound = 0.0;
641 std::vector<uint64_t> regionsInPLARegion;
642 for (uint64_t i = 0; i < regionsAndBounds.size(); i++) {
643 auto const& [region, bound] = regionsAndBounds[i];
645 i == 0 ? true : (!(region.upper() < regionsAndBounds[i - 1].first.lower() || region.lower() > regionsAndBounds[i - 1].first.upper())),
646 "Regions next to each other need to intersect.");
647 if (region.upper() <= plaRegion.lower() || region.lower() >= plaRegion.upper()) {
648 if (regionsInPLARegion.empty()) {
649 continue;
650 } else {
651 // Regions are sorted => we've walked past the interesting part
652 break;
653 }
654 }
655 lowerBound = utility::min(lowerBound, bound.lower());
656 upperBound = utility::max(upperBound, bound.upper());
657 regionsInPLARegion.push_back(i);
658 }
659
660 // TODO make this configurable
661 uint64_t regionsRefine = std::max((uint64_t)10, annotation.maxDegree());
662 refine = regionsInPLARegion.size() < regionsRefine;
663
664 if (refine) {
665 std::vector<Interval> newIntervals;
666 auto diameter = plaRegion.diameter();
667 // If we have no regions at all, initialize with the entire region
668 if (regionsAndBounds.empty()) {
669 regionsAndBounds.emplace_back(plaRegion, Interval(0, 1));
670 regionsInPLARegion.push_back(0);
671 }
672 // Add start (old regions might be larger than currently considered region)
673 if (regionsAndBounds[regionsInPLARegion.front()].first.lower() < plaRegion.lower()) {
674 newIntervals.push_back(Interval(regionsAndBounds[regionsInPLARegion.front()].first.lower(), plaRegion.lower()));
675 }
676 // Split up considered region
677 for (uint64_t i = 0; i < regionsRefine; i++) {
678 newIntervals.push_back(Interval(plaRegion.lower() + ((double)i / (double)regionsRefine) * diameter,
679 plaRegion.lower() + ((double)(i + 1) / (double)regionsRefine) * diameter));
680 }
681 // Add end
682 if (regionsAndBounds[regionsInPLARegion.back()].first.upper() > plaRegion.upper()) {
683 newIntervals.push_back(Interval(plaRegion.upper(), regionsAndBounds[regionsInPLARegion.back()].first.upper()));
684 }
685 // Remember everything that comes after what we changed
686 std::vector<std::pair<Interval, Interval>> regionsAndBoundsAfter;
687 for (uint64_t i = regionsInPLARegion.back() + 1; i < regionsAndBounds.size(); i++) {
688 regionsAndBoundsAfter.push_back(regionsAndBounds[i]);
689 }
690 // Remove previous results
691 regionsAndBounds.erase(regionsAndBounds.begin() + *regionsInPLARegion.begin(), regionsAndBounds.end());
692
693 // Compute region results using interval arithmetic
694 for (auto const& region : newIntervals) {
695 regionsAndBounds.emplace_back(region, annotation.evaluateOnIntervalMidpointTheorem(region));
696 }
697 // Emplace back remembered stuff
698 for (auto const& item : regionsAndBoundsAfter) {
699 regionsAndBounds.emplace_back(item);
700 }
701 }
702 } while (refine);
703 }
704
705 const ConstantType epsilon = 0;
706 // We want to check in the realm of feasible instantiations, even if our not our entire parameter space is feasible
707 lowerBound = utility::max(utility::min(lowerBound, utility::one<ConstantType>() - epsilon), epsilon);
708 upperBound = utility::max(utility::min(upperBound, utility::one<ConstantType>() - epsilon), epsilon);
709
710 STORM_LOG_ASSERT(lowerBound <= upperBound, "Whoops.");
711
712 placeholder = Interval(lowerBound, upperBound);
713 }
714 for (auto& key : insertThese) {
715 this->collectedValuations.insert(std::move(insertThese.extract(key.first)));
716 }
717 return false;
718}
719
721} // namespace transformer
722} // namespace storm
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
uint64_t getNumberOfSetBits() const
Returns the number of bits that are set to true in this bit vector.
size_t size() const
Retrieves the number of bits this bit vector can store.
bool get(uint64_t index) const
Retrieves the truth value of the bit at the given index and performs a bound check.
Valuation const & getLowerBoundaries() const
CoefficientType const & getLowerBoundary(VariableType const &variable) const
CoefficientType const & getUpperBoundary(VariableType const &variable) const
A class that can be used to build a sparse matrix by adding value by value.
A class that holds a possibly non-square matrix in the compressed row storage format.
const_rows getRow(index_type row) const
Returns an object representing the given row.
index_type getEntryCount() const
Returns the number of entries in the matrix.
std::vector< MatrixEntry< index_type, value_type > >::iterator iterator
index_type getRowCount() const
Returns the number of rows of the matrix.
static std::unordered_map< RationalFunction, Annotation > lastSavedAnnotations
Definition BigStep.h:207
std::optional< std::map< VariableType, std::set< CoefficientType > > > const & getExtrema() const
storm::storage::SparseMatrix< Interval > const & getMatrix() const
std::map< VariableType, std::set< uint_fast64_t > > const & getOccuringStatesAtVariable() const
std::vector< Interval > const & getVector() const
storm::utility::parametric::CoefficientType< ParametricType >::type CoefficientType
std::vector< std::set< VariableType > > const & getOccurringVariablesAtState() const
void specifyRegion(storm::storage::ParameterRegion< ParametricType > const &region, storm::solver::OptimizationDirection const &dirForParameters)
storm::utility::parametric::VariableType< ParametricType >::type VariableType
RobustParameterLifter(storm::storage::SparseMatrix< ParametricType > const &pMatrix, std::vector< ParametricType > const &pVector, storm::storage::BitVector const &selectedRows, storm::storage::BitVector const &selectedColumns, bool generateRowLabels=false, bool useMonotonicity=false)
Lifts the parameter choices to nondeterminisim.
virtual std::unique_ptr< storm::solver::SmtSolver > create(storm::expressions::ExpressionManager &manager) const
Creates a new SMT solver instance.
Definition solver.cpp:191
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_ERROR_COND(cond, message)
Definition macros.h:68
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:39
Expression maximum(Expression const &first, Expression const &second)
Expression minimum(Expression const &first, Expression const &second)
storm::utility::parametric::CoefficientType< storm::RationalFunction >::type CoefficientType
Interval evaluateExtremaAnnotations(std::map< UniPoly, std::set< double > > extremaAnnotations, Interval input)
carl::UnivariatePolynomial< RationalFunctionCoefficient > UniPoly
Definition BigStep.cpp:33
void gatherOccurringVariables(FunctionType const &function, std::set< typename VariableType< FunctionType >::type > &variableSet)
Add all variables that occur in the given function to the the given set.
ValueType max(ValueType const &first, ValueType const &second)
Extremum< storm::OptimizationDirection::Maximize, ValueType > Maximum
Definition Extremum.h:166
Extremum< storm::OptimizationDirection::Minimize, ValueType > Minimum
Definition Extremum.h:168
bool isConstant(ValueType const &)
ValueType min(ValueType const &first, ValueType const &second)
bool isZero(ValueType const &a)
Definition constants.cpp:44
ValueType zero()
Definition constants.cpp:26
ValueType one()
Definition constants.cpp:21
ValueType sqrt(ValueType const &number)
TargetType convertNumber(SourceType const &number)
carl::Interval< double > Interval
Interval type.
carl::RationalFunction< Polynomial, true > RationalFunction
carl::MultivariatePolynomial< RationalFunctionCoefficient > RawPolynomial