1#include "storm-config.h"
32double asDouble(CoefficientType
const& value) {
40 carl::VariablePool& pool = carl::VariablePool::getInstance();
41 auto variable = pool.findVariableWithName(name);
43 variable = pool.getFreshPersistentVariable(name);
51Region buildRegion(std::vector<std::pair<std::string, std::pair<double, double>>>
const& bounds) {
52 Region::Valuation lowerBoundaries;
53 Region::Valuation upperBoundaries;
54 for (
auto const& [variable, boundsOfVariable] : bounds) {
55 lowerBoundaries[variableByName(variable)] = coefficient(boundsOfVariable.first);
56 upperBoundaries[variableByName(variable)] = coefficient(boundsOfVariable.second);
58 return Region(lowerBoundaries, upperBoundaries);
65 manager->declareRationalVariable(name);
66 return manager->getVariableExpression(name);
70 return ConstraintSet{constraint};
75TEST(RegionConstraintCheckerTest, AffineConstraintHoldsThroughout) {
76 auto manager = std::make_shared<storm::expressions::ExpressionManager>();
77 auto x = variable(manager,
"x");
78 auto y = variable(manager,
"y");
81 auto result = Checker::checkConstraints(buildConstraintSet(x + y - manager->integer(3) <= manager->integer(0)),
82 buildRegion({{
"x", {0.0, 1.0}}, {
"y", {0.0, 1.0}}}),
"test");
84 EXPECT_EQ(storm::pars::analysis::ConstraintVerdict::Sat, result.verdict);
85 EXPECT_FALSE(result.violatedConstraint.has_value());
86 EXPECT_TRUE(result.undecidedConstraints.empty());
87 EXPECT_FALSE(result.witness.has_value());
88 EXPECT_TRUE(result.holdsThroughout());
91TEST(RegionConstraintCheckerTest, AffineConstraintViolatedAtCorner) {
92 auto manager = std::make_shared<storm::expressions::ExpressionManager>();
93 auto x = variable(manager,
"x");
94 auto y = variable(manager,
"y");
97 auto result = Checker::checkConstraints(buildConstraintSet(x + y - manager->integer(1) <= manager->integer(0)),
98 buildRegion({{
"x", {0.0, 1.0}}, {
"y", {0.0, 1.0}}}),
"test");
100 EXPECT_EQ(storm::pars::analysis::ConstraintVerdict::Unsat, result.verdict);
101 EXPECT_FALSE(result.holdsThroughout());
102 EXPECT_TRUE(result.violatedConstraint.has_value());
103 ASSERT_TRUE(result.witness.has_value());
104 EXPECT_NEAR(1.0, asDouble(result.witness->at(variableByName(
"x"))), 1e-9);
105 EXPECT_NEAR(1.0, asDouble(result.witness->at(variableByName(
"y"))), 1e-9);
108TEST(RegionConstraintCheckerTest, StrictInequalityOnBoundaryIsViolated) {
109 auto manager = std::make_shared<storm::expressions::ExpressionManager>();
110 auto x = variable(manager,
"x");
111 Region region = buildRegion({{
"x", {0.0, 1.0}}});
114 auto violated = Checker::checkConstraints(buildConstraintSet(x < manager->integer(1)), region,
"test");
115 EXPECT_EQ(storm::pars::analysis::ConstraintVerdict::Unsat, violated.verdict);
116 ASSERT_TRUE(violated.witness.has_value());
117 EXPECT_NEAR(1.0, asDouble(violated.witness->at(variableByName(
"x"))), 1e-9);
120 EXPECT_EQ(storm::pars::analysis::ConstraintVerdict::Sat, Checker::checkConstraints(buildConstraintSet(x <= manager->integer(1)), region,
"test").verdict);
123 EXPECT_EQ(storm::pars::analysis::ConstraintVerdict::Sat, Checker::checkConstraints(buildConstraintSet(x < manager->integer(2)), region,
"test").verdict);
126TEST(RegionConstraintCheckerTest, NegativeCoefficientIsHandled) {
127 auto manager = std::make_shared<storm::expressions::ExpressionManager>();
128 auto x = variable(manager,
"x");
129 Region region = buildRegion({{
"x", {0.0, 1.0}}});
132 EXPECT_EQ(storm::pars::analysis::ConstraintVerdict::Sat,
133 Checker::checkConstraints(buildConstraintSet(manager->integer(3) - manager->integer(2) * x >= manager->integer(0)), region,
"test").verdict);
136 EXPECT_EQ(storm::pars::analysis::ConstraintVerdict::Unsat,
137 Checker::checkConstraints(buildConstraintSet(manager->integer(1) - manager->integer(2) * x >= manager->integer(0)), region,
"test").verdict);
140 auto violated = Checker::checkConstraints(buildConstraintSet(manager->integer(1) - x >= manager->integer(0)), buildRegion({{
"x", {0.0, 2.0}}}),
"test");
141 EXPECT_EQ(storm::pars::analysis::ConstraintVerdict::Unsat, violated.verdict);
142 ASSERT_TRUE(violated.witness.has_value());
143 EXPECT_NEAR(2.0, asDouble(violated.witness->at(variableByName(
"x"))), 1e-9);
146TEST(RegionConstraintCheckerTest, NotEqualOverPositiveRegion) {
147 auto manager = std::make_shared<storm::expressions::ExpressionManager>();
148 auto x = variable(manager,
"x");
151 EXPECT_EQ(storm::pars::analysis::ConstraintVerdict::Sat,
152 Checker::checkConstraints(buildConstraintSet(x != manager->integer(0)), buildRegion({{
"x", {1.0, 2.0}}}),
"test").verdict);
155 auto violated = Checker::checkConstraints(buildConstraintSet(x != manager->integer(0)), buildRegion({{
"x", {-1.0, 1.0}}}),
"test");
156 EXPECT_EQ(storm::pars::analysis::ConstraintVerdict::Unsat, violated.verdict);
157 ASSERT_TRUE(violated.witness.has_value());
158 EXPECT_NEAR(0.0, asDouble(violated.witness->at(variableByName(
"x"))), 1e-9);
161TEST(RegionConstraintCheckerTest, EqualityRequiresIdenticallyZero) {
162 auto manager = std::make_shared<storm::expressions::ExpressionManager>();
163 auto x = variable(manager,
"x");
164 Region region = buildRegion({{
"x", {0.0, 1.0}}});
167 auto violated = Checker::checkConstraints(buildConstraintSet(x == manager->integer(0)), region,
"test");
168 EXPECT_EQ(storm::pars::analysis::ConstraintVerdict::Unsat, violated.verdict);
169 ASSERT_TRUE(violated.witness.has_value());
170 EXPECT_NEAR(1.0, asDouble(violated.witness->at(variableByName(
"x"))), 1e-9);
173 EXPECT_EQ(storm::pars::analysis::ConstraintVerdict::Sat,
174 Checker::checkConstraints(buildConstraintSet(x - x == manager->integer(0)), region,
"test").verdict);
177TEST(RegionConstraintCheckerTest, BooleanLiteralConstraintsAreDecided) {
178 auto manager = std::make_shared<storm::expressions::ExpressionManager>();
179 Region region = buildRegion({{
"x", {0.0, 1.0}}});
182 auto violated = Checker::checkConstraints(buildConstraintSet(manager->boolean(
false)), region,
"test");
183 EXPECT_EQ(storm::pars::analysis::ConstraintVerdict::Unsat, violated.verdict);
184 EXPECT_TRUE(violated.violatedConstraint.has_value());
185 ASSERT_TRUE(violated.witness.has_value());
186 EXPECT_TRUE(violated.witness->count(variableByName(
"x")) > 0);
189 auto satisfied = Checker::checkConstraints(buildConstraintSet(manager->boolean(
true)), region,
"test");
190 EXPECT_EQ(storm::pars::analysis::ConstraintVerdict::Sat, satisfied.verdict);
191 EXPECT_FALSE(satisfied.violatedConstraint.has_value());
194TEST(RegionConstraintCheckerTest, CancellingCoefficientDoesNotRequireVariableInRegion) {
195 auto manager = std::make_shared<storm::expressions::ExpressionManager>();
196 auto x = variable(manager,
"x");
199 auto result = Checker::checkConstraints(buildConstraintSet(x - x <= manager->integer(0)), buildRegion({{
"y", {0.0, 1.0}}}),
"test");
200 EXPECT_EQ(storm::pars::analysis::ConstraintVerdict::Sat, result.verdict);
203TEST(RegionConstraintCheckerTest, NonLinearConstraintIsUndecided) {
204 auto manager = std::make_shared<storm::expressions::ExpressionManager>();
205 auto x = variable(manager,
"x");
206 auto y = variable(manager,
"y");
207 Region region = buildRegion({{
"x", {0.0, 1.0}}, {
"y", {0.0, 1.0}}});
210 auto result = Checker::checkConstraints(buildConstraintSet(x * y <= manager->rational(0.25)), region,
"test");
211 EXPECT_EQ(storm::pars::analysis::ConstraintVerdict::Unknown, result.verdict);
212 EXPECT_FALSE(result.undecidedConstraints.empty());
213 EXPECT_FALSE(result.holdsThroughout());
216TEST(RegionConstraintCheckerTest, IfThenElseIsUndecided) {
217 auto manager = std::make_shared<storm::expressions::ExpressionManager>();
218 auto x = variable(manager,
"x");
219 Region region = buildRegion({{
"x", {0.0, 1.0}}});
221 auto condition = x <= manager->rational(0.5);
225 auto result = Checker::checkConstraints(buildConstraintSet(constraint), region,
"test");
226 EXPECT_EQ(storm::pars::analysis::ConstraintVerdict::Unknown, result.verdict);
229TEST(RegionConstraintCheckerTest, MissingVariableIsRejected) {
230 auto manager = std::make_shared<storm::expressions::ExpressionManager>();
231 auto x = variable(manager,
"x");
232 auto z = variable(manager,
"z");
235 EXPECT_THROW(Checker::checkConstraints(buildConstraintSet(x + z <= manager->integer(1)), buildRegion({{
"x", {0.0, 1.0}}}),
"test"),
236 storm::exceptions::InvalidArgumentException);
239TEST(RegionConstraintCheckerTest, RealModelConstraintsAreDecidedExactly) {
241 GTEST_SKIP() <<
"Z3 not available.";
248 EXPECT_EQ(1ul, formulas.size());
254 EXPECT_FALSE(collector.getWellformedConstraints().empty());
255 EXPECT_FALSE(collector.getGraphPreservingConstraints().empty());
256 Checker checker(collector);
260 auto unitSquare = buildRegion({{
"p", {0.0, 1.0}}, {
"q", {0.0, 1.0}}});
261 auto wellformed = checker.checkWellformedness(unitSquare);
262 EXPECT_EQ(storm::pars::analysis::ConstraintVerdict::Sat, wellformed.verdict);
263 EXPECT_TRUE(wellformed.undecidedConstraints.empty());
264 EXPECT_TRUE(wellformed.holdsThroughout());
270 auto graphPreserving = checker.checkGraphPreservation(unitSquare);
271 EXPECT_EQ(storm::pars::analysis::ConstraintVerdict::Unsat, graphPreserving.verdict);
272 EXPECT_FALSE(graphPreserving.holdsThroughout());
273 EXPECT_TRUE(graphPreserving.violatedConstraint.has_value());
274 EXPECT_TRUE(graphPreserving.witness.has_value());
277 auto interior = buildRegion({{
"p", {0.1, 0.9}}, {
"q", {0.1, 0.9}}});
278 EXPECT_TRUE(checker.isWellDefined(interior));
279 EXPECT_TRUE(checker.isGraphPreserving(interior));
282 auto outside = buildRegion({{
"p", {2.0, 3.0}}, {
"q", {2.0, 3.0}}});
283 EXPECT_FALSE(checker.isWellDefined(outside));
284 EXPECT_TRUE(Checker::checkConstraints(collector.getGraphPreservingConstraints(), outside,
"test").holdsThroughout());
285 auto outsideResult = checker.checkGraphPreservation(outside);
286 EXPECT_EQ(storm::pars::analysis::ConstraintVerdict::Unsat, outsideResult.verdict);
287 EXPECT_FALSE(checker.isGraphPreserving(outside));
288 EXPECT_TRUE(outsideResult.violatedConstraint.has_value());
289 EXPECT_TRUE(outsideResult.witness.has_value());
292TEST(RegionConstraintCheckerTest, NonAffineModelConstraintsAreNeverClaimedToHold) {
294 GTEST_SKIP() <<
"Z3 not available.";
301 if (formulas.size() != 1ul) {
302 GTEST_SKIP() <<
"Could not extract a formula from the model.";
309 Checker checker(collector);
312 auto result = checker.checkWellformedness(buildRegion({{
"p", {0.0, 1.0}}, {
"q", {0.0, 1.0}}}));
313 EXPECT_FALSE(result.undecidedConstraints.empty());
314 EXPECT_FALSE(result.holdsThroughout());
TEST(RegionConstraintCheckerTest, AffineConstraintHoldsThroughout)
Class to collect constraints on parametric Markov chains.
std::vector< storm::expressions::Expression > ConstraintSet
The type of the collected constraints.
std::shared_ptr< storm::models::sparse::Model< ValueType, RewardModelType > > build()
Convert the program given at construction time to an abstract model.
This class represents a discrete-time Markov chain.
Checks whether the constraints collected for a parametric model hold over an entire rectangular regio...
void checkValidity(Program::ValidityCheckLevel lvl=Program::ValidityCheckLevel::READYFORPROCESSING) const
Checks the validity of the program.
std::vector< storm::jani::Property > parsePropertiesForPrismProgram(std::string const &inputString, storm::prism::Program const &program, boost::optional< std::set< std::string > > const &propertyFilter)
storm::prism::Program parseProgram(std::string const &filename, bool prismCompatibility, bool simplify)
std::vector< std::shared_ptr< storm::logic::Formula const > > extractFormulasFromProperties(std::vector< storm::jani::Property > const &properties)
Expression ite(Expression const &condition, Expression const &thenExpression, Expression const &elseExpression)
storm::builder::BuilderOptions NextStateGeneratorOptions
storm::storage::ParameterRegion< storm::RationalFunction > RationalFunctionRegion
RationalFunctionRegion::CoefficientType CoefficientType
SettingsManager const & manager()
Retrieves the settings manager.
TargetType convertNumber(SourceType const &number)
carl::Variable RationalFunctionVariable
void clearRFVariablePool()
Clears the global pool of rational-function variables.