Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
VariableInformation.cpp
Go to the documentation of this file.
2
5
11
17
18#include <cmath>
19
20namespace storm {
21namespace generator {
22
27
41
47
51
64uint64_t getBitWidthLowerUpperBound(bool const& hasLowerBound, int64_t& lowerBound, bool const& hasUpperBound, int64_t& upperBound,
65 uint64_t const& reservedBitsForUnboundedVariables) {
66 if (hasLowerBound) {
67 if (hasUpperBound) {
68 STORM_LOG_THROW(lowerBound <= upperBound, storm::exceptions::WrongFormatException, "Lower bound must not be above upper bound.");
69 // We do not have to set any bounds in this case.
70 // Return the number of bits required to store all the values between lower and upper bound
71 return static_cast<uint64_t>(std::ceil(std::log2(upperBound - lowerBound + 1)));
72 } else {
73 // We only have a lower bound. Find the largest upper bound we can store with the given number of bits.
74 upperBound = lowerBound + ((1ll << reservedBitsForUnboundedVariables) - 1);
75 }
76 } else {
77 if (hasUpperBound) {
78 // We only have an upper bound. Find the smallest lower bound we can store with the given number of bits
79 lowerBound = upperBound - ((1ll << reservedBitsForUnboundedVariables) - 1);
80 } else {
81 // We neither have a lower nor an upper bound. Take the usual n-bit integer values for lower/upper bounds
82 lowerBound = -(1ll << (reservedBitsForUnboundedVariables - 1)); // = -2^(reservedBits-1)
83 upperBound = (1ll << (reservedBitsForUnboundedVariables - 1)) - 1; // = 2^(reservedBits-1) - 1
84 }
85 }
86 // If we reach this point, it means that the variable is unbounded.
87 // Lets check for potential overflows.
88 STORM_LOG_THROW(lowerBound <= upperBound, storm::exceptions::WrongFormatException,
89 "Lower bound must not be above upper bound. Has there been an integer over-/underflow?");
90 // By choice of the lower/upper bound, the number of reserved bits must coincide with the bitwidth
91 STORM_LOG_ASSERT(reservedBitsForUnboundedVariables == static_cast<uint64_t>(std::ceil(std::log2(upperBound - lowerBound + 1))),
92 "Unexpected bitwidth for unbounded variable.");
93 return reservedBitsForUnboundedVariables;
94}
95
96VariableInformation::VariableInformation(storm::prism::Program const& program, uint64_t reservedBitsForUnboundedVariables, bool outOfBoundsState)
97 : totalBitOffset(0) {
98 if (outOfBoundsState) {
99 outOfBoundsBit.emplace(program.getManager().declareBooleanVariable("_OutOfBoundsBit"), totalBitOffset, true, false);
101 }
102 for (auto const& booleanVariable : program.getGlobalBooleanVariables()) {
103 booleanVariables.emplace_back(booleanVariable.getExpressionVariable(), totalBitOffset, true, booleanVariable.isObservable());
104 ++totalBitOffset;
105 }
106 for (auto const& integerVariable : program.getGlobalIntegerVariables()) {
107 int64_t lowerBound, upperBound;
108 if (integerVariable.hasLowerBoundExpression()) {
109 lowerBound = integerVariable.getLowerBoundExpression().evaluateAsInt();
110 }
111 if (integerVariable.hasUpperBoundExpression()) {
112 upperBound = integerVariable.getUpperBoundExpression().evaluateAsInt();
113 }
114 uint64_t bitwidth = getBitWidthLowerUpperBound(integerVariable.hasLowerBoundExpression(), lowerBound, integerVariable.hasUpperBoundExpression(),
115 upperBound, reservedBitsForUnboundedVariables);
116 integerVariables.emplace_back(integerVariable.getExpressionVariable(), lowerBound, upperBound, totalBitOffset, bitwidth, true,
117 integerVariable.isObservable(), !integerVariable.hasLowerBoundExpression() || !integerVariable.hasUpperBoundExpression());
118 totalBitOffset += bitwidth;
119 }
120 for (auto const& module : program.getModules()) {
121 for (auto const& booleanVariable : module.getBooleanVariables()) {
122 booleanVariables.emplace_back(booleanVariable.getExpressionVariable(), totalBitOffset, false, booleanVariable.isObservable());
123 ++totalBitOffset;
124 }
125 for (auto const& integerVariable : module.getIntegerVariables()) {
126 int64_t lowerBound, upperBound;
127 if (integerVariable.hasLowerBoundExpression()) {
128 lowerBound = integerVariable.getLowerBoundExpression().evaluateAsInt();
129 }
130 if (integerVariable.hasUpperBoundExpression()) {
131 upperBound = integerVariable.getUpperBoundExpression().evaluateAsInt();
132 }
133 uint64_t bitwidth = getBitWidthLowerUpperBound(integerVariable.hasLowerBoundExpression(), lowerBound, integerVariable.hasUpperBoundExpression(),
134 upperBound, reservedBitsForUnboundedVariables);
135 integerVariables.emplace_back(integerVariable.getExpressionVariable(), lowerBound, upperBound, totalBitOffset, bitwidth, false,
136 integerVariable.isObservable(),
137 !integerVariable.hasLowerBoundExpression() || !integerVariable.hasUpperBoundExpression());
138 totalBitOffset += bitwidth;
139 }
140 }
141 for (auto const& oblab : program.getObservationLabels()) {
142 storm::expressions::Variable obVar;
143 if (program.getManager().hasVariable(oblab.getName())) {
144 obVar = program.getManager().getVariable(oblab.getName());
145 auto const& obPredicate = oblab.getStatePredicateExpression();
146 // Reaching this point means that the observation label is already known as a variable.
147 if (program.hasFormula(oblab.getName())) {
148 STORM_LOG_ASSERT(!program.getAllExpressionVariables(true).contains(obVar),
149 "There appears to be a formula and a variable with the same name " << obVar.getName() << " which is not expected.");
150 // The variable is actually a formula; We just need to check whether the type matches
151 // If the type doesn't match, we cannot use the expression variable for both, the formula and the observation label.
152 auto const& f = program.getFormula(oblab.getName());
153 STORM_LOG_THROW(f.getType() == obPredicate.getType(), storm::exceptions::NotSupportedException,
154 "Observation valuations for '"
155 << oblab
156 << " is not supported since a formula with the same name is already known and its expression has a different type.");
157 } else {
158 // There is already a known variable with the same name as the observation label. The only case we accept is a declaration of the form
159 // `observable "x" = x;`
160 STORM_LOG_THROW(obPredicate.isVariable() && obPredicate.getBaseExpression().asVariableExpression().getVariable() == obVar,
161 storm::exceptions::NotSupportedException,
162 "Observation valuations for '" << oblab << " is not supported since a variable '" << oblab.getName()
163 << "' is already known and the expression '" << oblab.getStatePredicateExpression()
164 << "' is not equal to it.");
165 }
166 } else {
167 obVar = program.getManager().declareVariable(oblab.getName(), oblab.getStatePredicateExpression().getType());
168 }
169 observationLabels.emplace_back(obVar);
170 }
171
172 sortVariables();
173}
174
176 std::vector<std::reference_wrapper<storm::jani::Automaton const>> const& parallelAutomata,
177 uint64_t reservedBitsForUnboundedVariables, bool outOfBoundsState)
178 : totalBitOffset(0) {
179 // Check that the model does not contain non-transient real variables.
180 STORM_LOG_THROW(!model.getGlobalVariables().containsNonTransientRealVariables(), storm::exceptions::InvalidArgumentException,
181 "Cannot build model from JANI model that contains global non-transient real variables.");
182 for (auto const& automaton : model.getAutomata()) {
183 STORM_LOG_THROW(!automaton.getVariables().containsNonTransientRealVariables(), storm::exceptions::InvalidArgumentException,
184 "Cannot build model from JANI model that contains non-transient real variables in automaton '" << automaton.getName() << "'.");
185 }
186
187 if (outOfBoundsState) {
188 std::string outOfBoundsVarName = "_OutOfBoundsBit";
189 while (model.getManager().hasVariable(outOfBoundsVarName)) {
190 outOfBoundsVarName += "_";
191 }
192 outOfBoundsBit.emplace(model.getManager().declareBooleanVariable(outOfBoundsVarName), totalBitOffset, true, false);
194 }
195
196 createVariablesForVariableSet(model.getGlobalVariables(), reservedBitsForUnboundedVariables, true);
197
198 for (auto const& automatonRef : parallelAutomata) {
199 createVariablesForAutomaton(automatonRef.get(), reservedBitsForUnboundedVariables);
200 }
201
202 sortVariables();
203}
204
207 // Find for each replaced array variable the corresponding references in this variable information
208 for (auto const& arrayVariable : arrayEliminatorData.eliminatedArrayVariables) {
209 if (!arrayVariable->isTransient()) {
210 auto findRes = arrayEliminatorData.replacements.find(arrayVariable->getExpressionVariable());
211 STORM_LOG_ASSERT(findRes != arrayEliminatorData.replacements.end(), "No replacement for array variable.");
212 auto const& replacements = findRes->second;
213 auto const& innerType = arrayVariable->getType().asArrayType().getBaseTypeRecursive();
214 if (innerType.isBasicType() && innerType.asBasicType().isBooleanType()) {
215 auto replInfo = convertArrayReplacement(replacements, booleanVariables);
216 this->arrayVariableToElementInformations.emplace(arrayVariable->getExpressionVariable(), std::move(replInfo));
217 } else if ((innerType.isBasicType() && innerType.asBasicType().isIntegerType()) ||
218 (innerType.isBoundedType() && innerType.asBoundedType().isIntegerType())) {
219 auto replInfo = convertArrayReplacement(replacements, integerVariables);
220 this->arrayVariableToElementInformations.emplace(arrayVariable->getExpressionVariable(), std::move(replInfo));
221 } else {
222 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unhandled type of base variable.");
223 }
224 }
225 }
226}
227
229 std::vector<uint64_t> const& arrayIndexVector) const {
230 return booleanVariables[arrayVariableToElementInformations.at(arrayVariable).getVariableInformationIndex(arrayIndexVector)];
231}
232
234 std::vector<uint64_t> const& arrayIndexVector) const {
235 return integerVariables[arrayVariableToElementInformations.at(arrayVariable).getVariableInformationIndex(arrayIndexVector)];
236}
237
238void VariableInformation::createVariablesForAutomaton(storm::jani::Automaton const& automaton, uint64_t reservedBitsForUnboundedVariables) {
239 uint_fast64_t bitwidth = static_cast<uint_fast64_t>(std::ceil(std::log2(automaton.getNumberOfLocations())));
240 locationVariables.emplace_back(automaton.getLocationExpressionVariable(), automaton.getNumberOfLocations() - 1, totalBitOffset, bitwidth, true);
241 totalBitOffset += bitwidth;
242
243 createVariablesForVariableSet(automaton.getVariables(), reservedBitsForUnboundedVariables, false);
244}
245
246void VariableInformation::createVariablesForVariableSet(storm::jani::VariableSet const& variableSet, uint64_t reservedBitsForUnboundedVariables, bool global) {
247 for (auto const& variable : variableSet.getBooleanVariables()) {
248 if (!variable.isTransient()) {
249 booleanVariables.emplace_back(variable.getExpressionVariable(), totalBitOffset, global, true);
251 }
252 }
253 for (auto const& variable : variableSet.getBoundedIntegerVariables()) {
254 if (!variable.isTransient()) {
255 int64_t lowerBound, upperBound;
256 auto const& type = variable.getType().asBoundedType();
257 STORM_LOG_ASSERT(type.hasLowerBound() || type.hasUpperBound(), "Bounded integer variable has neither a lower nor an upper bound.");
258 if (type.hasLowerBound()) {
259 lowerBound = type.getLowerBound().evaluateAsInt();
260 }
261 if (type.hasUpperBound()) {
262 upperBound = type.getUpperBound().evaluateAsInt();
263 }
264 uint64_t bitwidth =
265 getBitWidthLowerUpperBound(type.hasLowerBound(), lowerBound, type.hasUpperBound(), upperBound, reservedBitsForUnboundedVariables);
266 integerVariables.emplace_back(variable.getExpressionVariable(), lowerBound, upperBound, totalBitOffset, bitwidth, global, true,
267 !type.hasLowerBound() || !type.hasUpperBound());
268 totalBitOffset += bitwidth;
269 }
270 }
271 for (auto const& variable : variableSet.getUnboundedIntegerVariables()) {
272 if (!variable.isTransient()) {
273 int64_t lowerBound, upperBound;
274 uint64_t bitwidth = getBitWidthLowerUpperBound(false, lowerBound, false, upperBound, reservedBitsForUnboundedVariables);
275 integerVariables.emplace_back(variable.getExpressionVariable(), lowerBound, upperBound, totalBitOffset, bitwidth, global, true, true);
276 totalBitOffset += reservedBitsForUnboundedVariables;
277 }
278 }
279}
280
281uint_fast64_t VariableInformation::getTotalBitOffset(bool roundTo64Bit) const {
282 uint_fast64_t result = totalBitOffset;
283 if (roundTo64Bit & ((result & ((1ull << 6) - 1)) != 0)) {
284 result = ((result >> 6) + 1) << 6;
285 }
286 return result;
287}
288
290 return outOfBoundsBit.has_value();
291}
292
294 STORM_LOG_ASSERT(hasOutOfBoundsBit(), "Expected out-of-bounds bit.");
295 return outOfBoundsBit->bitOffset;
296}
297
298void VariableInformation::sortVariables() {
299 // Sort the variables so we can make some assumptions when iterating over them (in the next-state generators).
300 std::sort(booleanVariables.begin(), booleanVariables.end(),
301 [](BooleanVariableInformation const& a, BooleanVariableInformation const& b) { return a.variable < b.variable; });
302 std::sort(integerVariables.begin(), integerVariables.end(),
303 [](IntegerVariableInformation const& a, IntegerVariableInformation const& b) { return a.variable < b.variable; });
304}
305} // namespace generator
306} // namespace storm
Variable declareBooleanVariable(std::string const &name, bool auxiliary=false)
Declares a new boolean variable with a name that must not yet exist and its corresponding type.
bool hasVariable(std::string const &name) const
Retrieves whether a variable with the given name is known to the manager.
VariableSet & getVariables()
Retrieves the variables of this automaton.
Definition Automaton.cpp:59
storm::expressions::Variable const & getLocationExpressionVariable() const
Retrieves the expression variable that represents the location of this automaton.
uint64_t getNumberOfLocations() const
Retrieves the number of locations.
storm::expressions::ExpressionManager & getManager() const
Retrieves the expression manager responsible for the expressions in the model.
Definition Model.cpp:109
VariableSet & getGlobalVariables()
Retrieves the variables of this automaton.
Definition Model.cpp:717
std::vector< Automaton > & getAutomata()
Retrieves the automata of the model.
Definition Model.cpp:868
detail::Variables< Variable > getBoundedIntegerVariables()
Retrieves the bounded integer variables in this set.
bool containsNonTransientRealVariables() const
Retrieves whether the set of variables contains a non-transient real variable.
detail::Variables< Variable > getUnboundedIntegerVariables()
Retrieves the unbounded integer variables in this set.
detail::Variables< Variable > getBooleanVariables()
Retrieves the boolean variables in this set.
std::vector< BooleanVariable > const & getGlobalBooleanVariables() const
Retrieves the global boolean variables of the program.
Definition Program.cpp:478
storm::expressions::ExpressionManager & getManager() const
Retrieves the manager responsible for the expressions of this program.
Definition Program.cpp:2388
std::vector< IntegerVariable > const & getGlobalIntegerVariables() const
Retrieves the global integer variables of the program.
Definition Program.cpp:482
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:28
uint64_t getBitWidthLowerUpperBound(bool const &hasLowerBound, int64_t &lowerBound, bool const &hasUpperBound, int64_t &upperBound, uint64_t const &reservedBitsForUnboundedVariables)
Small helper function that sets unspecified lower/upper bounds for an integer variable based on the p...
ArrayVariableReplacementInformation convertArrayReplacement(typename storm::jani::ArrayEliminatorData::Replacement const &replacement, InfoType const &relevantVariableInfo)
BooleanVariableInformation(storm::expressions::Variable const &variable, uint_fast64_t bitOffset, bool global, bool observable)
IntegerVariableInformation(storm::expressions::Variable const &variable, int_fast64_t lowerBound, int_fast64_t upperBound, uint_fast64_t bitOffset, uint_fast64_t bitWidth, bool global=false, bool observable=true, bool forceOutOfBoundsCheck=false)
LocationVariableInformation(storm::expressions::Variable const &variable, uint64_t highestValue, uint_fast64_t bitOffset, uint_fast64_t bitWidth, bool observable)
ObservationLabelInformation(storm::expressions::Variable const &variable)
uint_fast64_t getTotalBitOffset(bool roundTo64Bit=false) const
std::unordered_map< storm::expressions::Variable, ArrayVariableReplacementInformation > arrayVariableToElementInformations
Replacements for each array variable.
uint_fast64_t totalBitOffset
The total bit offset over all variables.
void registerArrayVariableReplacements(storm::jani::ArrayEliminatorData const &arrayEliminatorData)
std::optional< BooleanVariableInformation > outOfBoundsBit
std::vector< IntegerVariableInformation > integerVariables
The integer variables.
BooleanVariableInformation const & getBooleanArrayVariableReplacement(storm::expressions::Variable const &arrayVariable, std::vector< uint64_t > const &arrayIndexVector) const
std::vector< LocationVariableInformation > locationVariables
The location variables.
VariableInformation(storm::prism::Program const &program, uint64_t reservedBitsForUnboundedVariables, bool outOfBoundsState=false)
IntegerVariableInformation const & getIntegerArrayVariableReplacement(storm::expressions::Variable const &arrayVariable, std::vector< uint64_t > const &arrayIndexVector) const
std::vector< BooleanVariableInformation > booleanVariables
The boolean variables.
std::unordered_map< storm::expressions::Variable, Replacement > replacements
std::vector< std::shared_ptr< Variable > > eliminatedArrayVariables