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