Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
RegionConstraintChecker.cpp
Go to the documentation of this file.
2
3#include <map>
4#include <optional>
5#include <set>
6#include <string>
7#include <utility>
8
13
14namespace storm::pars::analysis {
15
16namespace {
17
19using BaseExpression = storm::expressions::BaseExpression;
20using BinaryRelationExpression = storm::expressions::BinaryRelationExpression;
21
22using Form = storm::expressions::LinearCoefficientVisitor<CoefficientType>::VariableCoefficients;
23using VariablesByName = std::map<std::string, RationalFunctionRegion::VariableType>;
24
25// The exact extrema of an affine form over a region, with the points attaining them.
26struct Extremes {
27 CoefficientType minimum;
28 CoefficientType maximum;
29 RegionWitness minimizingPoint;
30 RegionWitness maximizingPoint;
31};
32
33// An affine form is monotone in each variable, so its extrema are attained where each variable is at the bound given by the sign of its coefficient.
34Extremes computeExtremes(Form const& form, VariablesByName const& variables, RationalFunctionRegion const& region) {
35 std::set<RationalFunctionRegion::VariableType> increasing;
36 std::set<RationalFunctionRegion::VariableType> decreasing;
37 for (auto const& [variable, coefficient] : form) {
38 (coefficient > storm::utility::zero<CoefficientType>() ? increasing : decreasing).insert(variables.at(variable.getName()));
39 }
40 RegionWitness minimizingPoint(region.getPoint(storm::solver::OptimizationDirection::Minimize, increasing, decreasing));
41 RegionWitness maximizingPoint(region.getPoint(storm::solver::OptimizationDirection::Maximize, increasing, decreasing));
42
43 auto evaluate = [&form, &variables](RegionWitness const& point) {
44 CoefficientType value = form.getConstantPart();
45 for (auto const& [variable, coefficient] : form) {
46 value += coefficient * point.at(variables.at(variable.getName()));
47 }
48 return value;
49 };
50 return Extremes{evaluate(minimizingPoint), evaluate(maximizingPoint), std::move(minimizingPoint), std::move(maximizingPoint)};
51}
52
53// The outcome of checking a single constraint.
54struct SingleVerdict {
55 ConstraintVerdict verdict = ConstraintVerdict::Unknown;
56 std::optional<RegionWitness> witness;
57};
58
59// Decides `f <relation> 0` from the extrema of the affine form f; constructs a witness if it fails.
60SingleVerdict checkAffineConstraint(RelationType relation, Extremes const& extremes) {
62
63 bool satisfiedThroughout;
64 switch (relation) {
65 case RelationType::Less:
66 satisfiedThroughout = extremes.maximum < zero;
67 break;
68 case RelationType::LessOrEqual:
69 satisfiedThroughout = extremes.maximum <= zero;
70 break;
71 case RelationType::Greater:
72 satisfiedThroughout = extremes.minimum > zero;
73 break;
74 case RelationType::GreaterOrEqual:
75 satisfiedThroughout = extremes.minimum >= zero;
76 break;
77 case RelationType::Equal:
78 satisfiedThroughout = extremes.minimum >= zero && extremes.maximum <= zero;
79 break;
80 case RelationType::NotEqual:
81 satisfiedThroughout = extremes.maximum < zero || extremes.minimum > zero;
82 break;
83 default:
84 return SingleVerdict{};
85 }
86
87 if (satisfiedThroughout) {
88 return SingleVerdict{ConstraintVerdict::Sat, std::nullopt};
89 }
90
91 switch (relation) {
92 case RelationType::Less:
93 case RelationType::LessOrEqual:
94 return SingleVerdict{ConstraintVerdict::Unsat, extremes.maximizingPoint};
95 case RelationType::Greater:
96 case RelationType::GreaterOrEqual:
97 return SingleVerdict{ConstraintVerdict::Unsat, extremes.minimizingPoint};
98 case RelationType::Equal:
99 // The form is not identically zero, so one of the two extremizing points witnesses the violation.
100 if (extremes.maximum != zero) {
101 return SingleVerdict{ConstraintVerdict::Unsat, extremes.maximizingPoint};
102 }
103 return SingleVerdict{ConstraintVerdict::Unsat, extremes.minimizingPoint};
104 case RelationType::NotEqual: {
105 if (extremes.minimum == zero) {
106 return SingleVerdict{ConstraintVerdict::Unsat, extremes.minimizingPoint};
107 }
108 if (extremes.maximum == zero) {
109 return SingleVerdict{ConstraintVerdict::Unsat, extremes.maximizingPoint};
110 }
111 // minimum < 0 < maximum: f is zero at the interpolated point for t = minimum / (minimum - maximum).
112 CoefficientType const t = extremes.minimum / (extremes.minimum - extremes.maximum);
113 RegionWitness witness;
114 for (auto const& [variable, value] : extremes.minimizingPoint) {
115 witness.emplace(variable, value + t * (extremes.maximizingPoint.at(variable) - value));
116 }
117 return SingleVerdict{ConstraintVerdict::Unsat, std::move(witness)};
118 }
119 default:
120 return SingleVerdict{};
121 }
122}
123
124} // namespace
125
126std::ostream& operator<<(std::ostream& out, RegionWitness const& witness) {
127 bool first = true;
128 for (auto const& [variable, value] : witness) {
129 out << (first ? "" : ", ") << variable.name() << " = " << value;
130 first = false;
131 }
132 return out;
133}
134
136
138 return checkConstraints(this->collector.getWellformedConstraints(), region, "well-formedness");
139}
140
142 // By definition, a model that is not well-formed is not graph-preserving, so the well-formedness constraints are part of graph preservation.
143 auto constraints = this->collector.getWellformedConstraints();
144 auto const& graphPreservingConstraints = this->collector.getGraphPreservingConstraints();
145 constraints.insert(constraints.end(), graphPreservingConstraints.begin(), graphPreservingConstraints.end());
146 return checkConstraints(constraints, region, "graph-preserving");
147}
148
152
156
158 RationalFunctionRegion const& region, std::string const& label) {
159 RegionCheckResult result;
160 VariablesByName variables;
161 for (auto const& variable : region.getVariables()) {
162 variables.emplace(variable.name(), variable);
163 }
165 bool violated = false;
166 bool undecided = false;
167
168 for (storm::expressions::Expression const& constraint : constraints) {
169 BaseExpression const& baseExpression = constraint.getBaseExpression();
170
171 SingleVerdict singleVerdict;
172 if (baseExpression.isBooleanLiteralExpression()) {
173 // The collector emits `false` for rejected constant entries, e.g. a fixed probability outside [0, 1].
174 if (baseExpression.asBooleanLiteralExpression().getValue()) {
175 singleVerdict = SingleVerdict{ConstraintVerdict::Sat, std::nullopt};
176 } else {
177 singleVerdict = SingleVerdict{ConstraintVerdict::Unsat, RegionWitness(region.getCenterPoint())};
178 }
179 } else if (baseExpression.isBinaryRelationExpression()) {
180 BinaryRelationExpression const& relationExpression = baseExpression.asBinaryRelationExpression();
181 auto form = coefficientVisitor.tryGetLinearCoefficients(constraint.getOperand(0));
182 auto rhs = coefficientVisitor.tryGetLinearCoefficients(constraint.getOperand(1));
183
184 if (form.has_value() && rhs.has_value()) {
185 // The relation is compared against zero for the form of `lhs - rhs`.
186 *form -= std::move(*rhs);
187
188 for (auto const& [variable, coefficient] : *form) {
189 STORM_LOG_THROW(variables.count(variable.getName()) != 0, storm::exceptions::InvalidArgumentException,
190 "Cannot check " << label << " constraint '" << constraint.toString()
191 << "' over the given region, because it constrains the variable '" << variable.getName()
192 << "', which is not part of the region. Please provide a region covering all constrained variables.");
193 }
194
195 Extremes const extremes = computeExtremes(*form, variables, region);
196 singleVerdict = checkAffineConstraint(relationExpression.getRelationType(), extremes);
197 }
198 }
199
200 switch (singleVerdict.verdict) {
201 case ConstraintVerdict::Sat:
202 break;
203 case ConstraintVerdict::Unsat:
204 violated = true;
205 if (!result.violatedConstraint.has_value()) {
206 result.witness = singleVerdict.witness;
207 result.violatedConstraint = constraint;
208 }
209 break;
210 case ConstraintVerdict::Unknown:
211 undecided = true;
212 result.undecidedConstraints.push_back(constraint);
213 break;
214 }
215 }
216
217 if (violated) {
218 result.verdict = ConstraintVerdict::Unsat;
219 } else if (undecided) {
220 result.verdict = ConstraintVerdict::Unknown;
221 } else {
222 result.verdict = ConstraintVerdict::Sat;
223 }
224
225 return result;
226}
227
228} // namespace storm::pars::analysis
Class to collect constraints on parametric Markov chains.
std::vector< storm::expressions::Expression > ConstraintSet
The type of the collected constraints.
Computes the coefficients of a linear expression.
std::optional< VariableCoefficients > tryGetLinearCoefficients(Expression const &expression)
As getLinearCoefficients, but returns std::nullopt instead of throwing if the expression is not linea...
bool isWellDefined(RationalFunctionRegion const &region) const
Returns true iff all well-formedness constraints were proven to hold throughout the region.
RegionCheckResult checkGraphPreservation(RationalFunctionRegion const &region) const
Checks the graph-preserving constraints together with the well-formedness constraints,...
RegionConstraintChecker(storm::analysis::ConstraintCollector const &collector)
static RegionCheckResult checkConstraints(storm::analysis::ConstraintCollector::ConstraintSet const &constraints, RationalFunctionRegion const &region, std::string const &label)
Checks an arbitrary constraint set over a region.
bool isGraphPreserving(RationalFunctionRegion const &region) const
Returns true iff all graph-preserving and well-formedness constraints were proven to hold throughout ...
RegionCheckResult checkWellformedness(RationalFunctionRegion const &region) const
std::set< VariableType > const & getVariables() const
Valuation getCenterPoint() const
Returns the center point of this region.
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:39
RelationType
An enum type specifying the different relations applicable.
storm::storage::ParameterRegion< storm::RationalFunction > RationalFunctionRegion
storm::solver::SmtSolver::CheckResult ConstraintVerdict
Tri-state outcome of a check: Sat means the constraint holds on the whole region, Unsat means it is v...
RationalFunctionRegion::CoefficientType CoefficientType
std::ostream & operator<<(std::ostream &stream, GraphPreservationStatus status)
double evaluate(storm::RationalFunction const &function, Valuation< storm::RationalFunction > const &valuation)
ValueType zero()
Definition constants.cpp:26
std::optional< RegionWitness > witness
A point at which a constraint is violated.
std::vector< storm::expressions::Expression > undecidedConstraints
All constraints that could not be decided.
std::optional< storm::expressions::Expression > violatedConstraint
The constraint violated at the witness.
A point of a parameter region, mapping the variables to exact values.