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>
44namespace transformer {
48template<
typename ParametricType,
typename ConstantType>
50 std::vector<ParametricType>
const& pVector,
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++;
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++;
70 uint64_t pMatrixEntryCount = 0;
71 uint64_t pVectorEntryCount = 0;
76 this->occurringVariablesAtState.resize(pMatrix.
getRowCount());
78 for (uint64_t row = 0; row < pMatrix.
getRowCount(); row++) {
79 if (!selectedRows.
get(row)) {
82 std::set<VariableType> occurringVariables;
83 for (
auto const& entry : pMatrix.
getRow(row)) {
84 auto column = entry.getColumn();
85 if (!selectedColumns.
get(column)) {
89 auto transition = entry.getValue();
91 auto variables = transition.gatherVariables();
92 occurringVariables.insert(variables.begin(), variables.end());
95 builder.
addNextValue(oldToNewColumnIndexMapping[row], oldToNewColumnIndexMapping[column], utility::convertNumber<double>(transition));
97 nonConstMatrixEntries.set(pMatrixEntryCount,
true);
99 builder.
addNextValue(oldToNewColumnIndexMapping[row], oldToNewColumnIndexMapping[column],
Interval());
100 Interval& placeholder = functionValuationCollector.add(valuation);
108 for (
auto& var : occurringVariables) {
109 occuringStatesAtVariable[var].insert(row);
111 occurringVariablesAtState[row] = std::move(occurringVariables);
114 for (uint64_t i = 0; i < pVector.size(); i++) {
115 auto const transition = pVector[i];
116 if (!selectedRows.
get(i)) {
120 vector.push_back(utility::convertNumber<double>(transition));
122 nonConstVectorEntries.set(pVectorEntryCount,
true);
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);
135 matrix = builder.
build();
136 vector.shrink_to_fit();
137 matrixAssignment.shrink_to_fit();
138 vectorAssignment.shrink_to_fit();
139 nonConstMatrixEntries.resize(pMatrixEntryCount);
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;
154 startEntryOfRow = startEntryOfNextRow;
156 STORM_LOG_ASSERT(matrixAssignmentIt == matrixAssignment.end(),
"Unexpected number of entries in the matrix assignment.");
158 auto vectorAssignmentIt = vectorAssignment.begin();
159 for (
auto const& nonConstVectorEntry : nonConstVectorEntries) {
160 for (uint64_t vectorIndex = matrix.getRowGroupIndices()[nonConstVectorEntry]; vectorIndex != matrix.getRowGroupIndices()[nonConstVectorEntry + 1];
162 vectorAssignmentIt->first = vector.begin() + vectorIndex;
163 ++vectorAssignmentIt;
166 STORM_LOG_ASSERT(vectorAssignmentIt == vectorAssignment.end(),
"Unexpected number of entries in the vector assignment.");
169template<
typename ParametricType,
typename ConstantType>
173 this->currentRegionAllIllDefined = functionValuationCollector.evaluateCollectedFunctions(region, dirForParameters);
178 for (
auto& assignment : matrixAssignment) {
179 assignment.first->setValue(assignment.second);
182 for (
auto& assignment : vectorAssignment) {
183 *assignment.first = assignment.second;
187template<
typename ParametricType,
typename ConstantType>
188const std::vector<std::set<typename RobustParameterLifter<ParametricType, ConstantType>::VariableType>>&
190 return occurringVariablesAtState;
193template<
typename ParametricType,
typename ConstantType>
194std::map<typename RobustParameterLifter<ParametricType, ConstantType>::VariableType, std::set<uint_fast64_t>>
const&
196 return occuringStatesAtVariable;
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>();
206 auto smtSolver = factory.
create(*expressionManager);
210 auto expression = rfte.toExpression(function) == expressionManager->rational(0);
212 auto variables = expressionManager->getVariables();
215 for (
auto const& var : variables) {
216 exprBounds = exprBounds && expressionManager->rational(0) <= var && var <= expressionManager->rational(1);
219 smtSolver->setTimeout(50);
221 smtSolver->add(exprBounds);
222 smtSolver->add(expression);
224 std::set<CoefficientType> zeroes = {};
227 auto checkResult = smtSolver->check();
230 auto model = smtSolver->getModel();
233 if (variables.size() != 1) {
236 auto const var = *variables.begin();
238 double value = model->getRationalValue(var);
240 zeroes.emplace(utility::convertNumber<CoefficientType>(value));
245 smtSolver->addNotCurrentModel();
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>(
264 carl::rootfinder::SplittingStrategy::ABERTH);
265 std::set<CoefficientType> zeroes = {};
266 for (carl::RealAlgebraicNumber<CoefficientType>
const& root : carlRoots) {
268 if (root.isNumeric()) {
273 zeroes.emplace(rootCoefficient);
278template<
typename ParametricType,
typename ConstantType>
279std::set<typename storm::utility::parametric::CoefficientType<ParametricType>::type>
280RobustParameterLifter<ParametricType, ConstantType>::RobustAbstractValuation::cubicEquationZeroes(
282 if (polynomial.isConstant()) {
285 STORM_LOG_ERROR_COND(polynomial.gatherVariables().size() == 1,
"Multi-variate polynomials currently not supported");
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) {
297 STORM_LOG_ASSERT(term.tdeg() < 4,
"Transitions are only allowed to have a maximum degree of four.");
298 switch (term.tdeg()) {
333 return {-b / (2 * a)};
339 std::set<CoefficientType> roots;
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);
348 roots = {utility::convertNumber<CoefficientType>(std::cbrt(-qDouble))};
352 roots.emplace(utility::convertNumber<CoefficientType>(
utility::sqrt(-pDouble)));
353 roots.emplace(utility::convertNumber<CoefficientType>(-
utility::sqrt(-pDouble)));
360 roots = {-3 * q / (p * 2), 3 * q / p};
362 double Ddouble = utility::convertNumber<ConstantType>(D);
364 roots = {u - p / (3 * u)};
367 double t = std::acos(3 * qDouble / pDouble / u) / 3;
368 double k = 2 * M_PI / 3;
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))};
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;
385 for (
auto const& var : occurringVariables) {
386 parameters.emplace(var);
390template<
typename ParametricType,
typename ConstantType>
395template<
typename ParametricType,
typename ConstantType>
400template<
typename ParametricType,
typename ConstantType>
402 return currentRegionAllIllDefined;
405template<
typename ParametricType,
typename ConstantType>
407 return this->transition == other.transition;
410template<
typename ParametricType,
typename ConstantType>
411std::set<typename RobustParameterLifter<ParametricType, ConstantType>::VariableType>
const&
416template<
typename ParametricType,
typename ConstantType>
418 return this->transition;
421template<
typename ParametricType,
typename ConstantType>
424 if (this->extrema || this->annotation) {
432 auto const& terms = annotation.getTerms();
437 std::optional<std::set<CoefficientType>> carlResult;
439 if (terms.size() < 5) {
440 carlResult = zeroesCarl(annotation.getProbability().derivative(), annotation.getParameter());
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));
452 annotation.computeDerivative(4);
454 this->annotation.emplace(annotation);
456 this->extrema = std::map<VariableType, std::set<CoefficientType>>();
458 for (
auto const& p : transition.gatherVariables()) {
459 (*this->extrema)[p] = {};
461 auto const& derivative = transition.derivative(p);
463 if (derivative.isConstant()) {
468 auto nominatorAsUnivariate = derivative.nominator().toUnivariatePolynomial();
470 nominatorAsUnivariate /= derivative.denominator().coefficient();
473 std::optional<std::set<CoefficientType>> zeroes;
475 if (derivative.nominator().totalDegree() < 4) {
476 zeroes = cubicEquationZeroes(
RawPolynomial(derivative.nominator()), p);
478 zeroes = zeroesSMT(derivative, p);
481 for (
auto const& zero : *zeroes) {
482 if (zero >= utility::zero<CoefficientType>() && zero <= utility::one<CoefficientType>()) {
483 this->extrema->at(p).emplace(zero);
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&
494 return this->extrema;
497template<
typename ParametricType,
typename ConstantType>
499 return this->annotation;
502template<
typename ParametricType,
typename ConstantType>
504 std::size_t seed = 0;
505 carl::hash_add(seed, transition);
509template<
typename ParametricType,
typename ConstantType>
512 if (!collectedValuations.count(valuation)) {
514 this->regionsAndBounds.emplace(valuation, std::vector<std::pair<Interval, Interval>>());
518 auto insertionRes = collectedValuations.insert(std::pair<RobustAbstractValuation, Interval>(std::move(valuation),
storm::Interval(0, 1)));
519 return insertionRes.first->second;
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);
532 double minValue = utility::infinity<double>();
533 double maxValue = -utility::infinity<double>();
535 for (
auto const& potentialExtremum : potentialExtrema) {
536 auto value = utility::convertNumber<double>(poly.evaluate(utility::convertNumber<RationalFunctionCoefficient>(potentialExtremum)));
537 if (value > maxValue) {
540 if (value < minValue) {
544 sumOfTerms +=
Interval(minValue, maxValue);
549template<
typename ParametricType,
typename ConstantType>
550bool RobustParameterLifter<ParametricType, ConstantType>::FunctionValuationCollector::evaluateCollectedFunctions(
552 std::unordered_map<RobustAbstractValuation, Interval, RobustAbstractValuationHash> insertThese;
553 for (
auto& [abstrValuation, placeholder] : collectedValuations) {
557 ConstantType lowerBound = utility::infinity<ConstantType>();
558 ConstantType upperBound = -utility::infinity<ConstantType>();
560 if (abstrValuation.getExtrema()) {
567 auto const& maybeAnnotation = abstrValuation.getAnnotation();
569 if (maybeAnnotation) {
571 auto p = maybeAnnotation->getParameter();
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);
582 for (
auto const& potentialExtremum : potentialExtrema) {
584 auto value = maybeAnnotation->evaluate(utility::convertNumber<double>(potentialExtremum));
585 if (value > upperBound) {
588 if (value < lowerBound) {
597 std::map<VariableType, CoefficientType> lowerPositions;
598 std::map<VariableType, CoefficientType> upperPositions;
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);
616 auto instantiation = std::map<VariableType, CoefficientType>(region.
getLowerBoundaries());
618 for (
auto const& potentialExtremum : potentialExtrema) {
620 instantiation[p] = potentialExtremum;
621 auto value = abstrValuation.getTransition().evaluate(instantiation);
622 if (value > maxValue) {
624 maxPosP = potentialExtremum;
626 if (value < minValue) {
628 minPosP = potentialExtremum;
632 lowerPositions[p] = minPosP;
633 upperPositions[p] = maxPosP;
637 lowerBound = utility::convertNumber<ConstantType>(abstrValuation.getTransition().evaluate(lowerPositions));
638 upperBound = utility::convertNumber<ConstantType>(abstrValuation.getTransition().evaluate(upperPositions));
641 if (upperBound < utility::zero<ConstantType>() || lowerBound > utility::one<ConstantType>()) {
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();
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()) {
672 regionsInPLARegion.push_back(i);
676 uint64_t regionsRefine = std::max((uint64_t)10, annotation.maxDegree());
677 refine = regionsInPLARegion.size() < regionsRefine;
680 std::vector<Interval> newIntervals;
681 auto diameter = plaRegion.diameter();
683 if (regionsAndBounds.empty()) {
684 regionsAndBounds.emplace_back(plaRegion,
Interval(0, 1));
685 regionsInPLARegion.push_back(0);
688 if (regionsAndBounds[regionsInPLARegion.front()].first.lower() < plaRegion.lower()) {
689 newIntervals.push_back(
Interval(regionsAndBounds[regionsInPLARegion.front()].first.lower(), plaRegion.lower()));
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));
697 if (regionsAndBounds[regionsInPLARegion.back()].first.upper() > plaRegion.upper()) {
698 newIntervals.push_back(
Interval(plaRegion.upper(), regionsAndBounds[regionsInPLARegion.back()].first.upper()));
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]);
706 regionsAndBounds.erase(regionsAndBounds.begin() + *regionsInPLARegion.begin(), regionsAndBounds.end());
709 for (
auto const& region : newIntervals) {
710 regionsAndBounds.emplace_back(region, annotation.evaluateOnIntervalMidpointTheorem(region));
713 for (
auto const& item : regionsAndBoundsAfter) {
714 regionsAndBounds.emplace_back(item);
724 const ConstantType epsilon = 0;
731 placeholder =
Interval(lowerBound, upperBound);
733 for (
auto& key : insertThese) {
734 this->collectedValuations.insert(std::move(insertThese.extract(key.first)));
739template class RobustParameterLifter<storm::RationalFunction, double>;
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.
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.
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)
std::set< storm::RationalFunctionVariable > getParameters(DFT< storm::RationalFunction > const &dft)
Get all rate/probability parameters occurring in the DFT.
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 &)
ValueType min(ValueType const &first, ValueType const &second)
bool isZero(ValueType const &a)
ValueType sqrt(ValueType const &number)
carl::Interval< double > Interval
Interval type.
carl::MultivariatePolynomial< RationalFunctionCoefficient > RawPolynomial