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