3#include <carl/core/rootfinder/RootFinder.h>
12#include <boost/functional/hash.hpp>
36template<
typename ParametricType,
typename ConstantType>
38 std::vector<ParametricType>
const& pVector,
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++;
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++;
58 uint64_t pMatrixEntryCount = 0;
59 uint64_t pVectorEntryCount = 0;
64 this->occurringVariablesAtState.resize(pMatrix.
getRowCount());
66 for (uint64_t row = 0; row < pMatrix.
getRowCount(); row++) {
67 if (!selectedRows.
get(row)) {
70 std::set<VariableType> occurringVariables;
71 for (
auto const& entry : pMatrix.
getRow(row)) {
72 auto column = entry.getColumn();
73 if (!selectedColumns.
get(column)) {
77 auto transition = entry.getValue();
79 auto variables = transition.gatherVariables();
80 occurringVariables.insert(variables.begin(), variables.end());
85 nonConstMatrixEntries.set(pMatrixEntryCount,
true);
87 builder.addNextValue(oldToNewColumnIndexMapping[row], oldToNewColumnIndexMapping[column],
Interval());
88 Interval& placeholder = functionValuationCollector.add(valuation);
96 for (
auto& var : occurringVariables) {
97 occuringStatesAtVariable[var].insert(row);
99 occurringVariablesAtState[row] = std::move(occurringVariables);
102 for (uint64_t i = 0; i < pVector.size(); i++) {
103 auto const transition = pVector[i];
104 if (!selectedRows.
get(i)) {
110 nonConstVectorEntries.set(pVectorEntryCount,
true);
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);
124 vector.shrink_to_fit();
125 matrixAssignment.shrink_to_fit();
126 vectorAssignment.shrink_to_fit();
127 nonConstMatrixEntries.resize(pMatrixEntryCount);
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;
142 startEntryOfRow = startEntryOfNextRow;
144 STORM_LOG_ASSERT(matrixAssignmentIt == matrixAssignment.end(),
"Unexpected number of entries in the matrix assignment.");
146 auto vectorAssignmentIt = vectorAssignment.begin();
147 for (uint64_t nonConstVectorEntry : nonConstVectorEntries) {
148 for (uint64_t vectorIndex = matrix.getRowGroupIndices()[nonConstVectorEntry]; vectorIndex != matrix.getRowGroupIndices()[nonConstVectorEntry + 1];
150 vectorAssignmentIt->first = vector.begin() + vectorIndex;
151 ++vectorAssignmentIt;
154 STORM_LOG_ASSERT(vectorAssignmentIt == vectorAssignment.end(),
"Unexpected number of entries in the vector assignment.");
157template<
typename ParametricType,
typename ConstantType>
161 this->currentRegionAllIllDefined = functionValuationCollector.evaluateCollectedFunctions(region, dirForParameters);
166 for (
auto& assignment : matrixAssignment) {
167 assignment.first->setValue(assignment.second);
170 for (
auto& assignment : vectorAssignment) {
171 *assignment.first = assignment.second;
175template<
typename ParametricType,
typename ConstantType>
176const std::vector<std::set<typename RobustParameterLifter<ParametricType, ConstantType>::VariableType>>&
178 return occurringVariablesAtState;
181template<
typename ParametricType,
typename ConstantType>
182std::map<typename RobustParameterLifter<ParametricType, ConstantType>::VariableType, std::set<uint_fast64_t>>
const&
184 return occuringStatesAtVariable;
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>();
194 auto smtSolver = factory.
create(*expressionManager);
198 auto expression = rfte.toExpression(function) == expressionManager->rational(0);
200 auto variables = expressionManager->getVariables();
203 for (
auto const& var : variables) {
204 exprBounds = exprBounds && expressionManager->rational(0) <= var && var <= expressionManager->rational(1);
207 smtSolver->setTimeout(50);
209 smtSolver->add(exprBounds);
210 smtSolver->add(expression);
212 std::set<CoefficientType> zeroes = {};
215 auto checkResult = smtSolver->check();
218 auto model = smtSolver->getModel();
221 if (variables.size() != 1) {
224 auto const var = *variables.begin();
226 double value = model->getRationalValue(var);
233 smtSolver->addNotCurrentModel();
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>(
252 carl::rootfinder::SplittingStrategy::ABERTH);
253 std::set<CoefficientType> zeroes = {};
254 for (carl::RealAlgebraicNumber<CoefficientType>
const& root : carlRoots) {
256 if (root.isNumeric()) {
261 zeroes.emplace(rootCoefficient);
266template<
typename ParametricType,
typename ConstantType>
267std::set<typename storm::utility::parametric::CoefficientType<ParametricType>::type>
270 if (polynomial.isConstant()) {
273 STORM_LOG_ERROR_COND(polynomial.gatherVariables().size() == 1,
"Multi-variate polynomials currently not supported");
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) {
285 switch (term.tdeg()) {
299 STORM_LOG_THROW(
false, storm::exceptions::InvalidArgumentException,
"Transitions are only allowed to have have a maximum degree of four.");
323 return {-b / (2 * a)};
329 std::set<CoefficientType> roots;
333 CoefficientType q = (2 * b * b * b - 9 * a * b * c + 27 * a * a * d) / (27 * a * a * a);
350 roots = {-3 * q / (p * 2), 3 * q / p};
354 roots = {u - p / (3 * u)};
357 double t = std::acos(3 * qDouble / pDouble / u) / 3;
358 double k = 2 * M_PI / 3;
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;
375 for (
auto const& var : occurringVariables) {
376 parameters.emplace(var);
380template<
typename ParametricType,
typename ConstantType>
385template<
typename ParametricType,
typename ConstantType>
390template<
typename ParametricType,
typename ConstantType>
392 return currentRegionAllIllDefined;
395template<
typename ParametricType,
typename ConstantType>
397 return this->transition == other.transition;
400template<
typename ParametricType,
typename ConstantType>
401std::set<typename RobustParameterLifter<ParametricType, ConstantType>::VariableType>
const&
406template<
typename ParametricType,
typename ConstantType>
408 return this->transition;
411template<
typename ParametricType,
typename ConstantType>
414 if (this->extrema || this->annotation) {
422 auto const& terms = annotation.getTerms();
427 std::optional<std::set<CoefficientType>> carlResult;
429 if (terms.size() < 5) {
430 carlResult = zeroesCarl(annotation.getProbability().derivative(), annotation.getParameter());
435 this->extrema = std::map<VariableType, std::set<CoefficientType>>();
436 (*this->extrema)[annotation.getParameter()];
437 for (
auto const& root : *carlResult) {
442 annotation.computeDerivative(4);
444 this->annotation.emplace(annotation);
446 this->extrema = std::map<VariableType, std::set<CoefficientType>>();
448 for (
auto const& p : transition.gatherVariables()) {
449 (*this->extrema)[p] = {};
451 auto const&
derivative = transition.derivative(p);
458 auto nominatorAsUnivariate =
derivative.nominator().toUnivariatePolynomial();
460 nominatorAsUnivariate /=
derivative.denominator().coefficient();
463 std::optional<std::set<CoefficientType>> zeroes;
465 if (
derivative.nominator().totalDegree() < 4) {
471 for (
auto const& zero : *zeroes) {
473 this->extrema->at(p).emplace(zero);
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&
484 return this->extrema;
487template<
typename ParametricType,
typename ConstantType>
489 return this->annotation;
492template<
typename ParametricType,
typename ConstantType>
494 std::size_t seed = 0;
495 boost::hash_combine(seed, transition);
499template<
typename ParametricType,
typename ConstantType>
502 if (!collectedValuations.count(valuation)) {
504 this->regionsAndBounds.emplace(valuation, std::vector<std::pair<Interval, Interval>>());
508 auto insertionRes = collectedValuations.insert(std::pair<RobustAbstractValuation, Interval>(std::move(valuation),
storm::Interval(0, 1)));
509 return insertionRes.first->second;
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);
525 for (
auto const& potentialExtremum : potentialExtrema) {
531 sumOfTerms +=
Interval(*minValue, *maxValue);
536template<
typename ParametricType,
typename ConstantType>
537bool RobustParameterLifter<ParametricType, ConstantType>::FunctionValuationCollector::evaluateCollectedFunctions(
539 std::unordered_map<RobustAbstractValuation, Interval, RobustAbstractValuationHash> insertThese;
540 for (
auto& [abstrValuation, placeholder] : collectedValuations) {
545 if (abstrValuation.getExtrema()) {
552 auto const& maybeAnnotation = abstrValuation.getAnnotation();
554 if (maybeAnnotation) {
556 auto p = maybeAnnotation->getParameter();
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);
569 for (
auto const& potentialExtremum : potentialExtrema) {
583 std::map<VariableType, CoefficientType> lowerPositions;
584 std::map<VariableType, CoefficientType> upperPositions;
586 for (
auto const& p : abstrValuation.getParameters()) {
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);
602 auto instantiation = std::map<VariableType, CoefficientType>(region.
getLowerBoundaries());
604 for (
auto const& potentialExtremum : potentialExtrema) {
606 instantiation[p] = potentialExtremum;
607 auto value = abstrValuation.getTransition().evaluate(instantiation);
608 if (maxValue &= value) {
609 maxPosP = potentialExtremum;
611 if (minValue &= value) {
612 minPosP = potentialExtremum;
615 STORM_LOG_ASSERT(!minValue.empty(),
"Expected at least one potential extremum.");
617 lowerPositions[p] = minPosP;
618 upperPositions[p] = maxPosP;
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();
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()) {
657 regionsInPLARegion.push_back(i);
661 uint64_t regionsRefine = std::max((uint64_t)10, annotation.maxDegree());
662 refine = regionsInPLARegion.size() < regionsRefine;
665 std::vector<Interval> newIntervals;
666 auto diameter = plaRegion.diameter();
668 if (regionsAndBounds.empty()) {
669 regionsAndBounds.emplace_back(plaRegion,
Interval(0, 1));
670 regionsInPLARegion.push_back(0);
673 if (regionsAndBounds[regionsInPLARegion.front()].first.lower() < plaRegion.lower()) {
674 newIntervals.push_back(
Interval(regionsAndBounds[regionsInPLARegion.front()].first.lower(), plaRegion.lower()));
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));
682 if (regionsAndBounds[regionsInPLARegion.back()].first.upper() > plaRegion.upper()) {
683 newIntervals.push_back(
Interval(plaRegion.upper(), regionsAndBounds[regionsInPLARegion.back()].first.upper()));
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]);
691 regionsAndBounds.erase(regionsAndBounds.begin() + *regionsInPLARegion.begin(), regionsAndBounds.end());
694 for (
auto const& region : newIntervals) {
695 regionsAndBounds.emplace_back(region, annotation.evaluateOnIntervalMidpointTheorem(region));
698 for (
auto const& item : regionsAndBoundsAfter) {
699 regionsAndBounds.emplace_back(item);
705 const ConstantType epsilon = 0;
712 placeholder =
Interval(lowerBound, upperBound);
714 for (
auto& key : insertThese) {
715 this->collectedValuations.insert(std::move(insertThese.extract(key.first)));
A bit vector that is internally represented as a vector of 64-bit values.
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.
virtual std::unique_ptr< storm::solver::SmtSolver > create(storm::expressions::ExpressionManager &manager) const
Creates a new SMT solver instance.
#define STORM_LOG_ASSERT(cond, message)
#define STORM_LOG_ERROR_COND(cond, message)
#define STORM_LOG_THROW(cond, exception, message)
Expression maximum(Expression const &first, Expression const &second)
Expression minimum(Expression const &first, Expression const &second)
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
Extremum< storm::OptimizationDirection::Minimize, ValueType > Minimum
bool isConstant(ValueType const &)
ValueType min(ValueType const &first, ValueType const &second)
bool isZero(ValueType const &a)
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