Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
JaniNextStateGenerator.cpp
Go to the documentation of this file.
2
31
32namespace storm {
33namespace generator {
34
35template<typename ValueType, typename StateType>
37 : JaniNextStateGenerator(model.substituteConstantsFunctionsTranscendentals(), options, false) {
38 // Intentionally left empty.
39}
40
41template<typename ValueType, typename StateType>
43 : NextStateGenerator<ValueType, StateType>(model.getExpressionManager(), options),
44 model(model),
45 rewardExpressions(),
46 hasStateActionRewards(false),
47 evaluateRewardExpressionsAtEdges(false),
48 evaluateRewardExpressionsAtDestinations(false) {
49 auto features = this->model.getModelFeatures();
54 // Eliminate arrays if necessary.
55 if (features.hasArrays()) {
56 arrayEliminatorData = this->model.eliminateArrays(true);
57 this->options.substituteExpressions([this](storm::expressions::Expression const& exp) { return arrayEliminatorData.transformExpression(exp); });
58 features.remove(storm::jani::ModelFeature::Arrays);
59 }
60 STORM_LOG_THROW(features.empty(), storm::exceptions::NotSupportedException,
61 "The explicit next-state generator does not support the following model feature(s): " << features.toString() << ".");
62 // Simplify the system compositions so that we can exclude the case where automata appear in the composition multiple times.
63 this->model.simplifyComposition();
64
65 // Get the reward expressions to be build. Also find out whether there is a non-trivial one.
66 bool hasNonTrivialRewardExpressions = false;
67 if (this->options.isBuildAllRewardModelsSet()) {
68 rewardExpressions = this->model.getAllRewardModelExpressions();
69 hasNonTrivialRewardExpressions = this->model.hasNonTrivialRewardExpression();
70 } else {
71 // Extract the reward models from the model based on the names we were given.
72 for (auto const& rewardModelName : this->options.getRewardModelNames()) {
73 rewardExpressions.emplace_back(rewardModelName, this->model.getRewardModelExpression(rewardModelName));
74 hasNonTrivialRewardExpressions = hasNonTrivialRewardExpressions || this->model.isNonTrivialRewardModelExpression(rewardModelName);
75 }
76 }
77 // If a transient variable has a non-zero default value, we also consider that non-trivial.
78 // In those cases, lifting edge destination assignments to the edges would mean that reward is collected twice:
79 // once at the edge (assigned value), once at the edge destinations (default value).
80 if (!hasNonTrivialRewardExpressions) {
81 for (auto const& rewExpr : rewardExpressions) {
82 STORM_LOG_ASSERT(rewExpr.second.isVariable(), "Expected trivial reward expression to be a variable. Got " << rewExpr.second << " instead.");
83 auto const& var = this->model.getGlobalVariables().getVariable(rewExpr.second.getBaseExpression().asVariableExpression().getVariable());
84 if (var.isTransient() && var.hasInitExpression() && !storm::utility::isZero(var.getInitExpression().evaluateAsRational())) {
85 hasNonTrivialRewardExpressions = true;
86 break;
87 }
88 }
89 }
90
91 // We try to lift the edge destination assignments to the edges as this reduces the number of evaluator calls.
92 // However, this will only be helpful if there are no assignment levels and only trivial reward expressions.
93 if (hasNonTrivialRewardExpressions || this->model.usesAssignmentLevels()) {
95 } else {
96 this->model.liftTransientEdgeDestinationAssignments(storm::jani::AssignmentLevelFinder().getLowestAssignmentLevel(this->model));
97 evaluateRewardExpressionsAtEdges = true;
98 }
99
100 // Create all synchronization-related information, e.g. the automata that are put in parallel.
101 this->createSynchronizationInformation();
102
103 // Now we are ready to initialize the variable information.
104 this->checkValid();
105 this->variableInformation =
106 VariableInformation(this->model, this->parallelAutomata, options.getReservedBitsForUnboundedVariables(), options.isAddOutOfBoundsStateSet());
107 this->variableInformation.registerArrayVariableReplacements(arrayEliminatorData);
108 this->transientVariableInformation = TransientVariableInformation<ValueType>(this->model, this->parallelAutomata);
109 this->transientVariableInformation.registerArrayVariableReplacements(arrayEliminatorData);
110 this->initializeSpecialStates();
111
112 // Create a proper evaluator.
113 this->evaluator = std::make_unique<storm::expressions::ExpressionEvaluator<ValueType>>(this->model.getManager());
114 this->transientVariableInformation.setDefaultValuesInEvaluator(*this->evaluator);
115
116 // Build the information structs for the reward models.
117 buildRewardModelInformation();
118
119 // If there are terminal states we need to handle, we now need to translate all labels to expressions.
120 if (this->options.hasTerminalStates()) {
121 for (auto const& expressionOrLabelAndBool : this->options.getTerminalStates()) {
122 if (expressionOrLabelAndBool.first.isExpression()) {
123 this->terminalStates.emplace_back(expressionOrLabelAndBool.first.getExpression(), expressionOrLabelAndBool.second);
124 } else {
125 // If it's a label, i.e. refers to a transient boolean variable we do some sanity checks first
126 if (!this->isSpecialLabel(expressionOrLabelAndBool.first.getLabel())) {
127 STORM_LOG_THROW(this->model.getGlobalVariables().hasVariable(expressionOrLabelAndBool.first.getLabel()),
128 storm::exceptions::InvalidArgumentException,
129 "Terminal states refer to illegal label '" << expressionOrLabelAndBool.first.getLabel() << "'.");
130
131 storm::jani::Variable const& variable = this->model.getGlobalVariables().getVariable(expressionOrLabelAndBool.first.getLabel());
133 storm::exceptions::InvalidArgumentException,
134 "Terminal states refer to non-boolean variable '" << expressionOrLabelAndBool.first.getLabel() << "'.");
135 STORM_LOG_THROW(variable.isTransient(), storm::exceptions::InvalidArgumentException,
136 "Terminal states refer to non-transient variable '" << expressionOrLabelAndBool.first.getLabel() << "'.");
137
138 this->terminalStates.emplace_back(variable.getExpressionVariable().getExpression(), expressionOrLabelAndBool.second);
139 }
140 }
141 }
142 }
143}
144
145template<typename ValueType, typename StateType>
153 // We do not add Functions as these should ideally be substituted before creating this generator.
154 // This is because functions may also occur in properties and the user of this class should take care of that.
155 return features;
156}
157
158template<typename ValueType, typename StateType>
160 auto features = model.getModelFeatures();
161 features.remove(storm::jani::ModelFeature::Arrays);
163 features.remove(storm::jani::ModelFeature::Functions); // can be substituted
167 if (!features.empty()) {
168 STORM_LOG_INFO("The model can not be build as it contains these unsupported features: " << features.toString());
169 return false;
170 }
171 // There probably are more cases where the model is unsupported. However, checking these is more involved.
172 // As this method is supposed to be a quick check, we just return true at this point.
173 return true;
174}
175
176template<typename ValueType, typename StateType>
178 switch (model.getModelType()) {
180 return ModelType::DTMC;
182 return ModelType::CTMC;
184 return ModelType::MDP;
186 return ModelType::MA;
187 default:
188 STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Invalid model type.");
189 }
190}
191
192template<typename ValueType, typename StateType>
194 return model.isDeterministicModel();
195}
196
197template<typename ValueType, typename StateType>
199 return model.isDiscreteTimeModel();
200}
201
202template<typename ValueType, typename StateType>
206
207template<typename ValueType, typename StateType>
208uint64_t JaniNextStateGenerator<ValueType, StateType>::getLocation(CompressedState const& state, LocationVariableInformation const& locationVariable) const {
209 if (locationVariable.bitWidth == 0) {
210 return 0;
211 } else {
212 return state.getAsInt(locationVariable.bitOffset, locationVariable.bitWidth);
213 }
214}
215
216template<typename ValueType, typename StateType>
217void JaniNextStateGenerator<ValueType, StateType>::setLocation(CompressedState& state, LocationVariableInformation const& locationVariable,
218 uint64_t locationIndex) const {
219 if (locationVariable.bitWidth != 0) {
220 state.setFromInt(locationVariable.bitOffset, locationVariable.bitWidth, locationIndex);
221 }
222}
223
224template<typename ValueType, typename StateType>
225std::vector<uint64_t> JaniNextStateGenerator<ValueType, StateType>::getLocations(CompressedState const& state) const {
226 std::vector<uint64_t> result(this->variableInformation.locationVariables.size());
227
228 auto resultIt = result.begin();
229 for (auto it = this->variableInformation.locationVariables.begin(), ite = this->variableInformation.locationVariables.end(); it != ite; ++it, ++resultIt) {
230 if (it->bitWidth == 0) {
231 *resultIt = 0;
232 } else {
233 *resultIt = state.getAsInt(it->bitOffset, it->bitWidth);
234 }
235 }
236
237 return result;
238}
239
240template<typename ValueType, typename StateType>
242 std::vector<StateType> initialStateIndices;
243
244 if (this->model.hasNonTrivialInitialStates()) {
245 // Prepare an SMT solver to enumerate all initial states.
247 std::unique_ptr<storm::solver::SmtSolver> solver = factory.create(model.getExpressionManager());
248
249 std::vector<storm::expressions::Expression> rangeExpressions = model.getAllRangeExpressions(this->parallelAutomata);
250 for (auto const& expression : rangeExpressions) {
251 solver->add(expression);
252 }
253 solver->add(model.getInitialStatesExpression(this->parallelAutomata));
254
255 // Proceed as long as the solver can still enumerate initial states.
257 // Create fresh state.
258 CompressedState initialState(this->variableInformation.getTotalBitOffset(true));
259
260 // Read variable assignment from the solution of the solver. Also, create an expression we can use to
261 // prevent the variable assignment from being enumerated again.
262 storm::expressions::Expression blockingExpression;
263 std::shared_ptr<storm::solver::SmtSolver::ModelReference> model = solver->getModel();
264 for (auto const& booleanVariable : this->variableInformation.booleanVariables) {
265 bool variableValue = model->getBooleanValue(booleanVariable.variable);
266 storm::expressions::Expression localBlockingExpression = variableValue ? !booleanVariable.variable : booleanVariable.variable;
267 blockingExpression = blockingExpression.isInitialized() ? blockingExpression || localBlockingExpression : localBlockingExpression;
268 initialState.set(booleanVariable.bitOffset, variableValue);
269 }
270 for (auto const& integerVariable : this->variableInformation.integerVariables) {
271 int_fast64_t variableValue = model->getIntegerValue(integerVariable.variable);
272 if (integerVariable.forceOutOfBoundsCheck || this->getOptions().isExplorationChecksSet()) {
273 STORM_LOG_THROW(variableValue >= integerVariable.lowerBound, storm::exceptions::WrongFormatException,
274 "The initial value for variable " << integerVariable.variable.getName() << " is lower than the lower bound.");
275 STORM_LOG_THROW(variableValue <= integerVariable.upperBound, storm::exceptions::WrongFormatException,
276 "The initial value for variable " << integerVariable.variable.getName() << " is higher than the upper bound.");
277 }
278 storm::expressions::Expression localBlockingExpression = integerVariable.variable != model->getManager().integer(variableValue);
279 blockingExpression = blockingExpression.isInitialized() ? blockingExpression || localBlockingExpression : localBlockingExpression;
280 initialState.setFromInt(integerVariable.bitOffset, integerVariable.bitWidth,
281 static_cast<uint_fast64_t>(variableValue - integerVariable.lowerBound));
282 }
283
284 // Gather iterators to the initial locations of all the automata.
285 std::vector<std::set<uint64_t>::const_iterator> initialLocationsIts;
286 std::vector<std::set<uint64_t>::const_iterator> initialLocationsItes;
287 for (auto const& automatonRef : this->parallelAutomata) {
288 auto const& automaton = automatonRef.get();
289 initialLocationsIts.push_back(automaton.getInitialLocationIndices().cbegin());
290 initialLocationsItes.push_back(automaton.getInitialLocationIndices().cend());
291 }
293 initialLocationsIts, initialLocationsItes,
294 [this, &initialState](uint64_t index, uint64_t value) { setLocation(initialState, this->variableInformation.locationVariables[index], value); },
295 [&stateToIdCallback, &initialStateIndices, &initialState]() {
296 // Register initial state.
297 StateType id = stateToIdCallback(initialState);
298 initialStateIndices.push_back(id);
299 return true;
300 });
301
302 // Block the current initial state to search for the next one.
303 if (!blockingExpression.isInitialized()) {
304 break;
305 }
306 solver->add(blockingExpression);
307 }
308
309 STORM_LOG_DEBUG("Enumerated " << initialStateIndices.size() << " initial states using SMT solving.");
310 } else {
311 // Create vectors holding all possible values
312 std::vector<std::vector<uint64_t>> allValues;
313 for (auto const& aRef : this->parallelAutomata) {
314 auto const& aInitLocs = aRef.get().getInitialLocationIndices();
315 allValues.emplace_back(aInitLocs.begin(), aInitLocs.end());
316 }
317 uint64_t locEndIndex = allValues.size();
318 for (auto const& intVar : this->variableInformation.integerVariables) {
319 STORM_LOG_ASSERT(intVar.lowerBound <= intVar.upperBound, "Expecting variable with non-empty set of possible values.");
320 // The value of integer variables is shifted so that 0 is always the smallest possible value
321 allValues.push_back(storm::utility::vector::buildVectorForRange<uint64_t>(static_cast<uint64_t>(0), intVar.upperBound + 1 - intVar.lowerBound));
322 }
323 uint64_t intEndIndex = allValues.size();
324 // For boolean variables we consider the values 0 and 1.
325 allValues.resize(allValues.size() + this->variableInformation.booleanVariables.size(),
326 std::vector<uint64_t>({static_cast<uint64_t>(0), static_cast<uint64_t>(1)}));
327
328 std::vector<std::vector<uint64_t>::const_iterator> its;
329 std::vector<std::vector<uint64_t>::const_iterator> ites;
330 for (auto const& valVec : allValues) {
331 its.push_back(valVec.cbegin());
332 ites.push_back(valVec.cend());
333 }
334
335 // Now create an initial state for each combination of values
336 CompressedState initialState(this->variableInformation.getTotalBitOffset(true));
338 its, ites,
339 [this, &initialState, &locEndIndex, &intEndIndex](uint64_t index, uint64_t value) {
340 // Set the value for the variable corresponding to the given index
341 if (index < locEndIndex) {
342 // Location variable
343 setLocation(initialState, this->variableInformation.locationVariables[index], value);
344 } else if (index < intEndIndex) {
345 // Integer variable
346 auto const& intVar = this->variableInformation.integerVariables[index - locEndIndex];
347 initialState.setFromInt(intVar.bitOffset, intVar.bitWidth, value);
348 } else {
349 // Boolean variable
350 STORM_LOG_ASSERT(index - intEndIndex < this->variableInformation.booleanVariables.size(), "Unexpected index.");
351 auto const& boolVar = this->variableInformation.booleanVariables[index - intEndIndex];
352 STORM_LOG_ASSERT(value <= 1u, "Unexpected value for boolean variable.");
353 initialState.set(boolVar.bitOffset, static_cast<bool>(value));
354 }
355 },
356 [&stateToIdCallback, &initialStateIndices, &initialState]() {
357 // Register initial state.
358 StateType id = stateToIdCallback(initialState);
359 initialStateIndices.push_back(id);
360 return true; // Keep on exploring
361 });
362 STORM_LOG_DEBUG("Enumerated " << initialStateIndices.size() << " initial states using brute force enumeration.");
363 }
364 return initialStateIndices;
365}
366
367template<typename ValueType, typename StateType>
368void JaniNextStateGenerator<ValueType, StateType>::applyUpdate(CompressedState& state, storm::jani::EdgeDestination const& destination,
369 storm::generator::LocationVariableInformation const& locationVariable, int64_t assignmentLevel,
370 storm::expressions::ExpressionEvaluator<ValueType> const& expressionEvaluator) {
371 // Update the location of the state.
372 setLocation(state, locationVariable, destination.getLocationIndex());
373
374 // Then perform the assignments.
375 auto const& assignments = destination.getOrderedAssignments().getNonTransientAssignments(assignmentLevel);
376 auto assignmentIt = assignments.begin();
377 auto assignmentIte = assignments.end();
378
379 // Iterate over all boolean assignments and carry them out.
380 auto boolIt = this->variableInformation.booleanVariables.begin();
381 for (; assignmentIt != assignmentIte && assignmentIt->lValueIsVariable() && assignmentIt->getExpressionVariable().hasBooleanType(); ++assignmentIt) {
382 while (assignmentIt->getExpressionVariable() != boolIt->variable) {
383 ++boolIt;
384 }
385 state.set(boolIt->bitOffset, expressionEvaluator.asBool(assignmentIt->getAssignedExpression()));
386 }
387
388 // Iterate over all integer assignments and carry them out.
389 auto integerIt = this->variableInformation.integerVariables.begin();
390 for (; assignmentIt != assignmentIte && assignmentIt->lValueIsVariable() && assignmentIt->getExpressionVariable().hasIntegerType(); ++assignmentIt) {
391 while (assignmentIt->getExpressionVariable() != integerIt->variable) {
392 ++integerIt;
393 }
394 int_fast64_t assignedValue = expressionEvaluator.asInt(assignmentIt->getAssignedExpression());
395 if (this->options.isAddOutOfBoundsStateSet()) {
396 if (assignedValue < integerIt->lowerBound || assignedValue > integerIt->upperBound) {
397 state = this->outOfBoundsState;
398 }
399 } else if (integerIt->forceOutOfBoundsCheck || this->options.isExplorationChecksSet()) {
400 STORM_LOG_THROW(assignedValue >= integerIt->lowerBound, storm::exceptions::WrongFormatException,
401 "The update " << assignmentIt->getExpressionVariable().getName() << " := " << assignmentIt->getAssignedExpression()
402 << " leads to an out-of-bounds value (" << assignedValue << ") for the variable '"
403 << assignmentIt->getExpressionVariable().getName() << "'.");
404 STORM_LOG_THROW(assignedValue <= integerIt->upperBound, storm::exceptions::WrongFormatException,
405 "The update " << assignmentIt->getExpressionVariable().getName() << " := " << assignmentIt->getAssignedExpression()
406 << " leads to an out-of-bounds value (" << assignedValue << ") for the variable '"
407 << assignmentIt->getExpressionVariable().getName() << "'.");
408 }
409 state.setFromInt(integerIt->bitOffset, integerIt->bitWidth, assignedValue - integerIt->lowerBound);
410 STORM_LOG_ASSERT(static_cast<int_fast64_t>(state.getAsInt(integerIt->bitOffset, integerIt->bitWidth)) + integerIt->lowerBound == assignedValue,
411 "Writing to the bit vector bucket failed (read " << state.getAsInt(integerIt->bitOffset, integerIt->bitWidth) << " but wrote "
412 << assignedValue << ").");
413 }
414 // Iterate over all array access assignments and carry them out.
415 for (; assignmentIt != assignmentIte && assignmentIt->lValueIsArrayAccess(); ++assignmentIt) {
416 auto const& arrayIndicesAsExpr = assignmentIt->getLValue().getArrayIndexVector();
417 std::vector<uint64_t> arrayIndices;
418 arrayIndices.reserve(arrayIndicesAsExpr.size());
419 for (auto const& i : arrayIndicesAsExpr) {
420 arrayIndices.push_back(static_cast<uint64_t>(expressionEvaluator.asInt(i)));
421 }
422 if (assignmentIt->getAssignedExpression().hasIntegerType()) {
423 IntegerVariableInformation const& intInfo =
424 this->variableInformation.getIntegerArrayVariableReplacement(assignmentIt->getLValue().getVariable().getExpressionVariable(), arrayIndices);
425 int_fast64_t assignedValue = expressionEvaluator.asInt(assignmentIt->getAssignedExpression());
426
427 if (this->options.isAddOutOfBoundsStateSet()) {
428 if (assignedValue < intInfo.lowerBound || assignedValue > intInfo.upperBound) {
429 state = this->outOfBoundsState;
430 }
431 } else if (this->options.isExplorationChecksSet()) {
432 STORM_LOG_THROW(assignedValue >= intInfo.lowerBound, storm::exceptions::WrongFormatException,
433 "The update " << assignmentIt->getLValue() << " := " << assignmentIt->getAssignedExpression()
434 << " leads to an out-of-bounds value (" << assignedValue << ") for the variable '"
435 << assignmentIt->getExpressionVariable().getName() << "'.");
436 STORM_LOG_THROW(assignedValue <= intInfo.upperBound, storm::exceptions::WrongFormatException,
437 "The update " << assignmentIt->getLValue() << " := " << assignmentIt->getAssignedExpression()
438 << " leads to an out-of-bounds value (" << assignedValue << ") for the variable '"
439 << assignmentIt->getExpressionVariable().getName() << "'.");
440 }
441 state.setFromInt(intInfo.bitOffset, intInfo.bitWidth, assignedValue - intInfo.lowerBound);
442 STORM_LOG_ASSERT(static_cast<int_fast64_t>(state.getAsInt(intInfo.bitOffset, intInfo.bitWidth)) + intInfo.lowerBound == assignedValue,
443 "Writing to the bit vector bucket failed (read " << state.getAsInt(intInfo.bitOffset, intInfo.bitWidth) << " but wrote "
444 << assignedValue << ").");
445 } else if (assignmentIt->getAssignedExpression().hasBooleanType()) {
446 BooleanVariableInformation const& boolInfo =
447 this->variableInformation.getBooleanArrayVariableReplacement(assignmentIt->getLValue().getVariable().getExpressionVariable(), arrayIndices);
448 state.set(boolInfo.bitOffset, expressionEvaluator.asBool(assignmentIt->getAssignedExpression()));
449 } else {
450 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unhandled type of base variable.");
451 }
452 }
453
454 // Check that we processed all assignments.
455 STORM_LOG_ASSERT(assignmentIt == assignmentIte, "Not all assignments were consumed.");
456}
457
458template<typename ValueType, typename StateType>
459void JaniNextStateGenerator<ValueType, StateType>::applyTransientUpdate(TransientVariableValuation<ValueType>& transientValuation,
460 storm::jani::detail::ConstAssignments const& transientAssignments,
461 storm::expressions::ExpressionEvaluator<ValueType> const& expressionEvaluator) const {
462 auto assignmentIt = transientAssignments.begin();
463 auto assignmentIte = transientAssignments.end();
464
465 // Iterate over all boolean assignments and carry them out.
466 auto boolIt = this->transientVariableInformation.booleanVariableInformation.begin();
467 for (; assignmentIt != assignmentIte && assignmentIt->lValueIsVariable() && assignmentIt->getExpressionVariable().hasBooleanType(); ++assignmentIt) {
468 while (assignmentIt->getExpressionVariable() != boolIt->variable) {
469 ++boolIt;
470 }
471 transientValuation.booleanValues.emplace_back(&(*boolIt), expressionEvaluator.asBool(assignmentIt->getAssignedExpression()));
472 }
473 // Iterate over all integer assignments and carry them out.
474 auto integerIt = this->transientVariableInformation.integerVariableInformation.begin();
475 for (; assignmentIt != assignmentIte && assignmentIt->lValueIsVariable() && assignmentIt->getExpressionVariable().hasIntegerType(); ++assignmentIt) {
476 while (assignmentIt->getExpressionVariable() != integerIt->variable) {
477 ++integerIt;
478 }
479 int64_t assignedValue = expressionEvaluator.asInt(assignmentIt->getAssignedExpression());
480 if (this->options.isExplorationChecksSet()) {
481 STORM_LOG_THROW(!integerIt->lowerBound || assignedValue >= integerIt->lowerBound.get(), storm::exceptions::WrongFormatException,
482 "The update " << assignmentIt->getExpressionVariable().getName() << " := " << assignmentIt->getAssignedExpression()
483 << " leads to an out-of-bounds value (" << assignedValue << ") for the variable '"
484 << assignmentIt->getExpressionVariable().getName() << "'.");
485 STORM_LOG_THROW(!integerIt->upperBound || assignedValue <= integerIt->upperBound.get(), storm::exceptions::WrongFormatException,
486 "The update " << assignmentIt->getExpressionVariable().getName() << " := " << assignmentIt->getAssignedExpression()
487 << " leads to an out-of-bounds value (" << assignedValue << ") for the variable '"
488 << assignmentIt->getExpressionVariable().getName() << "'.");
489 }
490 transientValuation.integerValues.emplace_back(&(*integerIt), assignedValue);
491 }
492 // Iterate over all rational assignments and carry them out.
493 auto rationalIt = this->transientVariableInformation.rationalVariableInformation.begin();
494 for (; assignmentIt != assignmentIte && assignmentIt->lValueIsVariable() && assignmentIt->getExpressionVariable().hasRationalType(); ++assignmentIt) {
495 while (assignmentIt->getExpressionVariable() != rationalIt->variable) {
496 ++rationalIt;
497 }
498 transientValuation.rationalValues.emplace_back(&(*rationalIt), expressionEvaluator.asRational(assignmentIt->getAssignedExpression()));
499 }
500
501 // Iterate over all array access assignments and carry them out.
502 for (; assignmentIt != assignmentIte && assignmentIt->lValueIsArrayAccess(); ++assignmentIt) {
503 auto const& arrayIndicesAsExpr = assignmentIt->getLValue().getArrayIndexVector();
504 std::vector<uint64_t> arrayIndices;
505 arrayIndices.reserve(arrayIndicesAsExpr.size());
506 for (auto const& i : arrayIndicesAsExpr) {
507 arrayIndices.push_back(static_cast<uint64_t>(expressionEvaluator.asInt(i)));
508 }
509 storm::expressions::Type const& baseType = assignmentIt->getLValue().getVariable().getExpressionVariable().getType();
510 if (baseType.isIntegerType()) {
511 auto const& intInfo = this->transientVariableInformation.getIntegerArrayVariableReplacement(
512 assignmentIt->getLValue().getVariable().getExpressionVariable(), arrayIndices);
513 int64_t assignedValue = expressionEvaluator.asInt(assignmentIt->getAssignedExpression());
514 if (this->options.isExplorationChecksSet()) {
515 STORM_LOG_THROW(assignedValue >= intInfo.lowerBound, storm::exceptions::WrongFormatException,
516 "The update " << assignmentIt->getLValue() << " := " << assignmentIt->getAssignedExpression()
517 << " leads to an out-of-bounds value (" << assignedValue << ") for the variable '"
518 << assignmentIt->getExpressionVariable().getName() << "'.");
519 STORM_LOG_THROW(assignedValue <= intInfo.upperBound, storm::exceptions::WrongFormatException,
520 "The update " << assignmentIt->getLValue() << " := " << assignmentIt->getAssignedExpression()
521 << " leads to an out-of-bounds value (" << assignedValue << ") for the variable '"
522 << assignmentIt->getExpressionVariable().getName() << "'.");
523 }
524 transientValuation.integerValues.emplace_back(&intInfo, assignedValue);
525 } else if (baseType.isBooleanType()) {
526 auto const& boolInfo = this->transientVariableInformation.getBooleanArrayVariableReplacement(
527 assignmentIt->getLValue().getVariable().getExpressionVariable(), arrayIndices);
528 transientValuation.booleanValues.emplace_back(&boolInfo, expressionEvaluator.asBool(assignmentIt->getAssignedExpression()));
529 } else if (baseType.isRationalType()) {
530 auto const& rationalInfo = this->transientVariableInformation.getRationalArrayVariableReplacement(
531 assignmentIt->getLValue().getVariable().getExpressionVariable(), arrayIndices);
532 transientValuation.rationalValues.emplace_back(&rationalInfo, expressionEvaluator.asRational(assignmentIt->getAssignedExpression()));
533 } else {
534 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unhandled type of base variable.");
535 }
536 }
537
538 // Check that we processed all assignments.
539 STORM_LOG_ASSERT(assignmentIt == assignmentIte, "Not all assignments were consumed.");
540}
541
542template<typename ValueType, typename StateType>
543TransientVariableValuation<ValueType> JaniNextStateGenerator<ValueType, StateType>::getTransientVariableValuationAtLocations(
544 std::vector<uint64_t> const& locations, storm::expressions::ExpressionEvaluator<ValueType> const& evaluator) const {
545 uint64_t automatonIndex = 0;
546 TransientVariableValuation<ValueType> transientVariableValuation;
547 for (auto const& automatonRef : this->parallelAutomata) {
548 auto const& automaton = automatonRef.get();
549 uint64_t currentLocationIndex = locations[automatonIndex];
550 storm::jani::Location const& location = automaton.getLocation(currentLocationIndex);
551 STORM_LOG_ASSERT(!location.getAssignments().hasMultipleLevels(true), "Indexed assignments at locations are not supported in the jani standard.");
552 applyTransientUpdate(transientVariableValuation, location.getAssignments().getTransientAssignments(), evaluator);
553 ++automatonIndex;
554 }
555 return transientVariableValuation;
556}
557
558template<typename ValueType, typename StateType>
561 transientVariableInformation.setDefaultValuesInEvaluator(evaluator);
562 auto transientVariableValuation = getTransientVariableValuationAtLocations(getLocations(state), evaluator);
563 transientVariableValuation.setInEvaluator(evaluator, this->getOptions().isExplorationChecksSet());
564}
565
566template<typename ValueType, typename StateType>
569 if (this->variableInformation.hasOutOfBoundsBit()) {
570 builder.addBooleanVariable(this->variableInformation.outOfBoundsBit->variable);
571 }
572 for (auto const& v : this->variableInformation.locationVariables) {
573 builder.addIntegerVariable(v.variable, 0, v.highestValue);
574 }
575 for (auto const& v : this->variableInformation.booleanVariables) {
576 builder.addBooleanVariable(v.variable);
577 }
578 for (auto const& v : this->variableInformation.integerVariables) {
579 builder.addIntegerVariable(v.variable, v.lowerBound, v.upperBound);
580 }
581 // Also add information for transient variables
582 for (auto const& varInfo : transientVariableInformation.booleanVariableInformation) {
583 builder.addBooleanVariable(varInfo.variable);
584 }
585 for (auto const& varInfo : transientVariableInformation.integerVariableInformation) {
586 builder.addIntegerVariable(varInfo.variable, varInfo.lowerBound.value_or(std::numeric_limits<int64_t>::min()),
587 varInfo.upperBound.value_or(std::numeric_limits<int64_t>::max()));
588 }
589 for (auto const& varInfo : transientVariableInformation.rationalVariableInformation) {
590 if (std::is_same_v<ValueType, storm::RationalNumber>) {
591 uint64_t const bitSize = std::max<uint64_t>(64, this->getOptions().getReservedBitsForUnboundedVariables());
592 builder.addRationalVariable(varInfo.variable, bitSize * 2); // reserve bits for numerator and denominator
593 } else {
594 STORM_LOG_THROW((std::is_same_v<ValueType, double>), storm::exceptions::NotSupportedException,
595 "State valuations for transient variables of the given value type are not supported.");
596 builder.addDoubleVariable(varInfo.variable);
597 }
598 }
599 return storm::storage::sparse::Valuations(builder.buildClassDescription(), builder.getManager().shared_from_this());
600}
601
602template<typename ValueType, typename StateType>
604 storm::storage::sparse::Valuations& valuations) const {
605 // Add values for non-transient variables
606 unpackStateAppendToValuations(*this->state, this->variableInformation, valuations.getStorage());
607
608 auto transientVariableValuation = getTransientVariableValuationAtLocations(getLocations(*this->state), *this->evaluator);
609 transientVariableValuation.setInValuations(currentStateIndex, transientVariableInformation, valuations.getStorage());
610}
611
612template<typename ValueType, typename StateType>
614 // The evaluator should have the default values of the transient variables right now.
615
616 // Prepare the result, in case we return early.
618
619 // Retrieve the locations from the state.
620 std::vector<uint64_t> locations = getLocations(*this->state);
621
622 // First, construct the state rewards, as we may return early if there are no choices later and we already
623 // need the state rewards then.
624 auto transientVariableValuation = getTransientVariableValuationAtLocations(locations, *this->evaluator);
625 transientVariableValuation.setInEvaluator(*this->evaluator, this->getOptions().isExplorationChecksSet());
626 result.addStateRewards(evaluateRewardExpressions());
627
628 // If a terminal expression was set and we must not expand this state, return now.
629 // Terminal state expressions do not consider transient variables.
630 if (!this->terminalStates.empty()) {
631 for (auto const& expressionBool : this->terminalStates) {
632 if (this->evaluator->asBool(expressionBool.first) == expressionBool.second) {
633 // Set back transient variables to default values so we are ready to process the next state
634 this->transientVariableInformation.setDefaultValuesInEvaluator(*this->evaluator);
635 return result;
636 }
637 }
638 }
639
640 // Set back transient variables to default values so we are ready to process the transition assignments
641 this->transientVariableInformation.setDefaultValuesInEvaluator(*this->evaluator);
642
643 // Get all choices for the state.
644 result.setExpanded();
645 std::vector<Choice<ValueType>> allChoices;
646 if (this->getOptions().isApplyMaximalProgressAssumptionSet()) {
647 // First explore only edges without a rate
648 allChoices = getActionChoices(locations, *this->state, stateToIdCallback, EdgeFilter::WithoutRate);
649 if (allChoices.empty()) {
650 // Expand the Markovian edges if there are no probabilistic ones.
651 allChoices = getActionChoices(locations, *this->state, stateToIdCallback, EdgeFilter::WithRate);
652 }
653 } else {
654 allChoices = getActionChoices(locations, *this->state, stateToIdCallback);
655 }
656 std::size_t totalNumberOfChoices = allChoices.size();
657
658 // If there is not a single choice, we return immediately, because the state has no behavior (other than
659 // the state reward).
660 if (totalNumberOfChoices == 0) {
661 return result;
662 }
663
664 // If the model is a deterministic model, we need to fuse the choices into one.
665 if (this->isDeterministicModel() && totalNumberOfChoices > 1) {
666 Choice<ValueType> globalChoice;
667
668 if (this->options.isAddOverlappingGuardLabelSet()) {
669 this->overlappingGuardStates->push_back(stateToIdCallback(*this->state));
670 }
671
672 // For CTMCs, we need to keep track of the total exit rate to scale the action rewards later. For DTMCs
673 // this is equal to the number of choices, which is why we initialize it like this here.
674 ValueType totalExitRate = this->isDiscreteTimeModel() ? static_cast<ValueType>(totalNumberOfChoices) : storm::utility::zero<ValueType>();
675
676 // Iterate over all choices and combine the probabilities/rates into one choice.
677 for (auto const& choice : allChoices) {
678 for (auto const& stateProbabilityPair : choice) {
679 if (this->isDiscreteTimeModel()) {
680 globalChoice.addProbability(stateProbabilityPair.first, stateProbabilityPair.second / totalNumberOfChoices);
681 } else {
682 globalChoice.addProbability(stateProbabilityPair.first, stateProbabilityPair.second);
683 }
684 }
685
686 if (hasStateActionRewards && !this->isDiscreteTimeModel()) {
687 totalExitRate += choice.getTotalMass();
688 }
689 }
690
691 std::vector<ValueType> stateActionRewards(rewardExpressions.size(), storm::utility::zero<ValueType>());
692 for (auto const& choice : allChoices) {
693 if (hasStateActionRewards) {
694 for (uint_fast64_t rewardVariableIndex = 0; rewardVariableIndex < rewardExpressions.size(); ++rewardVariableIndex) {
695 stateActionRewards[rewardVariableIndex] += choice.getRewards()[rewardVariableIndex] * choice.getTotalMass() / totalExitRate;
696 }
697 }
698
699 if (this->options.isBuildChoiceOriginsSet() && choice.hasOriginData()) {
700 globalChoice.addOriginData(choice.getOriginData());
701 }
702 }
703 globalChoice.addRewards(std::move(stateActionRewards));
704
705 // Move the newly fused choice in place.
706 allChoices.clear();
707 allChoices.push_back(std::move(globalChoice));
708 }
709
710 // Move all remaining choices in place.
711 for (auto& choice : allChoices) {
712 result.addChoice(std::move(choice));
713 }
714
715 this->postprocess(result);
716
717 return result;
718}
719
720template<typename ValueType, typename StateType>
721Choice<ValueType> JaniNextStateGenerator<ValueType, StateType>::expandNonSynchronizingEdge(storm::jani::Edge const& edge, uint64_t outputActionIndex,
722 uint64_t automatonIndex, CompressedState const& state,
723 StateToIdCallback stateToIdCallback) {
724 // Determine the exit rate if it's a Markovian edge.
725 boost::optional<ValueType> exitRate = boost::none;
726 if (edge.hasRate()) {
727 exitRate = this->evaluator->asRational(edge.getRate());
728 }
729
730 Choice<ValueType> choice(outputActionIndex, static_cast<bool>(exitRate));
731 std::vector<ValueType> stateActionRewards;
732
733 // Perform the transient edge assignments and create the state action rewards
734 TransientVariableValuation<ValueType> transientVariableValuation;
735 if (!evaluateRewardExpressionsAtEdges || edge.getAssignments().empty()) {
736 stateActionRewards.resize(rewardModelInformation.size(), storm::utility::zero<ValueType>());
737 } else {
738 for (int64_t assignmentLevel = edge.getAssignments().getLowestLevel(true); assignmentLevel <= edge.getAssignments().getHighestLevel(true);
739 ++assignmentLevel) {
740 transientVariableValuation.clear();
741 applyTransientUpdate(transientVariableValuation, edge.getAssignments().getTransientAssignments(assignmentLevel), *this->evaluator);
742 transientVariableValuation.setInEvaluator(*this->evaluator, this->getOptions().isExplorationChecksSet());
743 }
744 stateActionRewards = evaluateRewardExpressions();
745 transientVariableInformation.setDefaultValuesInEvaluator(*this->evaluator);
746 }
747
748 // Iterate over all updates of the current command.
750 for (auto const& destination : edge.getDestinations()) {
751 ValueType probability = this->evaluator->asRational(destination.getProbability());
752
753 if (probability != storm::utility::zero<ValueType>()) {
754 bool evaluatorChanged = false;
755 // Obtain target state index and add it to the list of known states. If it has not yet been
756 // seen, we also add it to the set of states that have yet to be explored.
757 int64_t assignmentLevel = edge.getLowestAssignmentLevel(); // Might be the largest possible integer, if there is no assignment
758 int64_t const& highestLevel = edge.getHighestAssignmentLevel();
759 bool hasTransientAssignments = destination.hasTransientAssignment();
760 CompressedState newState = state;
761 applyUpdate(newState, destination, this->variableInformation.locationVariables[automatonIndex], assignmentLevel, *this->evaluator);
762 if (hasTransientAssignments) {
764 "Transition rewards are not supported and scaling to action rewards is disabled.");
765 transientVariableValuation.clear();
766 applyTransientUpdate(transientVariableValuation, destination.getOrderedAssignments().getTransientAssignments(assignmentLevel),
767 *this->evaluator);
768 transientVariableValuation.setInEvaluator(*this->evaluator, this->getOptions().isExplorationChecksSet());
769 evaluatorChanged = true;
770 }
771 if (assignmentLevel < highestLevel) {
772 while (assignmentLevel < highestLevel) {
773 ++assignmentLevel;
774 unpackStateIntoEvaluator(newState, this->variableInformation, *this->evaluator);
775 evaluatorChanged = true;
776 applyUpdate(newState, destination, this->variableInformation.locationVariables[automatonIndex], assignmentLevel, *this->evaluator);
777 if (hasTransientAssignments) {
778 transientVariableValuation.clear();
779 applyTransientUpdate(transientVariableValuation, destination.getOrderedAssignments().getTransientAssignments(assignmentLevel),
780 *this->evaluator);
781 transientVariableValuation.setInEvaluator(*this->evaluator, this->getOptions().isExplorationChecksSet());
782 evaluatorChanged = true;
783 }
784 }
785 }
786 if (evaluateRewardExpressionsAtDestinations) {
787 unpackStateIntoEvaluator(newState, this->variableInformation, *this->evaluator);
788 evaluatorChanged = true;
789 addEvaluatedRewardExpressions(stateActionRewards, probability);
790 }
791
792 if (evaluatorChanged) {
793 // Restore the old variable valuation
794 unpackStateIntoEvaluator(state, this->variableInformation, *this->evaluator);
795 if (hasTransientAssignments) {
796 this->transientVariableInformation.setDefaultValuesInEvaluator(*this->evaluator);
797 }
798 }
799
800 StateType stateIndex = stateToIdCallback(newState);
801
802 // Update the choice by adding the probability/target state to it.
803 probability = exitRate ? exitRate.get() * probability : probability;
804 choice.addProbability(stateIndex, probability);
805
806 if (this->options.isExplorationChecksSet()) {
807 probabilitySum += probability;
808 }
809 }
810 }
811
812 // Add the state action rewards
813 choice.addRewards(std::move(stateActionRewards));
814
815 if (this->options.isExplorationChecksSet()) {
816 // Check that the resulting distribution is in fact a distribution.
817 STORM_LOG_THROW(!this->isDiscreteTimeModel() || (!storm::utility::isConstant(probabilitySum) || this->comparator.isOne(probabilitySum)),
818 storm::exceptions::WrongFormatException, "Probabilities do not sum to one for edge (actually sum to " << probabilitySum << ").");
819 }
820
821 return choice;
822}
823
824template<typename ValueType, typename StateType>
825void JaniNextStateGenerator<ValueType, StateType>::generateSynchronizedDistribution(storm::storage::BitVector const& state,
826 AutomataEdgeSets const& edgeCombination,
827 std::vector<EdgeSetWithIndices::const_iterator> const& iteratorList,
828 storm::generator::Distribution<StateType, ValueType>& distribution,
829 std::vector<ValueType>& stateActionRewards, EdgeIndexSet& edgeIndices,
830 StateToIdCallback stateToIdCallback) {
831 // Collect some information of the edges.
832 int64_t lowestDestinationAssignmentLevel = std::numeric_limits<int64_t>::max();
833 int64_t highestDestinationAssignmentLevel = std::numeric_limits<int64_t>::min();
834 int64_t lowestEdgeAssignmentLevel = std::numeric_limits<int64_t>::max();
835 int64_t highestEdgeAssignmentLevel = std::numeric_limits<int64_t>::min();
836 uint64_t numDestinations = 1;
837 for (uint_fast64_t i = 0; i < iteratorList.size(); ++i) {
838 if (this->getOptions().isBuildChoiceOriginsSet()) {
839 auto automatonIndex = model.getAutomatonIndex(parallelAutomata[edgeCombination[i].first].get().getName());
840 edgeIndices.insert(storm::jani::Model::encodeAutomatonAndEdgeIndices(automatonIndex, iteratorList[i]->first));
841 }
842 storm::jani::Edge const& edge = *iteratorList[i]->second;
843 lowestDestinationAssignmentLevel = std::min(lowestDestinationAssignmentLevel, edge.getLowestAssignmentLevel());
844 highestDestinationAssignmentLevel = std::max(highestDestinationAssignmentLevel, edge.getHighestAssignmentLevel());
845 if (!edge.getAssignments().empty(true)) {
846 lowestEdgeAssignmentLevel = std::min(lowestEdgeAssignmentLevel, edge.getAssignments().getLowestLevel(true));
847 highestEdgeAssignmentLevel = std::max(highestEdgeAssignmentLevel, edge.getAssignments().getHighestLevel(true));
848 }
849 numDestinations *= edge.getNumberOfDestinations();
850 }
851
852 // Perform the edge assignments (if there are any)
853 TransientVariableValuation<ValueType> transientVariableValuation;
854 if (evaluateRewardExpressionsAtEdges && lowestEdgeAssignmentLevel <= highestEdgeAssignmentLevel) {
855 for (int64_t assignmentLevel = lowestEdgeAssignmentLevel; assignmentLevel <= highestEdgeAssignmentLevel; ++assignmentLevel) {
856 transientVariableValuation.clear();
857 for (uint_fast64_t i = 0; i < iteratorList.size(); ++i) {
858 storm::jani::Edge const& edge = *iteratorList[i]->second;
859 applyTransientUpdate(transientVariableValuation, edge.getAssignments().getTransientAssignments(assignmentLevel), *this->evaluator);
860 }
861 transientVariableValuation.setInEvaluator(*this->evaluator, this->getOptions().isExplorationChecksSet());
862 }
863 addEvaluatedRewardExpressions(stateActionRewards, storm::utility::one<ValueType>());
864 transientVariableInformation.setDefaultValuesInEvaluator(*this->evaluator);
865 }
866
867 std::vector<storm::jani::EdgeDestination const*> destinations;
868 std::vector<LocationVariableInformation const*> locationVars;
869 destinations.reserve(iteratorList.size());
870 locationVars.reserve(iteratorList.size());
871
872 for (uint64_t destinationId = 0; destinationId < numDestinations; ++destinationId) {
873 // First assignment level
874 destinations.clear();
875 locationVars.clear();
876 transientVariableValuation.clear();
877 CompressedState successorState = state;
878 ValueType successorProbability = storm::utility::one<ValueType>();
879
880 uint64_t destinationIndex = destinationId;
881 for (uint64_t i = 0; i < iteratorList.size(); ++i) {
882 storm::jani::Edge const& edge = *iteratorList[i]->second;
883 STORM_LOG_ASSERT(edge.getNumberOfDestinations() > 0, "Found an edge with zero destinations. This is not expected.");
884 uint64_t localDestinationIndex = destinationIndex % edge.getNumberOfDestinations();
885 destinations.push_back(&edge.getDestination(localDestinationIndex));
886 locationVars.push_back(&this->variableInformation.locationVariables[edgeCombination[i].first]);
887 destinationIndex /= edge.getNumberOfDestinations();
888 ValueType probability = this->evaluator->asRational(destinations.back()->getProbability());
889 if (edge.hasRate()) {
890 successorProbability *= probability * this->evaluator->asRational(edge.getRate());
891 } else {
892 successorProbability *= probability;
893 }
894 if (storm::utility::isZero(successorProbability)) {
895 break;
896 }
897
898 applyUpdate(successorState, *destinations.back(), *locationVars.back(), lowestDestinationAssignmentLevel, *this->evaluator);
899 applyTransientUpdate(transientVariableValuation,
900 destinations.back()->getOrderedAssignments().getTransientAssignments(lowestDestinationAssignmentLevel), *this->evaluator);
901 }
902
903 if (!storm::utility::isZero(successorProbability)) {
904 bool evaluatorChanged = false;
905 // remaining assignment levels (if there are any)
906 for (int64_t assignmentLevel = lowestDestinationAssignmentLevel + 1; assignmentLevel <= highestDestinationAssignmentLevel; ++assignmentLevel) {
907 unpackStateIntoEvaluator(successorState, this->variableInformation, *this->evaluator);
908 transientVariableValuation.setInEvaluator(*this->evaluator, this->getOptions().isExplorationChecksSet());
909 transientVariableValuation.clear();
910 evaluatorChanged = true;
911 auto locationVarIt = locationVars.begin();
912 for (auto const& destPtr : destinations) {
913 applyUpdate(successorState, *destPtr, **locationVarIt, assignmentLevel, *this->evaluator);
914 applyTransientUpdate(transientVariableValuation, destinations.back()->getOrderedAssignments().getTransientAssignments(assignmentLevel),
915 *this->evaluator);
916 ++locationVarIt;
917 }
918 }
919 if (!transientVariableValuation.empty()) {
920 evaluatorChanged = true;
921 transientVariableValuation.setInEvaluator(*this->evaluator, this->getOptions().isExplorationChecksSet());
922 }
923 if (evaluateRewardExpressionsAtDestinations) {
924 unpackStateIntoEvaluator(successorState, this->variableInformation, *this->evaluator);
925 evaluatorChanged = true;
926 addEvaluatedRewardExpressions(stateActionRewards, successorProbability);
927 }
928 if (evaluatorChanged) {
929 // Restore the old state information
930 unpackStateIntoEvaluator(state, this->variableInformation, *this->evaluator);
931 this->transientVariableInformation.setDefaultValuesInEvaluator(*this->evaluator);
932 }
933
934 StateType id = stateToIdCallback(successorState);
935 distribution.add(id, successorProbability);
936 }
937 }
938}
939
940template<typename ValueType, typename StateType>
941void JaniNextStateGenerator<ValueType, StateType>::expandSynchronizingEdgeCombination(AutomataEdgeSets const& edgeCombination, uint64_t outputActionIndex,
942 CompressedState const& state, StateToIdCallback stateToIdCallback,
943 std::vector<Choice<ValueType>>& newChoices) {
944 if (this->options.isExplorationChecksSet()) {
945 // Check whether a global variable is written multiple times in any combination.
946 checkGlobalVariableWritesValid(edgeCombination);
947 }
948
949 std::vector<EdgeSetWithIndices::const_iterator> iteratorList(edgeCombination.size());
950
951 // Initialize the list of iterators.
952 for (size_t i = 0; i < edgeCombination.size(); ++i) {
953 iteratorList[i] = edgeCombination[i].second.cbegin();
954 }
955
956 storm::generator::Distribution<StateType, ValueType> distribution;
957
958 // As long as there is one feasible combination of commands, keep on expanding it.
959 bool done = false;
960 while (!done) {
961 distribution.clear();
962
963 EdgeIndexSet edgeIndices;
964 std::vector<ValueType> stateActionRewards(rewardExpressions.size(), storm::utility::zero<ValueType>());
965 // old version without assignment levels generateSynchronizedDistribution(state, storm::utility::one<ValueType>(), 0, edgeCombination, iteratorList,
966 // distribution, stateActionRewards, edgeIndices, stateToIdCallback);
967 generateSynchronizedDistribution(state, edgeCombination, iteratorList, distribution, stateActionRewards, edgeIndices, stateToIdCallback);
968 distribution.compress();
969
970 // At this point, we applied all commands of the current command combination and newTargetStates
971 // contains all target states and their respective probabilities. That means we are now ready to
972 // add the choice to the list of transitions.
973 newChoices.emplace_back(outputActionIndex);
974
975 // Now create the actual distribution.
976 Choice<ValueType>& choice = newChoices.back();
977
978 // Add the edge indices if requested.
979 if (this->getOptions().isBuildChoiceOriginsSet()) {
980 choice.addOriginData(boost::any(std::move(edgeIndices)));
981 }
982
983 // Add the rewards to the choice.
984 choice.addRewards(std::move(stateActionRewards));
985
986 // Add the probabilities/rates to the newly created choice.
988 choice.reserve(std::distance(distribution.begin(), distribution.end()));
989 for (auto const& stateProbability : distribution) {
990 choice.addProbability(stateProbability.getState(), stateProbability.getValue());
991
992 if (this->options.isExplorationChecksSet()) {
993 probabilitySum += stateProbability.getValue();
994 }
995 }
996
997 if (this->options.isExplorationChecksSet()) {
998 // Check that the resulting distribution is in fact a distribution.
999 STORM_LOG_THROW(!this->isDiscreteTimeModel() || !storm::utility::isConstant(probabilitySum) || this->comparator.isOne(probabilitySum),
1000 storm::exceptions::WrongFormatException,
1001 "Sum of update probabilities do not sum to one for some edge (actually sum to " << probabilitySum << ").");
1002 }
1003
1004 if (this->options.isBuildChoiceLabelsSet()) {
1005 if (outputActionIndex != storm::jani::Model::SILENT_ACTION_INDEX) {
1006 choice.addLabel(model.getAction(outputActionIndex).getName());
1007 }
1008 }
1009
1010 // Now, check whether there is one more command combination to consider.
1011 bool movedIterator = false;
1012 for (uint64_t j = 0; !movedIterator && j < iteratorList.size(); ++j) {
1013 ++iteratorList[j];
1014 if (iteratorList[j] != edgeCombination[j].second.end()) {
1015 movedIterator = true;
1016 } else {
1017 // Reset the iterator to the beginning of the list.
1018 iteratorList[j] = edgeCombination[j].second.begin();
1019 }
1020 }
1021
1022 done = !movedIterator;
1023 }
1024}
1025
1026template<typename ValueType, typename StateType>
1027std::vector<Choice<ValueType>> JaniNextStateGenerator<ValueType, StateType>::getActionChoices(std::vector<uint64_t> const& locations,
1028 CompressedState const& state, StateToIdCallback stateToIdCallback,
1029 EdgeFilter const& edgeFilter) {
1030 std::vector<Choice<ValueType>> result;
1031
1032 // To avoid reallocations, we declare some memory here here.
1033 // This vector will store for each automaton the set of edges with the current output and the current source location
1034 std::vector<EdgeSetWithIndices const*> edgeSetsMemory;
1035 // This vector will store the 'first' combination of edges that is productive.
1036 std::vector<typename EdgeSetWithIndices::const_iterator> edgeIteratorMemory;
1037
1038 for (OutputAndEdges const& outputAndEdges : edges) {
1039 auto const& edges = outputAndEdges.second;
1040 if (edges.size() == 1) {
1041 // If the synch consists of just one element, it's non-synchronizing.
1042 auto const& nonsychingEdges = edges.front();
1043 uint64_t automatonIndex = nonsychingEdges.first;
1044
1045 auto edgesIt = nonsychingEdges.second.find(locations[automatonIndex]);
1046 if (edgesIt != nonsychingEdges.second.end()) {
1047 for (auto const& indexAndEdge : edgesIt->second) {
1048 if (edgeFilter != EdgeFilter::All) {
1049 STORM_LOG_ASSERT(edgeFilter == EdgeFilter::WithRate || edgeFilter == EdgeFilter::WithoutRate, "Unexpected edge filter.");
1050 if ((edgeFilter == EdgeFilter::WithRate) != indexAndEdge.second->hasRate()) {
1051 continue;
1052 }
1053 }
1054 if (!this->evaluator->asBool(indexAndEdge.second->getGuard())) {
1055 continue;
1056 }
1057
1058 uint64_t actionIndex = outputAndEdges.first ? outputAndEdges.first.get() : indexAndEdge.second->getActionIndex();
1059 result.push_back(expandNonSynchronizingEdge(*indexAndEdge.second, actionIndex, automatonIndex, state, stateToIdCallback));
1060
1061 if (this->getOptions().isBuildChoiceOriginsSet()) {
1062 auto modelAutomatonIndex = model.getAutomatonIndex(parallelAutomata[automatonIndex].get().getName());
1063 EdgeIndexSet edgeIndex{storm::jani::Model::encodeAutomatonAndEdgeIndices(modelAutomatonIndex, indexAndEdge.first)};
1064 result.back().addOriginData(boost::any(std::move(edgeIndex)));
1065 }
1066
1067 if (this->getOptions().isBuildChoiceLabelsSet()) {
1068 if (actionIndex != storm::jani::Model::SILENT_ACTION_INDEX) {
1069 result.back().addLabel(model.getAction(actionIndex).getName());
1070 }
1071 }
1072 }
1073 }
1074 } else {
1075 // If the element has more than one set of edges, we need to perform a synchronization.
1076 // We require that some output action for the synchronisation must have been set before.
1077 // This might be the silent action, if the Jani model does not specify an output action.
1078 STORM_LOG_ASSERT(outputAndEdges.first, "Need output action index for synchronization.");
1079
1080 uint64_t outputActionIndex = outputAndEdges.first.get();
1081
1082 // Find out whether this combination is productive
1083 bool productiveCombination = true;
1084 // First check, whether each automaton has at least one edge with the current output and the current source location
1085 // We will also store the edges of each automaton with the current outputAction
1086 edgeSetsMemory.clear();
1087 for (auto const& automatonAndEdges : outputAndEdges.second) {
1088 uint64_t automatonIndex = automatonAndEdges.first;
1089 LocationsAndEdges const& locationsAndEdges = automatonAndEdges.second;
1090 auto edgesIt = locationsAndEdges.find(locations[automatonIndex]);
1091 if (edgesIt == locationsAndEdges.end()) {
1092 productiveCombination = false;
1093 break;
1094 }
1095 edgeSetsMemory.push_back(&edgesIt->second);
1096 }
1097
1098 if (productiveCombination) {
1099 // second, check whether each automaton has at least one enabled action
1100 edgeIteratorMemory.clear(); // Store the first enabled edge in each automaton.
1101 for (auto const& edgesIt : edgeSetsMemory) {
1102 bool atLeastOneEdge = false;
1103 EdgeSetWithIndices const& edgeSetWithIndices = *edgesIt;
1104 for (auto indexAndEdgeIt = edgeSetWithIndices.begin(), indexAndEdgeIte = edgeSetWithIndices.end(); indexAndEdgeIt != indexAndEdgeIte;
1105 ++indexAndEdgeIt) {
1106 // check whether we do not consider this edge
1107 if (edgeFilter != EdgeFilter::All) {
1108 STORM_LOG_ASSERT(edgeFilter == EdgeFilter::WithRate || edgeFilter == EdgeFilter::WithoutRate, "Unexpected edge filter.");
1109 if ((edgeFilter == EdgeFilter::WithRate) != indexAndEdgeIt->second->hasRate()) {
1110 continue;
1111 }
1112 }
1113
1114 if (!this->evaluator->asBool(indexAndEdgeIt->second->getGuard())) {
1115 continue;
1116 }
1117
1118 // If we reach this point, the edge is considered enabled.
1119 atLeastOneEdge = true;
1120 edgeIteratorMemory.push_back(indexAndEdgeIt);
1121 break;
1122 }
1123
1124 // If there is no enabled edge of this automaton, the whole combination is not productive.
1125 if (!atLeastOneEdge) {
1126 productiveCombination = false;
1127 break;
1128 }
1129 }
1130 }
1131
1132 // produce the combination
1133 if (productiveCombination) {
1134 AutomataEdgeSets automataEdgeSets;
1135 automataEdgeSets.reserve(outputAndEdges.second.size());
1136 STORM_LOG_ASSERT(edgeSetsMemory.size() == outputAndEdges.second.size(), "Unexpected number of edge sets stored.");
1137 STORM_LOG_ASSERT(edgeIteratorMemory.size() == outputAndEdges.second.size(), "Unexpected number of edge iterators stored.");
1138 auto edgeSetIt = edgeSetsMemory.begin();
1139 auto edgeIteratorIt = edgeIteratorMemory.begin();
1140 for (auto const& automatonAndEdges : outputAndEdges.second) {
1141 EdgeSetWithIndices enabledEdgesOfAutomaton;
1142 uint64_t automatonIndex = automatonAndEdges.first;
1143 EdgeSetWithIndices const& edgeSetWithIndices = **edgeSetIt;
1144 auto indexAndEdgeIt = *edgeIteratorIt;
1145 // The first edge where the edgeIterator points to is always enabled.
1146 enabledEdgesOfAutomaton.emplace_back(*indexAndEdgeIt);
1147 auto indexAndEdgeIte = edgeSetWithIndices.end();
1148 for (++indexAndEdgeIt; indexAndEdgeIt != indexAndEdgeIte; ++indexAndEdgeIt) {
1149 // check whether we do not consider this edge
1150 if (edgeFilter != EdgeFilter::All) {
1151 STORM_LOG_ASSERT(edgeFilter == EdgeFilter::WithRate || edgeFilter == EdgeFilter::WithoutRate, "Unexpected edge filter.");
1152 if ((edgeFilter == EdgeFilter::WithRate) != indexAndEdgeIt->second->hasRate()) {
1153 continue;
1154 }
1155 }
1156
1157 if (!this->evaluator->asBool(indexAndEdgeIt->second->getGuard())) {
1158 continue;
1159 }
1160 // If we reach this point, the edge is considered enabled.
1161 enabledEdgesOfAutomaton.emplace_back(*indexAndEdgeIt);
1162 }
1163 automataEdgeSets.emplace_back(std::move(automatonIndex), std::move(enabledEdgesOfAutomaton));
1164 ++edgeSetIt;
1165 ++edgeIteratorIt;
1166 }
1167 // insert choices in the result vector.
1168 expandSynchronizingEdgeCombination(automataEdgeSets, outputActionIndex, state, stateToIdCallback, result);
1169 }
1170 }
1171 }
1172
1173 return result;
1174}
1175
1176template<typename ValueType, typename StateType>
1177void JaniNextStateGenerator<ValueType, StateType>::checkGlobalVariableWritesValid(AutomataEdgeSets const& enabledEdges) const {
1178 // Todo: this also throws if the writes are on different assignment level
1179 // Todo: this also throws if the writes are on different elements of the same array
1180 std::map<storm::expressions::Variable, uint64_t> writtenGlobalVariables;
1181 for (auto edgeSetIt = enabledEdges.begin(), edgeSetIte = enabledEdges.end(); edgeSetIt != edgeSetIte; ++edgeSetIt) {
1182 for (auto const& indexAndEdge : edgeSetIt->second) {
1183 for (auto const& globalVariable : indexAndEdge.second->getWrittenGlobalVariables()) {
1184 auto it = writtenGlobalVariables.find(globalVariable);
1185
1186 auto index = std::distance(enabledEdges.begin(), edgeSetIt);
1187 if (it != writtenGlobalVariables.end()) {
1188 STORM_LOG_THROW(it->second == static_cast<uint64_t>(index), storm::exceptions::WrongFormatException,
1189 "Multiple writes to global variable '" << globalVariable.getName() << "' in synchronizing edges.");
1190 } else {
1191 writtenGlobalVariables.emplace(globalVariable, index);
1192 }
1193 }
1194 }
1195 }
1196}
1197
1198template<typename ValueType, typename StateType>
1200 return rewardExpressions.size();
1201}
1202
1203template<typename ValueType, typename StateType>
1205 return rewardModelInformation[index];
1206}
1207
1208template<typename ValueType, typename StateType>
1210 std::vector<StateType> const& initialStateIndices,
1211 std::vector<StateType> const& deadlockStateIndices,
1212 std::vector<StateType> const& unexploredStateIndices) {
1213 // As in JANI we can use transient boolean variable assignments in locations to identify states, we need to
1214 // create a list of boolean transient variables and the expressions that define them.
1215 std::vector<std::pair<std::string, storm::expressions::Expression>> transientVariableExpressions;
1216 for (auto const& variable : model.getGlobalVariables().getTransientVariables()) {
1217 if (variable.getType().isBasicType() && variable.getType().asBasicType().isBooleanType()) {
1218 if (this->options.isBuildAllLabelsSet() || this->options.getLabelNames().find(variable.getName()) != this->options.getLabelNames().end()) {
1219 transientVariableExpressions.emplace_back(variable.getName(), variable.getExpressionVariable().getExpression());
1220 }
1221 }
1222 }
1223 return NextStateGenerator<ValueType, StateType>::label(stateStorage, initialStateIndices, deadlockStateIndices, unexploredStateIndices,
1224 transientVariableExpressions);
1225}
1226
1227template<typename ValueType, typename StateType>
1228std::vector<ValueType> JaniNextStateGenerator<ValueType, StateType>::evaluateRewardExpressions() const {
1229 std::vector<ValueType> result;
1230 result.reserve(rewardExpressions.size());
1231 for (auto const& rewardExpression : rewardExpressions) {
1232 result.push_back(this->evaluator->asRational(rewardExpression.second));
1233 }
1234 return result;
1235}
1236
1237template<typename ValueType, typename StateType>
1238void JaniNextStateGenerator<ValueType, StateType>::addEvaluatedRewardExpressions(std::vector<ValueType>& rewards, ValueType const& factor) const {
1239 STORM_LOG_ASSERT(rewards.size() == rewardExpressions.size(), "Reward count mismatch.");
1240 auto rewIt = rewards.begin();
1241 for (auto const& rewardExpression : rewardExpressions) {
1242 (*rewIt) += factor * this->evaluator->asRational(rewardExpression.second);
1243 ++rewIt;
1244 }
1245}
1246
1247template<typename ValueType, typename StateType>
1248void JaniNextStateGenerator<ValueType, StateType>::buildRewardModelInformation() {
1249 for (auto const& rewardModel : rewardExpressions) {
1250 storm::jani::RewardModelInformation info(this->model, rewardModel.second);
1251 rewardModelInformation.emplace_back(rewardModel.first, info.hasStateRewards(), false, false);
1252 STORM_LOG_THROW(this->options.isScaleAndLiftTransitionRewardsSet() || !info.hasTransitionRewards(), storm::exceptions::NotSupportedException,
1253 "Transition rewards are not supported and a reduction to action-based rewards was not possible.");
1254 if (info.hasTransitionRewards()) {
1255 evaluateRewardExpressionsAtDestinations = true;
1256 }
1257 if (info.hasActionRewards() || (this->options.isScaleAndLiftTransitionRewardsSet() && info.hasTransitionRewards())) {
1258 hasStateActionRewards = true;
1259 rewardModelInformation.back().setHasStateActionRewards();
1260 }
1261 }
1262 if (!hasStateActionRewards) {
1263 evaluateRewardExpressionsAtDestinations = false;
1264 evaluateRewardExpressionsAtEdges = false;
1265 }
1266}
1267
1268template<typename ValueType, typename StateType>
1269void JaniNextStateGenerator<ValueType, StateType>::createSynchronizationInformation() {
1270 // Create synchronizing edges information.
1271 storm::jani::Composition const& topLevelComposition = this->model.getSystemComposition();
1272 if (topLevelComposition.isAutomatonComposition()) {
1273 auto const& automaton = this->model.getAutomaton(topLevelComposition.asAutomatonComposition().getAutomatonName());
1274 this->parallelAutomata.push_back(automaton);
1275
1276 LocationsAndEdges locationsAndEdges;
1277 uint64_t edgeIndex = 0;
1278 for (auto const& edge : automaton.getEdges()) {
1279 locationsAndEdges[edge.getSourceLocationIndex()].emplace_back(std::make_pair(edgeIndex, &edge));
1280 ++edgeIndex;
1281 }
1282
1283 AutomataAndEdges automataAndEdges;
1284 automataAndEdges.emplace_back(std::make_pair(0, std::move(locationsAndEdges)));
1285
1286 this->edges.emplace_back(std::make_pair(boost::none, std::move(automataAndEdges)));
1287 } else {
1288 STORM_LOG_THROW(topLevelComposition.isParallelComposition(), storm::exceptions::WrongFormatException, "Expected parallel composition.");
1289 storm::jani::ParallelComposition const& parallelComposition = topLevelComposition.asParallelComposition();
1290
1291 uint64_t automatonIndex = 0;
1292 for (auto const& composition : parallelComposition.getSubcompositions()) {
1293 STORM_LOG_THROW(composition->isAutomatonComposition(), storm::exceptions::WrongFormatException, "Expected flat parallel composition.");
1294 STORM_LOG_THROW(composition->asAutomatonComposition().getInputEnabledActions().empty(), storm::exceptions::NotSupportedException,
1295 "Input-enabled actions are not supported right now.");
1296
1297 this->parallelAutomata.push_back(this->model.getAutomaton(composition->asAutomatonComposition().getAutomatonName()));
1298
1299 // Add edges with silent action.
1300 LocationsAndEdges locationsAndEdges;
1301 uint64_t edgeIndex = 0;
1302 for (auto const& edge : parallelAutomata.back().get().getEdges()) {
1304 locationsAndEdges[edge.getSourceLocationIndex()].emplace_back(std::make_pair(edgeIndex, &edge));
1305 }
1306 ++edgeIndex;
1307 }
1308
1309 if (!locationsAndEdges.empty()) {
1310 AutomataAndEdges automataAndEdges;
1311 automataAndEdges.emplace_back(std::make_pair(automatonIndex, std::move(locationsAndEdges)));
1312 this->edges.emplace_back(std::make_pair(boost::none, std::move(automataAndEdges)));
1313 }
1314 ++automatonIndex;
1315 }
1316
1317 for (auto const& vector : parallelComposition.getSynchronizationVectors()) {
1318 uint64_t outputActionIndex = this->model.getActionIndex(vector.getOutput());
1319
1320 AutomataAndEdges automataAndEdges;
1321 bool atLeastOneEdge = true;
1322 uint64_t automatonIndex = 0;
1323 for (auto const& element : vector.getInput()) {
1325 LocationsAndEdges locationsAndEdges;
1326 uint64_t actionIndex = this->model.getActionIndex(element);
1327 uint64_t edgeIndex = 0;
1328 for (auto const& edge : parallelAutomata[automatonIndex].get().getEdges()) {
1329 if (edge.getActionIndex() == actionIndex) {
1330 locationsAndEdges[edge.getSourceLocationIndex()].emplace_back(std::make_pair(edgeIndex, &edge));
1331 }
1332 ++edgeIndex;
1333 }
1334 if (locationsAndEdges.empty()) {
1335 atLeastOneEdge = false;
1336 break;
1337 }
1338 automataAndEdges.emplace_back(std::make_pair(automatonIndex, std::move(locationsAndEdges)));
1339 }
1340 ++automatonIndex;
1341 }
1342
1343 if (atLeastOneEdge) {
1344 this->edges.emplace_back(std::make_pair(outputActionIndex, std::move(automataAndEdges)));
1345 }
1346 }
1347 }
1348
1349 STORM_LOG_TRACE("Number of synchronizations: " << this->edges.size() << ".");
1350}
1351
1352template<typename ValueType, typename StateType>
1353std::shared_ptr<storm::storage::sparse::ChoiceOrigins> JaniNextStateGenerator<ValueType, StateType>::generateChoiceOrigins(
1354 std::vector<boost::any>& dataForChoiceOrigins) const {
1355 if (!this->getOptions().isBuildChoiceOriginsSet()) {
1356 return nullptr;
1357 }
1358
1359 std::vector<uint_fast64_t> identifiers;
1360 identifiers.reserve(dataForChoiceOrigins.size());
1361
1362 std::map<EdgeIndexSet, uint_fast64_t> edgeIndexSetToIdentifierMap;
1363 // The empty edge set (i.e., the choices without origin) always has to get identifier getIdentifierForChoicesWithNoOrigin() -- which is assumed to be 0
1364 STORM_LOG_ASSERT(storm::storage::sparse::ChoiceOrigins::getIdentifierForChoicesWithNoOrigin() == 0, "The no origin identifier is assumed to be zero.");
1365 edgeIndexSetToIdentifierMap.insert(std::make_pair(EdgeIndexSet(), 0));
1366 uint_fast64_t currentIdentifier = 1;
1367 for (boost::any& originData : dataForChoiceOrigins) {
1368 STORM_LOG_ASSERT(originData.empty() || boost::any_cast<EdgeIndexSet>(&originData) != nullptr,
1369 "Origin data has unexpected type: " << originData.type().name() << ".");
1370
1371 EdgeIndexSet currentEdgeIndexSet = originData.empty() ? EdgeIndexSet() : boost::any_cast<EdgeIndexSet>(std::move(originData));
1372 auto insertionRes = edgeIndexSetToIdentifierMap.emplace(std::move(currentEdgeIndexSet), currentIdentifier);
1373 identifiers.push_back(insertionRes.first->second);
1374 if (insertionRes.second) {
1375 ++currentIdentifier;
1376 }
1377 }
1378
1379 std::vector<EdgeIndexSet> identifierToEdgeIndexSetMapping(currentIdentifier);
1380 for (auto const& setIdPair : edgeIndexSetToIdentifierMap) {
1381 identifierToEdgeIndexSetMapping[setIdPair.second] = setIdPair.first;
1382 }
1383
1384 return std::make_shared<storm::storage::sparse::JaniChoiceOrigins>(std::make_shared<storm::jani::Model>(model), std::move(identifiers),
1385 std::move(identifierToEdgeIndexSetMapping));
1386}
1387
1388template<typename ValueType, typename StateType>
1389storm::storage::BitVector JaniNextStateGenerator<ValueType, StateType>::evaluateObservationLabels(CompressedState const& /*state*/) const {
1390 STORM_LOG_WARN("There are no observation labels in JANI currenty");
1391 return storm::storage::BitVector(0);
1392}
1393
1394template<typename ValueType, typename StateType>
1395void JaniNextStateGenerator<ValueType, StateType>::checkValid() const {
1396 // If the program still contains undefined constants and we are not in a parametric setting, assemble an appropriate error message.
1397 if (!std::is_same<ValueType, storm::RationalFunction>::value && model.hasUndefinedConstants()) {
1398 std::vector<std::reference_wrapper<storm::jani::Constant const>> undefinedConstants = model.getUndefinedConstants();
1399 std::stringstream stream;
1400 bool printComma = false;
1401 for (auto const& constant : undefinedConstants) {
1402 if (printComma) {
1403 stream << ", ";
1404 } else {
1405 printComma = true;
1406 }
1407 stream << constant.get().getName() << " (" << constant.get().getType() << ")";
1408 }
1409 stream << ".";
1410 STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Program still contains these undefined constants: " + stream.str() + ".");
1411 } else if (std::is_same<ValueType, storm::RationalFunction>::value && !model.undefinedConstantsAreGraphPreserving()) {
1412 STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException,
1413 "The input model contains undefined constants that influence the graph structure of the underlying model, which is not allowed.");
1414 }
1415}
1416
1417template class JaniNextStateGenerator<double>;
1418template class JaniNextStateGenerator<storm::RationalNumber>;
1419template class JaniNextStateGenerator<storm::RationalFunction>;
1420} // namespace generator
1421} // namespace storm
uint64_t getReservedBitsForUnboundedVariables() const
std::set< std::string > const & getRewardModelNames() const
Which reward models are built.
std::vector< std::pair< LabelOrExpression, bool > > const & getTerminalStates() const
ExpressionManager const & getManager() const
Retrieves the manager responsible for this expression.
bool isInitialized() const
Checks whether the object encapsulates a base-expression.
Expression integer(int_fast64_t value) const
Creates an expression that characterizes the given integer literal.
bool isBooleanType() const
Checks whether this type is a boolean type.
Definition Type.cpp:194
bool isIntegerType() const
Checks whether this type is an integral type.
Definition Type.cpp:198
bool isRationalType() const
Checks whether this type is a rational type.
Definition Type.cpp:234
storm::expressions::Expression getExpression() const
Retrieves an expression that represents the variable.
Definition Variable.cpp:34
void clear()
Clears this distribution.
ContainerType::iterator begin()
Access to iterators over the entries of the distribution.
void compress()
Compresses the internal storage by summing the values of entries which agree on the index.
ContainerType::iterator end()
void add(DistributionEntry< IndexType, ValueType > const &entry)
Adds the given entry to the distribution.
storm::storage::FlatSet< uint_fast64_t > EdgeIndexSet
virtual ModelType getModelType() const override
static storm::jani::ModelFeatures getSupportedJaniFeatures()
Returns the jani features with which this builder can deal natively.
virtual std::shared_ptr< storm::storage::sparse::ChoiceOrigins > generateChoiceOrigins(std::vector< boost::any > &dataForChoiceOrigins) const override
virtual StateBehavior< ValueType, StateType > expand(StateToIdCallback const &stateToIdCallback) override
virtual std::vector< StateType > getInitialStates(StateToIdCallback const &stateToIdCallback) override
JaniNextStateGenerator(storm::jani::Model const &model, NextStateGeneratorOptions const &options=NextStateGeneratorOptions())
virtual storm::builder::RewardModelInformation getRewardModelInformation(uint64_t const &index) const override
virtual bool isPartiallyObservable() const override
virtual std::size_t getNumberOfRewardModels() const override
static bool canHandle(storm::jani::Model const &model)
A quick check to detect whether the given model is not supported.
NextStateGenerator< ValueType, StateType >::StateToIdCallback StateToIdCallback
virtual storm::models::sparse::StateLabeling label(storm::storage::sparse::StateStorage< StateType > const &stateStorage, std::vector< StateType > const &initialStateIndices={}, std::vector< StateType > const &deadlockStateIndices={}, std::vector< StateType > const &unexploredStateIndices={}) override
virtual void addStateValuation(storm::storage::sparse::state_type const &currentStateIndex, storm::storage::sparse::Valuations &valuations) const override
Adds the valuation for the currently loaded state to the given builder.
virtual void unpackTransientVariableValuesIntoEvaluator(CompressedState const &state, storm::expressions::ExpressionEvaluator< ValueType > &evaluator) const override
Sets the values of all transient variables in the current state to the given evaluator.
virtual storm::storage::sparse::Valuations initializeStateValuations() const override
Initializes state valuations by adding the appropriate variables.
std::unique_ptr< storm::expressions::ExpressionEvaluator< BaseValueType > > evaluator
void postprocess(StateBehavior< ValueType, uint32_t > &result)
boost::optional< std::vector< uint64_t > > overlappingGuardStates
std::vector< std::pair< storm::expressions::Expression, bool > > terminalStates
virtual storm::models::sparse::StateLabeling label(storm::storage::sparse::StateStorage< StateType > const &stateStorage, std::vector< StateType > const &initialStateIndices={}, std::vector< StateType > const &deadlockStateIndices={}, std::vector< StateType > const &unexploredStateIndices={})=0
void addChoice(Choice< ValueType, StateType > &&choice)
Adds the given choice to the behavior of the state.
void addStateRewards(std::vector< ValueType > &&stateRewards)
Adds the given state rewards to the behavior of the state.
void setExpanded(bool newValue=true)
Sets whether the state was expanded.
std::string const & getName() const
Returns the name of the location.
Definition Action.cpp:9
std::string const & getAutomatonName() const
Retrieves the name of the automaton this composition element refers to.
bool isBooleanType() const
Definition BasicType.cpp:20
virtual bool isAutomatonComposition() const
virtual bool isParallelComposition() const
AutomatonComposition const & asAutomatonComposition() const
ParallelComposition const & asParallelComposition() const
storm::expressions::Expression const & getProbability() const
Retrieves the probability of choosing this destination.
bool hasTransientAssignment() const
Retrieves whether this destination has transient assignments.
OrderedAssignments const & getOrderedAssignments() const
Retrieves the assignments to make when choosing this destination.
uint64_t getLocationIndex() const
Retrieves the id of the destination location.
uint64_t getActionIndex() const
Retrieves the id of the action with which this edge is labeled.
Definition Edge.cpp:45
std::vector< EdgeDestination > const & getDestinations() const
Retrieves the destinations of this edge.
Definition Edge.cpp:77
bool hasRate() const
Retrieves whether this edge has an associated rate.
Definition Edge.cpp:49
uint64_t getSourceLocationIndex() const
Retrieves the index of the source location.
Definition Edge.cpp:41
OrderedAssignments const & getAssignments() const
Retrieves the assignments of this edge.
Definition Edge.cpp:89
storm::expressions::Expression const & getRate() const
Retrieves the rate of this edge.
Definition Edge.cpp:53
int64_t const & getHighestAssignmentLevel() const
Retrieves the highest assignment level occurring in a destination assignment If no assignment exists,...
Definition Edge.cpp:147
std::size_t getNumberOfDestinations() const
Retrieves the number of destinations of this edge.
Definition Edge.cpp:85
int64_t const & getLowestAssignmentLevel() const
Retrieves the lowest assignment level occurring in a destination assignment.
Definition Edge.cpp:143
EdgeDestination const & getDestination(uint64_t index) const
Retrieves the destination with the given index.
Definition Edge.cpp:73
virtual bool isBasicType() const
Definition JaniType.cpp:11
BasicType const & asBasicType() const
Definition JaniType.cpp:31
OrderedAssignments const & getAssignments() const
Retrieves the assignments of this location.
Definition Location.cpp:23
ModelFeatures & add(ModelFeature const &modelFeature)
void remove(ModelFeature const &modelFeature)
bool hasUndefinedConstants() const
Retrieves whether the model still has undefined constants.
Definition Model.cpp:1079
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
storm::expressions::Expression getRewardModelExpression(std::string const &identifier) const
Retrieves the defining reward expression of the reward model with the given identifier.
Definition Model.cpp:809
static uint64_t encodeAutomatonAndEdgeIndices(uint64_t automatonIndex, uint64_t edgeIndex)
Encode and decode a tuple of automaton and edge index in one 64-bit index.
Definition Model.cpp:1600
Composition const & getSystemComposition() const
Retrieves the system composition expression.
Definition Model.cpp:945
void liftTransientEdgeDestinationAssignments(int64_t maxLevel=0)
Lifts the common edge destination assignments of transient variables to edge assignments.
Definition Model.cpp:1556
static const uint64_t SILENT_ACTION_INDEX
The index of the silent action.
Definition Model.h:658
bool hasNonTrivialRewardExpression() const
Returns true iff there is a non-trivial reward model, i.e., a reward model that does not consist of a...
Definition Model.cpp:789
bool undefinedConstantsAreGraphPreserving() const
Checks that undefined constants (parameters) of the model preserve the graph of the underlying model.
Definition Model.cpp:1497
void pushEdgeAssignmentsToDestinations()
Definition Model.cpp:1550
Action const & getAction(uint64_t index) const
Retrieves the action with the given index.
Definition Model.cpp:624
std::vector< std::pair< std::string, storm::expressions::Expression > > getAllRewardModelExpressions() const
Retrieves all available reward model names and expressions of the model.
Definition Model.cpp:837
bool isNonTrivialRewardModelExpression(std::string const &identifier) const
Returns true iff the given identifier corresponds to a non-trivial reward expression i....
Definition Model.cpp:793
void simplifyComposition()
Attempts to simplify the composition.
Definition Model.cpp:981
ModelFeatures const & getModelFeatures() const
Retrieves the enabled model features.
Definition Model.cpp:125
Automaton & getAutomaton(std::string const &name)
Retrieves the automaton with the given name.
Definition Model.cpp:884
bool hasNonTrivialInitialStates() const
Retrieves whether there are non-trivial initial states in the model or any of the contained automata.
Definition Model.cpp:1303
uint64_t getActionIndex(std::string const &name) const
Get the index of the action.
Definition Model.cpp:632
bool usesAssignmentLevels(bool onlyTransient=false) const
Retrieves whether the model uses an assignment level other than zero.
Definition Model.cpp:1571
uint64_t getAutomatonIndex(std::string const &name) const
Retrieves the index of the given automaton.
Definition Model.cpp:904
std::vector< std::reference_wrapper< Constant const > > getUndefinedConstants() const
Retrieves all undefined constants of the model.
Definition Model.cpp:1088
bool hasMultipleLevels(bool onlyTransient=false) const
Checks whether the assignments have several levels.
bool empty(bool onlyTransient=false) const
Retrieves whether this set of assignments is empty.
int64_t getLowestLevel(bool onlyTransient=false) const
Retrieves the lowest level among all assignments.
detail::ConstAssignments getNonTransientAssignments() const
Returns all non-transient assignments in this set of assignments.
detail::ConstAssignments getTransientAssignments() const
Returns all transient assignments in this set of assignments.
int64_t getHighestLevel(bool onlyTransient=false) const
Retrieves the highest level among all assignments.
std::vector< SynchronizationVector > const & getSynchronizationVectors() const
Retrieves the synchronization vectors of the parallel composition.
std::vector< std::shared_ptr< Composition > > const & getSubcompositions() const
Retrieves the subcompositions of the parallel composition.
static bool isNoActionInput(std::string const &action)
storm::expressions::Variable const & getExpressionVariable() const
Retrieves the associated expression variable.
Definition Variable.cpp:26
JaniType & getType()
Definition Variable.cpp:67
bool isTransient() const
Definition Variable.cpp:42
bool hasVariable(std::string const &name) const
Retrieves whether this variable set contains a variable with the given name.
Variable const & getVariable(std::string const &name) const
Retrieves the variable with the given name.
This class manages the labeling of the state space with a number of (atomic) labels.
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
void setFromInt(uint64_t bitIndex, uint64_t numberOfBits, uint64_t value)
Sets the selected number of lowermost bits of the provided value at the given bit index.
void set(uint64_t index, bool value=true)
Sets the given truth value at the given index.
uint64_t getAsInt(uint64_t bitIndex, uint64_t numberOfBits) const
Retrieves the content of the current bit vector at the given index for the given number of bits as an...
static uint_fast64_t getIdentifierForChoicesWithNoOrigin()
Helper to incrementally build a ValuationClassDescription, i.e.
Provides access to valuations of variables for a set of entities (e.g.
Definition Valuations.h:28
ValuationsStorage const & getStorage() const
virtual std::unique_ptr< storm::solver::SmtSolver > create(storm::expressions::ExpressionManager &manager) const
Creates a new SMT solver instance.
Definition solver.cpp:159
#define STORM_LOG_INFO(message)
Definition logging.h:27
#define STORM_LOG_WARN(message)
Definition logging.h:28
#define STORM_LOG_DEBUG(message)
Definition logging.h:21
#define STORM_LOG_TRACE(message)
Definition logging.h:15
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:28
SFTBDDChecker::ValueType ValueType
void unpackStateIntoEvaluator(CompressedState const &state, VariableInformation const &variableInformation, storm::expressions::ExpressionEvaluator< ValueType > &evaluator)
Unpacks the compressed state into the evaluator.
void unpackStateAppendToValuations(CompressedState const &state, VariableInformation const &variableInformation, storm::storage::sparse::ValuationsStorage &valuations)
Appends the values of the variables in the given state to the valuations object.
storm::storage::BitVector CompressedState
storm::builder::BuilderOptions NextStateGeneratorOptions
storm::adapters::DereferenceIteratorAdapter< std::vector< std::shared_ptr< Assignment > > const > ConstAssignments
bool isDiscreteTimeModel(ModelType const &modelType)
Definition ModelType.cpp:89
void forEach(std::vector< IteratorType > const &its, std::vector< IteratorType > const &ites, std::function< void(uint64_t, decltype(*std::declval< IteratorType >()))> const &setValueCallback, std::function< bool()> const &newCombinationCallback)
std::vector< T > buildVectorForRange(T min, T max)
Constructs a vector [min, min+1, ...., max-1].
Definition vector.h:129
bool isConstant(ValueType const &)
bool isZero(ValueType const &a)
Definition constants.cpp:42
ValueType zero()
Definition constants.cpp:24
ValueType one()
Definition constants.cpp:19
void addOriginData(boost::any const &data)
Adds the given data that specifies the origin of this choice w.r.t.
Definition Choice.cpp:116
void addRewards(std::vector< ValueType > &&values)
Adds the given choices rewards to this choice.
Definition Choice.cpp:169
void addProbability(StateType const &state, ValueType const &value)
Adds the given probability value to the given state in the underlying distribution.
Definition Choice.cpp:158