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