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