Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
GraphConditionsTest.cpp
Go to the documentation of this file.
1#include "storm-config.h"
2#include "test/storm_gtest.h"
3
4#include <algorithm>
5#include <cstdio>
6#include <fstream>
7#include <set>
8#include <sstream>
9#include <vector>
10
14#include "storm/api/storm.h"
17#include "storm/io/file.h"
21
22namespace {
23
27std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> buildParametricDtmc(std::string const& programFile, std::string const& formulaAsString) {
29 program.checkValidity();
30 std::vector<std::shared_ptr<storm::logic::Formula const>> formulas =
32 EXPECT_EQ(1ul, formulas.size());
33 storm::generator::NextStateGeneratorOptions options(*formulas.front());
35}
36
40std::vector<std::string> asStringsInDiscoveryOrder(storm::analysis::ConstraintCollector::ConstraintSet const& constraints) {
41 std::vector<std::string> result;
42 for (auto const& entry : constraints) {
43 result.push_back(entry.toString());
44 }
45 return result;
46}
47
53std::vector<std::string> asSortedStrings(storm::analysis::ConstraintCollector::ConstraintSet const& constraints) {
54 auto result = asStringsInDiscoveryOrder(constraints);
55 std::sort(result.begin(), result.end());
56 return result;
57}
58
59} // namespace
60
61class GraphConditionsTest : public ::testing::Test {
62 protected:
63 void SetUp() override {
64#ifndef STORM_HAVE_Z3
65 GTEST_SKIP() << "Z3 not available.";
66#endif
68 }
69
70 void TearDown() override {
72 }
73};
74
75TEST_F(GraphConditionsTest, PolynomialDenominators) {
76 auto dtmc = buildParametricDtmc(STORM_TEST_RESOURCES_DIR "/pdtmc/only_p.pm", "P=? [F s=1]");
77
79
80 // The only parameter must be reported.
81 std::vector<storm::RationalFunctionVariable> variables(collector.getVariables().begin(), collector.getVariables().end());
82 ASSERT_EQ(1ul, variables.size());
83 EXPECT_EQ("p", variables.front().name());
84
85 // The transitions are the symbolic values p and 1 - p, and both denominators are positive constants. We
86 // therefore expect the constraints 0 <= p <= 1 and 0 <= 1 - p <= 1. Note that the row sums to 1 identically,
87 // so no row-sum constraint is collected.
88 //
89 // Relations are normalized so that the polynomial never occurs on the greater side, and the upper bound is
90 // phrased as a relation of the difference to zero. Consequently, the constraints derived from p and from
91 // 1 - p coincide pairwise and are collected only once each.
92 std::vector<std::string> expectedWellformed{"((-1 + p) <= 0)", "(-(p) <= 0)"};
93 EXPECT_EQ(expectedWellformed, asSortedStrings(collector.getWellformedConstraints()));
94
95 // Both transitions must stay non-zero, which for p and 1 - p amounts to p != 0 and 1 - p != 0.
96 std::vector<std::string> expectedGraphPreserving{"((1 - p) != 0)", "(p != 0)"};
97 EXPECT_EQ(expectedGraphPreserving, asSortedStrings(collector.getGraphPreservingConstraints()));
98}
99
100TEST_F(GraphConditionsTest, SymbolicDenominators) {
101 auto dtmc = buildParametricDtmc(STORM_TEST_RESOURCES_DIR "/pdtmc/only_rational_denominator.pm", "P=? [F s=2]");
102
104
105 std::vector<storm::RationalFunctionVariable> variables(collector.getVariables().begin(), collector.getVariables().end());
106 ASSERT_EQ(2ul, variables.size());
107
108 // Here both denominators are the symbolic value 1 - p + q, so non-negativity cannot be reduced to a sign
109 // constraint on the nominator. Instead, the denominator must be constrained to be non-zero, and the sign of
110 // each nominator must follow the sign of the denominator.
111 std::vector<std::string> expectedWellformed{"((((-1 + p) - q) < 0) ? ((-1 + p) <= 0) : ((1 - p) <= 0))", "((((-1 + p) - q) < 0) ? (-(q) <= 0) : (q <= 0))",
112 "(((1 - p) + q) != 0)"};
113 EXPECT_EQ(expectedWellformed, asSortedStrings(collector.getWellformedConstraints()));
114
115 std::vector<std::string> expectedGraphPreserving{"((1 - p) != 0)", "(q != 0)"};
116 EXPECT_EQ(expectedGraphPreserving, asSortedStrings(collector.getGraphPreservingConstraints()));
117}
118
119TEST_F(GraphConditionsTest, ConstraintsAreDeduplicated) {
120 auto dtmc = buildParametricDtmc(STORM_TEST_RESOURCES_DIR "/pdtmc/only_p.pm", "P=? [F s=1]");
121
123
124 // The same constraint must not be collected twice, even though it can be derived from several transitions.
125 // The parametric die model has seven transitions guarded by the same two parameters.
126 auto dtmcWithManyTransitions = buildParametricDtmc(STORM_TEST_RESOURCES_DIR "/pdtmc/parametric_die_2.pm", "P=? [F s=7]");
127 storm::analysis::ConstraintCollector dieCollector(*dtmcWithManyTransitions);
128
129 for (auto const& constraints : {asSortedStrings(collector.getWellformedConstraints()), asSortedStrings(collector.getGraphPreservingConstraints()),
130 asSortedStrings(dieCollector.getWellformedConstraints()), asSortedStrings(dieCollector.getGraphPreservingConstraints())}) {
131 std::set<std::string> distinct(constraints.begin(), constraints.end());
132 EXPECT_EQ(constraints.size(), distinct.size());
133 }
134}
135
136TEST_F(GraphConditionsTest, ConstraintsAreOrderedDeterministically) {
137 auto dtmc = buildParametricDtmc(STORM_TEST_RESOURCES_DIR "/pdtmc/only_p.pm", "P=? [F s=1]");
138
139 // Collecting the constraints repeatedly from equal models must yield the same order.
140 storm::analysis::ConstraintCollector firstCollector(*dtmc);
141 storm::analysis::ConstraintCollector secondCollector(*dtmc);
142
143 EXPECT_EQ(asStringsInDiscoveryOrder(firstCollector.getWellformedConstraints()), asStringsInDiscoveryOrder(secondCollector.getWellformedConstraints()));
144 EXPECT_EQ(asStringsInDiscoveryOrder(firstCollector.getGraphPreservingConstraints()),
145 asStringsInDiscoveryOrder(secondCollector.getGraphPreservingConstraints()));
146
147 // The constraints are collected in the order in which the model traversal discovers them, which is not their
148 // lexicographic order. Pinning the expected order keeps this test from passing vacuously.
149 std::vector<std::string> expectedWellformed{"(-(p) <= 0)", "((-1 + p) <= 0)"};
150 std::vector<std::string> expectedGraphPreserving{"(p != 0)", "((1 - p) != 0)"};
151
152 EXPECT_EQ(expectedWellformed, asStringsInDiscoveryOrder(firstCollector.getWellformedConstraints()));
153 EXPECT_EQ(expectedGraphPreserving, asStringsInDiscoveryOrder(firstCollector.getGraphPreservingConstraints()));
154
155 // Guard the guard: if the discovery order ever happened to coincide with the lexicographic one, the assertions
156 // above could no longer detect a collector that reorders or sorts its constraints.
157 auto sortedWellformed = asSortedStrings(firstCollector.getWellformedConstraints());
158 EXPECT_NE(expectedWellformed, sortedWellformed);
159}
160
162 auto dtmc = buildParametricDtmc(STORM_TEST_RESOURCES_DIR "/pdtmc/only_rational_denominator.pm", "P=? [F s=2]");
163
165
166 // Use one of the model's symbolic transition values as the solution function.
168 for (auto const& entry : dtmc->getTransitionMatrix()) {
169 if (!storm::utility::isConstant(entry.getValue())) {
170 resultValue = entry.getValue();
171 break;
172 }
173 }
174
175 std::string temporaryFile = "graph-conditions-export.txt";
177
178 std::ifstream input(temporaryFile);
179 ASSERT_TRUE(input.good());
180 std::stringstream buffer;
181 buffer << input.rdbuf();
182 std::string content = buffer.str();
184
185 // The export must mention the parameters, the result, and both kinds of constraints.
186 EXPECT_NE(std::string::npos, content.find("$Parameters: p; q; \n"));
187 EXPECT_NE(std::string::npos, content.find("$Result: "));
188 EXPECT_NE(std::string::npos, content.find("$Well-formed Constraints: \n"));
189 EXPECT_NE(std::string::npos, content.find("$Graph-preserving Constraints: \n"));
190
191 // The export must list exactly the collected constraints. The collector returns them in the order in which it
192 // discovered them, so both sides are sorted before comparing.
193 std::vector<std::string> wellformedLines;
194 std::string currentSection;
195 std::istringstream contentStream(content);
196 std::string line;
197 while (std::getline(contentStream, line)) {
198 if (line == "$Well-formed Constraints: ") {
199 currentSection = "wellformed";
200 } else if (line == "$Graph-preserving Constraints: ") {
201 currentSection = "graphPreserving";
202 } else if (!line.empty() && currentSection == "wellformed") {
203 wellformedLines.push_back(line);
204 }
205 }
206 std::sort(wellformedLines.begin(), wellformedLines.end());
207 EXPECT_EQ(asSortedStrings(collector.getWellformedConstraints()), wellformedLines);
208
209 std::remove(temporaryFile.c_str());
210}
TEST_F(GraphConditionsTest, PolynomialDenominators)
Class to collect constraints on parametric Markov chains.
ConstraintSet const & getGraphPreservingConstraints() const
Returns the set of graph-preserving constraints.
std::set< storm::RationalFunctionVariable > const & getVariables() const
Returns the set of variables in the model.
ConstraintSet const & getWellformedConstraints() const
Returns the set of wellformed-ness constraints.
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.
Definition Dtmc.h:16
void checkValidity(Program::ValidityCheckLevel lvl=Program::ValidityCheckLevel::READYFORPROCESSING) const
Checks the validity of the program.
Definition Program.cpp:1252
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)
storm::builder::BuilderOptions NextStateGeneratorOptions
void closeFile(std::ofstream &stream)
Close the given file after writing.
Definition file.h:47
void exportParametricResultToFile(std::optional< storm::RationalFunction > result, storm::OptionalRef< storm::analysis::ConstraintCollector const > const &constraintCollector, std::string const &path)
Definition export.cpp:27
bool isConstant(ValueType const &)
ValueType zero()
Definition constants.cpp:26
carl::RationalFunction< Polynomial, true > RationalFunction
void clearRFVariablePool()
Clears the global pool of rational-function variables.