Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
PrismNextStateGenerator.cpp
Go to the documentation of this file.
2
3#include <boost/any.hpp>
4
21namespace storm {
22namespace generator {
23
24template<typename ValueType, typename StateType>
26 std::shared_ptr<ActionMask<ValueType, StateType>> const& mask)
27 : PrismNextStateGenerator<ValueType, StateType>(program.substituteConstantsFormulas(), options, mask, false) {
28 // Intentionally left empty.
29}
31template<typename ValueType, typename StateType>
33 std::shared_ptr<ActionMask<ValueType, StateType>> const& mask, bool)
34 : NextStateGenerator<ValueType, StateType>(program.getManager(), options, mask), program(program), rewardModels(), hasStateActionRewards(false) {
35 STORM_LOG_TRACE("Creating next-state generator for PRISM program: " << program);
36 STORM_LOG_THROW(!this->program.specifiesSystemComposition(), storm::exceptions::WrongFormatException,
37 "The explicit next-state generator currently does not support custom system compositions.");
38 STORM_LOG_THROW(storm::IsIntervalType<ValueType> || !this->program.hasIntervalUpdates(), storm::exceptions::WrongFormatException,
39 "The provided PRISM program contains interval updates, which are not supported by the chosen value type.");
41 // Only after checking validity of the program, we initialize the variable information.
42 this->checkValid();
43 this->variableInformation = VariableInformation(program, options.getReservedBitsForUnboundedVariables(), options.isAddOutOfBoundsStateSet());
45
46 // Create a proper evaluator.
47 this->evaluator = std::make_unique<storm::expressions::ExpressionEvaluator<BaseValueType>>(program.getManager());
49 if (this->options.isBuildAllRewardModelsSet()) {
50 for (auto const& rewardModel : this->program.getRewardModels()) {
51 rewardModels.push_back(rewardModel);
52 }
53 } else {
54 // Extract the reward models from the program based on the names we were given.
55 for (auto const& rewardModelName : this->options.getRewardModelNames()) {
56 if (this->program.hasRewardModel(rewardModelName)) {
57 rewardModels.push_back(this->program.getRewardModel(rewardModelName));
58 } else {
59 STORM_LOG_THROW(rewardModelName.empty(), storm::exceptions::InvalidArgumentException,
60 "Cannot build unknown reward model '" << rewardModelName << "'.");
61 STORM_LOG_THROW(this->program.getNumberOfRewardModels() == 1, storm::exceptions::InvalidArgumentException,
62 "Reference to standard reward model is ambiguous.");
63 }
64 }
65
66 // If no reward model was yet added, but there was one that was given in the options, we try to build the
67 // standard reward model.
68 if (rewardModels.empty() && !this->options.getRewardModelNames().empty()) {
69 rewardModels.push_back(this->program.getRewardModel(0));
70 }
71 }
72
73 // Determine whether any reward model has state action rewards.
74 for (auto const& rewardModel : rewardModels) {
75 hasStateActionRewards |= rewardModel.get().hasStateActionRewards();
76 }
77
78 // If there are terminal states we need to handle, we now need to translate all labels to expressions.
79 if (this->options.hasTerminalStates()) {
80 for (auto const& expressionOrLabelAndBool : this->options.getTerminalStates()) {
81 if (expressionOrLabelAndBool.first.isExpression()) {
82 this->terminalStates.push_back(std::make_pair(expressionOrLabelAndBool.first.getExpression(), expressionOrLabelAndBool.second));
83 } else {
84 if (program.hasLabel(expressionOrLabelAndBool.first.getLabel())) {
85 this->terminalStates.push_back(
86 std::make_pair(this->program.getLabelExpression(expressionOrLabelAndBool.first.getLabel()), expressionOrLabelAndBool.second));
87 } else {
88 // If the label is not present in the program and is not a special one, we raise an error.
89 STORM_LOG_THROW(this->isSpecialLabel(expressionOrLabelAndBool.first.getLabel()), storm::exceptions::InvalidArgumentException,
90 "Terminal states refer to illegal label '" << expressionOrLabelAndBool.first.getLabel() << "'.");
91 }
92 }
93 }
94 }
95
97 moduleIndexToPlayerIndexMap = program.buildModuleIndexToPlayerIndexMap();
98 actionIndexToPlayerIndexMap = program.buildActionIndexToPlayerIndexMap();
99 }
100}
101
102template<typename ValueType, typename StateType>
104 // We can handle all valid prism programs (except for PTAs)
105 return program.getModelType() != storm::prism::Program::ModelType::PTA;
106}
107
108template<typename ValueType, typename StateType>
109void PrismNextStateGenerator<ValueType, StateType>::checkValid() const {
110 // If the program still contains undefined constants and we are not in a parametric setting, assemble an appropriate error message.
111 if (!std::is_same<ValueType, storm::RationalFunction>::value && program.hasUndefinedConstants()) {
112 std::vector<std::reference_wrapper<storm::prism::Constant const>> undefinedConstants = program.getUndefinedConstants();
113 std::stringstream stream;
114 bool printComma = false;
115 for (auto const& constant : undefinedConstants) {
116 if (printComma) {
117 stream << ", ";
118 } else {
119 printComma = true;
120 }
121 stream << constant.get().getName() << " (" << constant.get().getType() << ")";
122 }
123 stream << ".";
124 STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Program still contains these undefined constants: " + stream.str() + ".");
125 } else if (std::is_same<ValueType, storm::RationalFunction>::value && !program.undefinedConstantsAreGraphPreserving()) {
126 auto undef = program.getUndefinedConstantsAsString();
127 STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException,
128 "The program contains undefined constants that appear in some places other than update probabilities and reward value expressions, "
129 "which is not admitted. Undefined constants are: "
130 << undef << ".");
131 }
132}
133
134template<typename ValueType, typename StateType>
136 switch (program.getModelType()) {
138 return ModelType::DTMC;
140 return ModelType::CTMC;
142 return ModelType::MDP;
144 return ModelType::MA;
146 return ModelType::POMDP;
148 return ModelType::SMG;
149 default:
150 STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Invalid model type.");
151 }
152}
153
154template<typename ValueType, typename StateType>
156 return program.isDeterministicModel();
157}
158
159template<typename ValueType, typename StateType>
161 return program.isDiscreteTimeModel();
162}
163
164template<typename ValueType, typename StateType>
166 return program.isPartiallyObservable();
167}
168
169template<typename ValueType, typename StateType>
171 std::vector<StateType> initialStateIndices;
172
173 // If all states are initial, we can simplify the enumeration substantially.
174 if (program.hasInitialConstruct() && program.getInitialStatesExpression().isTrue()) {
175 // Create vectors holding all possible values
176 std::vector<std::vector<uint64_t>> allValues;
177 for (auto const& intVar : this->variableInformation.integerVariables) {
178 STORM_LOG_ASSERT(intVar.lowerBound <= intVar.upperBound, "Expecting variable with non-empty set of possible values.");
179 // The value of integer variables is shifted so that 0 is always the smallest possible value
180 allValues.push_back(storm::utility::vector::buildVectorForRange<uint64_t>(static_cast<uint64_t>(0), intVar.upperBound + 1 - intVar.lowerBound));
181 }
182 uint64_t intEndIndex = allValues.size();
183 // For boolean variables we consider the values 0 and 1.
184 allValues.resize(allValues.size() + this->variableInformation.booleanVariables.size(),
185 std::vector<uint64_t>({static_cast<uint64_t>(0), static_cast<uint64_t>(1)}));
186
187 std::vector<std::vector<uint64_t>::const_iterator> its;
188 std::vector<std::vector<uint64_t>::const_iterator> ites;
189 for (auto const& valVec : allValues) {
190 its.push_back(valVec.cbegin());
191 ites.push_back(valVec.cend());
192 }
193
194 // Now create an initial state for each combination of values
195 CompressedState initialState(this->variableInformation.getTotalBitOffset(true));
197 its, ites,
198 [this, &initialState, &intEndIndex](uint64_t index, uint64_t value) {
199 // Set the value for the variable corresponding to the given index
200 if (index < intEndIndex) {
201 // Integer variable
202 auto const& intVar = this->variableInformation.integerVariables[index];
203 initialState.setFromInt(intVar.bitOffset, intVar.bitWidth, value);
204 } else {
205 // Boolean variable
206 STORM_LOG_ASSERT(index - intEndIndex < this->variableInformation.booleanVariables.size(), "Unexpected index.");
207 auto const& boolVar = this->variableInformation.booleanVariables[index - intEndIndex];
208 STORM_LOG_ASSERT(value <= 1u, "Unexpected value for boolean variable.");
209 initialState.set(boolVar.bitOffset, static_cast<bool>(value));
210 }
211 },
212 [&stateToIdCallback, &initialStateIndices, &initialState]() {
213 // Register initial state.
214 StateType id = stateToIdCallback(initialState);
215 initialStateIndices.push_back(id);
216 return true; // Keep on exploring
217 });
218 STORM_LOG_DEBUG("Enumerated " << initialStateIndices.size() << " initial states using brute force enumeration.");
219 } else {
220 // Prepare an SMT solver to enumerate all initial states.
222 std::unique_ptr<storm::solver::SmtSolver> solver = factory.create(program.getManager());
223
224 std::vector<storm::expressions::Expression> rangeExpressions = program.getAllRangeExpressions();
225 for (auto const& expression : rangeExpressions) {
226 solver->add(expression);
227 }
228 solver->add(program.getInitialStatesExpression());
229
230 // Proceed ss long as the solver can still enumerate initial states.
232 // Create fresh state.
233 CompressedState initialState(this->variableInformation.getTotalBitOffset(true));
234
235 // Read variable assignment from the solution of the solver. Also, create an expression we can use to
236 // prevent the variable assignment from being enumerated again.
237 storm::expressions::Expression blockingExpression;
238 std::shared_ptr<storm::solver::SmtSolver::ModelReference> model = solver->getModel();
239 for (auto const& booleanVariable : this->variableInformation.booleanVariables) {
240 bool variableValue = model->getBooleanValue(booleanVariable.variable);
241 storm::expressions::Expression localBlockingExpression = variableValue ? !booleanVariable.variable : booleanVariable.variable;
242 blockingExpression = blockingExpression.isInitialized() ? blockingExpression || localBlockingExpression : localBlockingExpression;
243 initialState.set(booleanVariable.bitOffset, variableValue);
244 }
245 for (auto const& integerVariable : this->variableInformation.integerVariables) {
246 int_fast64_t variableValue = model->getIntegerValue(integerVariable.variable);
247 storm::expressions::Expression localBlockingExpression = integerVariable.variable != model->getManager().integer(variableValue);
248 blockingExpression = blockingExpression.isInitialized() ? blockingExpression || localBlockingExpression : localBlockingExpression;
249 initialState.setFromInt(integerVariable.bitOffset, integerVariable.bitWidth,
250 static_cast<uint_fast64_t>(variableValue - integerVariable.lowerBound));
251 }
252
253 // Register initial state and return it.
254 StateType id = stateToIdCallback(initialState);
255 initialStateIndices.push_back(id);
256
257 // Block the current initial state to search for the next one.
258 if (!blockingExpression.isInitialized()) {
259 break;
260 }
261 solver->add(blockingExpression);
262 }
263
264 STORM_LOG_DEBUG("Enumerated " << initialStateIndices.size() << " initial states using SMT solving.");
265 }
266
267 return initialStateIndices;
268}
269
270template<typename ValueType, typename StateType>
272 // Prepare the result, in case we return early.
274
275 // First, construct the state rewards, as we may return early if there are no choices later and we already
276 // need the state rewards then.
277 for (auto const& rewardModel : rewardModels) {
278 ValueType stateRewardValue = storm::utility::zero<ValueType>();
279 if (rewardModel.get().hasStateRewards()) {
280 for (auto const& stateReward : rewardModel.get().getStateRewards()) {
281 if (this->evaluator->asBool(stateReward.getStatePredicateExpression())) {
282 stateRewardValue += ValueType(this->evaluator->asRational(stateReward.getRewardValueExpression()));
283 }
284 }
285 }
286 result.addStateReward(stateRewardValue);
287 }
288
289 // If a terminal expression was set, we must not expand this state
290 if (!this->terminalStates.empty()) {
291 for (auto const& expressionBool : this->terminalStates) {
292 if (this->evaluator->asBool(expressionBool.first) == expressionBool.second) {
293 if (!isPartiallyObservable()) {
294 // If the model is not partially observable, return.
295 return result;
296 } else {
297 // for partially observable models, we need to add self-loops for all enabled actions.
298 result.setExpanded();
299 std::vector<Choice<ValueType>> allChoices = getSelfLoopsForAllActions(*this->state, stateToIdCallback);
300 if (allChoices.size() != 0) {
301 for (auto& choice : allChoices) {
302 result.addChoice(std::move(choice));
303 }
304
305 this->postprocess(result);
306 }
307 return result;
308 }
309 }
310 }
311 }
312
313 // Get all choices for the state.
314 result.setExpanded();
315
316 std::vector<Choice<ValueType>> allChoices;
317 if (this->getOptions().isApplyMaximalProgressAssumptionSet()) {
318 // First explore only edges without a rate
319 allChoices = getAsynchronousChoices(*this->state, stateToIdCallback, CommandFilter::Probabilistic);
320 addSynchronousChoices(allChoices, *this->state, stateToIdCallback, CommandFilter::Probabilistic);
321 if (allChoices.empty()) {
322 // Expand the Markovian edges if there are no probabilistic ones.
323 allChoices = getAsynchronousChoices(*this->state, stateToIdCallback, CommandFilter::Markovian);
324 addSynchronousChoices(allChoices, *this->state, stateToIdCallback, CommandFilter::Markovian);
325 }
326 } else {
327 allChoices = getAsynchronousChoices(*this->state, stateToIdCallback);
328 addSynchronousChoices(allChoices, *this->state, stateToIdCallback);
329 }
330
331 // If there is not a single choice, we return immediately, because the state has no behavior (other than
332 // the state reward).
333 if (allChoices.empty()) {
334 return result;
335 }
336
337 // If the model is a deterministic model, we need to fuse the choices into one.
338 if (this->isDeterministicModel() && allChoices.size() > 1) {
339 Choice<ValueType> globalChoice;
340
341 if (this->options.isAddOverlappingGuardLabelSet()) {
342 this->overlappingGuardStates->push_back(stateToIdCallback(*this->state));
343 }
344
345 // For CTMCs, we need to keep track of the total exit rate to scale the action rewards later. For DTMCs
346 // this is equal to the number of choices, which is why we initialize it like this here.
347 ValueType const totalNumberOfChoices = storm::utility::convertNumber<ValueType, uint64_t>(allChoices.size());
348 ValueType totalExitRate = this->isDiscreteTimeModel() ? totalNumberOfChoices : storm::utility::zero<ValueType>();
349
350 // Iterate over all choices and combine the probabilities/rates into one choice.
351 for (auto const& choice : allChoices) {
352 for (auto const& stateProbabilityPair : choice) {
353 if (this->isDiscreteTimeModel()) {
354 globalChoice.addProbability(stateProbabilityPair.first, stateProbabilityPair.second / totalNumberOfChoices);
355 } else {
356 globalChoice.addProbability(stateProbabilityPair.first, stateProbabilityPair.second);
357 }
358 }
359
360 if (hasStateActionRewards && !this->isDiscreteTimeModel()) {
361 totalExitRate += choice.getTotalMass();
362 }
363
364 if (this->options.isBuildChoiceLabelsSet() && choice.hasLabels()) {
365 globalChoice.addLabels(choice.getLabels());
366 }
367
368 if (this->options.isBuildChoiceOriginsSet() && choice.hasOriginData()) {
369 globalChoice.addOriginData(choice.getOriginData());
370 }
371 }
372
373 // Now construct the state-action reward for all selected reward models.
374 for (auto const& rewardModel : rewardModels) {
375 ValueType stateActionRewardValue = storm::utility::zero<ValueType>();
376 if (rewardModel.get().hasStateActionRewards()) {
377 for (auto const& stateActionReward : rewardModel.get().getStateActionRewards()) {
378 for (auto const& choice : allChoices) {
379 if (stateActionReward.getActionIndex() == choice.getActionIndex() &&
380 this->evaluator->asBool(stateActionReward.getStatePredicateExpression())) {
381 stateActionRewardValue +=
382 ValueType(this->evaluator->asRational(stateActionReward.getRewardValueExpression())) * choice.getTotalMass();
383 }
384 }
385 }
386 }
387 if (hasStateActionRewards) {
388 globalChoice.addReward(stateActionRewardValue / totalExitRate);
389 }
390 }
391
392 // Move the newly fused choice in place.
393 allChoices.clear();
394 allChoices.push_back(std::move(globalChoice));
395 }
396
397 // For SMG we check whether the state has a unique player
398 if (program.getModelType() == storm::prism::Program::ModelType::SMG && allChoices.size() > 1) {
399 auto choiceIt = allChoices.begin();
400 STORM_LOG_ASSERT(choiceIt->hasPlayerIndex(),
401 "State '" << this->stateToString(*this->state)
402 << "' features a choice without player index."); // This should have been catched while creating the choice already
403 storm::storage::PlayerIndex statePlayerIndex = choiceIt->getPlayerIndex();
405 "State '" << this->stateToString(*this->state)
406 << "' features a choice with invalid player index."); // This should have been catched while creating the choice already
407 for (++choiceIt; choiceIt != allChoices.end(); ++choiceIt) {
408 STORM_LOG_ASSERT(choiceIt->hasPlayerIndex(),
409 "State '" << this->stateToString(*this->state)
410 << "' features a choice without player index."); // This should have been catched while creating the choice already
411 STORM_LOG_ASSERT(choiceIt->getPlayerIndex() != storm::storage::INVALID_PLAYER_INDEX,
412 "State '" << this->stateToString(*this->state)
413 << "' features a choice with invalid player index."); // This should have been catched while creating the choice already
414 STORM_LOG_THROW(statePlayerIndex == choiceIt->getPlayerIndex(), storm::exceptions::WrongFormatException,
415 "The player for state '" << this->stateToString(*this->state) << "' is not unique. At least one choice is owned by player '"
416 << statePlayerIndex << "' while another is owned by player '" << choiceIt->getPlayerIndex() << "'.");
417 }
418 }
419
420 // Move all remaining choices in place.
421 for (auto& choice : allChoices) {
422 result.addChoice(std::move(choice));
423 }
424
425 this->postprocess(result);
426
427 return result;
428}
429
430template<typename ValueType, typename StateType>
434
435template<typename ValueType, typename StateType>
436CompressedState PrismNextStateGenerator<ValueType, StateType>::applyUpdate(CompressedState const& state, storm::prism::Update const& update) {
437 CompressedState newState(state);
438
439 // NOTE: the following process assumes that the assignments of the update are ordered in such a way that the
440 // assignments to boolean variables precede the assignments to all integer variables and that within the
441 // types, the assignments to variables are ordered (in ascending order) by the expression variables.
442 // This is guaranteed for PRISM models, by sorting the assignments as soon as an update is created.
443
444 auto assignmentIt = update.getAssignments().begin();
445 auto assignmentIte = update.getAssignments().end();
446
447 // Iterate over all boolean assignments and carry them out.
448 auto boolIt = this->variableInformation.booleanVariables.begin();
449 for (; assignmentIt != assignmentIte && assignmentIt->getExpression().hasBooleanType(); ++assignmentIt) {
450 while (assignmentIt->getVariable() != boolIt->variable) {
451 ++boolIt;
452 }
453 newState.set(boolIt->bitOffset, this->evaluator->asBool(assignmentIt->getExpression()));
454 }
455
456 // Iterate over all integer assignments and carry them out.
457 auto integerIt = this->variableInformation.integerVariables.begin();
458 for (; assignmentIt != assignmentIte && assignmentIt->getExpression().hasIntegerType(); ++assignmentIt) {
459 while (assignmentIt->getVariable() != integerIt->variable) {
460 ++integerIt;
461 }
462 int_fast64_t assignedValue = this->evaluator->asInt(assignmentIt->getExpression());
463 if (this->options.isAddOutOfBoundsStateSet()) {
464 if (assignedValue < integerIt->lowerBound || assignedValue > integerIt->upperBound) {
465 return this->outOfBoundsState;
466 }
467 } else if (integerIt->forceOutOfBoundsCheck || this->options.isExplorationChecksSet()) {
468 STORM_LOG_THROW(assignedValue >= integerIt->lowerBound, storm::exceptions::WrongFormatException,
469 "The update " << update << " leads to an out-of-bounds value (" << assignedValue << ") for the variable '"
470 << assignmentIt->getVariableName() << "'.");
471 STORM_LOG_THROW(assignedValue <= integerIt->upperBound, storm::exceptions::WrongFormatException,
472 "The update " << update << " leads to an out-of-bounds value (" << assignedValue << ") for the variable '"
473 << assignmentIt->getVariableName() << "'.");
474 }
475 newState.setFromInt(integerIt->bitOffset, integerIt->bitWidth, assignedValue - integerIt->lowerBound);
476 STORM_LOG_ASSERT(static_cast<int_fast64_t>(newState.getAsInt(integerIt->bitOffset, integerIt->bitWidth)) + integerIt->lowerBound == assignedValue,
477 "Writing to the bit vector bucket failed (read " << newState.getAsInt(integerIt->bitOffset, integerIt->bitWidth) << " but wrote "
478 << assignedValue << ").");
479 }
480
481 // Check that we processed all assignments.
482 STORM_LOG_ASSERT(assignmentIt == assignmentIte, "Not all assignments were consumed.");
483
484 return newState;
485}
486
489 typename std::set<uint_fast64_t>::const_iterator currentCommandIndexIt)
491 // Intentionally left empty
492 }
494 std::set<uint_fast64_t> const* commandIndicesPtr;
495 typename std::set<uint_fast64_t>::const_iterator currentCommandIndexIt;
496};
497
498template<typename ValueType, typename StateType>
499boost::optional<std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>>>
500PrismNextStateGenerator<ValueType, StateType>::getActiveCommandsByActionIndex(uint_fast64_t const& actionIndex, CommandFilter const& commandFilter) {
501 // First check whether there is at least one enabled command at each module
502 // This avoids evaluating unnecessarily many guards.
503 // If we find one module without an enabled command, we return boost::none.
504 // At the same time, we store pointers to the relevant modules, the relevant command sets and the first enabled command within each set.
505
506 // Iterate over all modules.
507 std::vector<ActiveCommandData> activeCommands;
508 for (uint_fast64_t i = 0; i < program.getNumberOfModules(); ++i) {
509 storm::prism::Module const& module = program.getModule(i);
510
511 // If the module has no command labeled with the given action, we can skip this module.
512 if (!module.hasActionIndex(actionIndex)) {
513 continue;
514 }
515
516 std::set<uint_fast64_t> const& commandIndices = module.getCommandIndicesByActionIndex(actionIndex);
517
518 // If the module contains the action, but there is no command in the module that is labeled with
519 // this action, we don't have any feasible command combinations.
520 if (commandIndices.empty()) {
521 return boost::none;
522 }
523
524 // Look up commands by their indices and check if the guard evaluates to true in the given state.
525 bool hasOneEnabledCommand = false;
526 for (auto commandIndexIt = commandIndices.begin(), commandIndexIte = commandIndices.end(); commandIndexIt != commandIndexIte; ++commandIndexIt) {
527 storm::prism::Command const& command = module.getCommand(*commandIndexIt);
528 if (!isCommandPotentiallySynchronizing(command)) {
529 continue;
530 }
531 if (commandFilter != CommandFilter::All) {
532 STORM_LOG_ASSERT(commandFilter == CommandFilter::Markovian || commandFilter == CommandFilter::Probabilistic, "Unexpected command filter.");
533 if ((commandFilter == CommandFilter::Markovian) != command.isMarkovian()) {
534 continue;
535 }
536 }
537 if (this->evaluator->asBool(command.getGuardExpression())) {
538 // Found the first enabled command for this module.
539 hasOneEnabledCommand = true;
540 activeCommands.emplace_back(&module, &commandIndices, commandIndexIt);
541 break;
542 }
543 }
544
545 if (!hasOneEnabledCommand) {
546 return boost::none;
547 }
548 }
549
550 // If we reach this point, there has to be at least one active command for each relevant module.
551 std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>> result;
552
553 // Iterate over all command sets.
554 for (auto const& activeCommand : activeCommands) {
555 std::vector<std::reference_wrapper<storm::prism::Command const>> commands;
556
557 auto commandIndexIt = activeCommand.currentCommandIndexIt;
558 // The command at the current position is already known to be enabled
559 commands.push_back(activeCommand.modulePtr->getCommand(*commandIndexIt));
560
561 // Look up commands by their indices and add them if the guard evaluates to true in the given state.
562 auto commandIndexIte = activeCommand.commandIndicesPtr->end();
563 for (++commandIndexIt; commandIndexIt != commandIndexIte; ++commandIndexIt) {
564 storm::prism::Command const& command = activeCommand.modulePtr->getCommand(*commandIndexIt);
565 if (commandFilter != CommandFilter::All) {
566 STORM_LOG_ASSERT(commandFilter == CommandFilter::Markovian || commandFilter == CommandFilter::Probabilistic, "Unexpected command filter.");
567 if ((commandFilter == CommandFilter::Markovian) != command.isMarkovian()) {
568 continue;
569 }
570 }
571 if (this->evaluator->asBool(command.getGuardExpression())) {
572 commands.push_back(command);
573 }
574 }
575
576 result.push_back(std::move(commands));
577 }
578
579 STORM_LOG_ASSERT(!result.empty(), "Expected non-empty list.");
580 return result;
581}
582
583template<typename ValueType, typename BaseValueType>
585 if constexpr (storm::IsIntervalType<ValueType>) {
586 if (update.isLikelihoodInterval()) {
587 BaseValueType lower = evaluator.asRational(update.getLikelihoodExpressionInterval().first);
588 BaseValueType upper = evaluator.asRational(update.getLikelihoodExpressionInterval().second);
589 return ValueType(lower, carl::BoundType::WEAK, upper, carl::BoundType::WEAK);
590 } else {
591 return evaluator.asRational(update.getLikelihoodExpression());
592 }
593 } else {
594 // We don't expect to see an interval likelihood now. This case should have been catched earlier.
595 STORM_LOG_ASSERT(!update.isLikelihoodInterval(), "Unexpected interval likelihood for non-interval model in update " << update << ".");
596 return evaluator.asRational(update.getLikelihoodExpression());
597 }
598}
599
600template<typename ValueType, typename StateType>
601std::vector<Choice<ValueType>> PrismNextStateGenerator<ValueType, StateType>::getAsynchronousChoices(CompressedState const& state,
602 StateToIdCallback stateToIdCallback,
603 CommandFilter const& commandFilter) {
604 std::vector<Choice<ValueType>> result;
605
606 // Iterate over all modules.
607 for (uint_fast64_t i = 0; i < program.getNumberOfModules(); ++i) {
608 storm::prism::Module const& module = program.getModule(i);
609
610 // Iterate over all commands.
611 for (uint_fast64_t j = 0; j < module.getNumberOfCommands(); ++j) {
612 storm::prism::Command const& command = module.getCommand(j);
613
614 // Only consider commands that are not possibly synchronizing.
615 if (isCommandPotentiallySynchronizing(command)) {
616 continue;
617 }
618
619 if (commandFilter != CommandFilter::All) {
620 STORM_LOG_ASSERT(commandFilter == CommandFilter::Markovian || commandFilter == CommandFilter::Probabilistic, "Unexpected command filter.");
621 if ((commandFilter == CommandFilter::Markovian) != command.isMarkovian()) {
622 continue;
623 }
624 }
625 if (this->actionMask != nullptr) {
626 if (!this->actionMask->query(*this, command.getActionIndex())) {
627 continue;
628 }
629 }
630
631 // Skip the command, if it is not enabled.
632 if (!this->evaluator->asBool(command.getGuardExpression())) {
633 continue;
634 }
635
636 result.push_back(Choice<ValueType>(command.getActionIndex(), command.isMarkovian()));
637 Choice<ValueType>& choice = result.back();
638
639 // Remember the choice origin only if we were asked to.
640 if (this->options.isBuildChoiceOriginsSet()) {
641 CommandSet commandIndex{command.getGlobalIndex()};
642 choice.addOriginData(boost::any(std::move(commandIndex)));
643 }
644
645 // Iterate over all updates of the current command.
647 for (uint_fast64_t k = 0; k < command.getNumberOfUpdates(); ++k) {
648 storm::prism::Update const& update = command.getUpdate(k);
649
650 ValueType probability = evaluateLikelihoodExpression<ValueType>(update, *this->evaluator);
651 if (probability != storm::utility::zero<ValueType>()) {
652 // Obtain target state index and add it to the list of known states. If it has not yet been
653 // seen, we also add it to the set of states that have yet to be explored.
654 StateType stateIndex = stateToIdCallback(applyUpdate(state, update));
655
656 // Update the choice by adding the probability/target state to it.
657 choice.addProbability(stateIndex, probability);
658 if (this->options.isExplorationChecksSet()) {
659 if constexpr (!std::is_same_v<ValueType, storm::RationalFunction>) {
660 STORM_LOG_THROW(probability > storm::utility::zero<ValueType>(), storm::exceptions::WrongFormatException,
661 "Probability expression in update '" << update << " evaluates to negative value " << probability << ".");
663 storm::exceptions::WrongFormatException,
664 "Probability expression in update '" << update << " evaluates to value " << probability << " >1.");
665 }
666 probabilitySum += probability;
667 }
668 }
669 }
670
671 // Create the state-action reward for the newly created choice.
672 for (auto const& rewardModel : rewardModels) {
673 ValueType stateActionRewardValue = storm::utility::zero<ValueType>();
674 if (rewardModel.get().hasStateActionRewards()) {
675 for (auto const& stateActionReward : rewardModel.get().getStateActionRewards()) {
676 if (stateActionReward.getActionIndex() == choice.getActionIndex() &&
677 this->evaluator->asBool(stateActionReward.getStatePredicateExpression())) {
678 stateActionRewardValue += ValueType(this->evaluator->asRational(stateActionReward.getRewardValueExpression()));
679 }
680 }
681 }
682 choice.addReward(stateActionRewardValue);
683 }
684
685 if (this->options.isBuildChoiceLabelsSet() && command.isLabeled()) {
686 choice.addLabel(program.getActionName(command.getActionIndex()));
687 }
688
690 if (command.getActionIndex() == 0) {
691 // Unlabeled command
692 auto const playerOfModule = moduleIndexToPlayerIndexMap.at(i);
693 STORM_LOG_THROW(playerOfModule != storm::storage::INVALID_PLAYER_INDEX, storm::exceptions::WrongFormatException,
694 "Module " << module.getName() << " is not owned by any player but has at least one enabled, unlabeled command.");
695 choice.setPlayerIndex(playerOfModule);
696 } else {
697 // Labelled command (that happens to not synchronize with another command
698 auto const playerOfAction = actionIndexToPlayerIndexMap.at(command.getActionIndex());
699 STORM_LOG_THROW(playerOfAction != storm::storage::INVALID_PLAYER_INDEX, storm::exceptions::WrongFormatException,
700 "Command with action label '" << program.getActionName(command.getActionIndex()) << "' is not owned by any player.");
701 choice.setPlayerIndex(playerOfAction);
702 }
703 }
704
705 if (this->options.isExplorationChecksSet()) {
706 // Check that the resulting distribution is in fact a distribution.
707 STORM_LOG_THROW(!program.isDiscreteTimeModel() || !storm::utility::isConstant(probabilitySum) || this->comparator.isOne(probabilitySum),
708 storm::exceptions::WrongFormatException,
709 "Probabilities do not sum to one for command '" << command << "' (actually sum to " << probabilitySum << ").");
710 }
711 }
712 }
713
714 return result;
715}
716
717template<typename ValueType, typename StateType>
718std::vector<Choice<ValueType>> PrismNextStateGenerator<ValueType, StateType>::getSelfLoopsForAllActions(CompressedState const& state,
719 StateToIdCallback stateToIdCallback,
720 CommandFilter const& commandFilter) {
721 std::vector<Choice<ValueType>> result;
722
723 // Asynchronous actions
724 // Iterate over all modules.
725 for (uint_fast64_t i = 0; i < program.getNumberOfModules(); ++i) {
726 storm::prism::Module const& module = program.getModule(i);
727
728 // Iterate over all commands.
729 for (uint_fast64_t j = 0; j < module.getNumberOfCommands(); ++j) {
730 storm::prism::Command const& command = module.getCommand(j);
731
732 // Only consider commands that are not possibly synchronizing.
733 if (isCommandPotentiallySynchronizing(command)) {
734 continue;
735 }
736
737 if (this->actionMask != nullptr) {
738 if (!this->actionMask->query(*this, command.getActionIndex())) {
739 continue;
740 }
741 }
742
743 // Skip the command, if it is not enabled.
744 if (!this->evaluator->asBool(command.getGuardExpression())) {
745 continue;
746 }
747
748 result.push_back(Choice<ValueType>(command.getActionIndex(), command.isMarkovian()));
749 Choice<ValueType>& choice = result.back();
750
751 // Remember the choice origin only if we were asked to.
752 if (this->options.isBuildChoiceOriginsSet()) {
753 CommandSet commandIndex{command.getGlobalIndex()};
754 choice.addOriginData(boost::any(std::move(commandIndex)));
755 }
756 choice.addProbability(stateToIdCallback(*this->state), storm::utility::one<ValueType>());
757
758 // Create the state-action reward for the newly created choice.
759 for (auto const& rewardModel : rewardModels) {
760 ValueType stateActionRewardValue = storm::utility::zero<ValueType>();
761 if (rewardModel.get().hasStateActionRewards()) {
762 for (auto const& stateActionReward : rewardModel.get().getStateActionRewards()) {
763 if (stateActionReward.getActionIndex() == choice.getActionIndex() &&
764 this->evaluator->asBool(stateActionReward.getStatePredicateExpression())) {
765 stateActionRewardValue += ValueType(this->evaluator->asRational(stateActionReward.getRewardValueExpression()));
766 }
767 }
768 }
769 choice.addReward(stateActionRewardValue);
770 }
771
772 if (this->options.isBuildChoiceLabelsSet() && command.isLabeled()) {
773 choice.addLabel(program.getActionName(command.getActionIndex()));
774 }
775 }
776 }
777
778 // Synchronizing actions
779 for (uint_fast64_t actionIndex : program.getSynchronizingActionIndices()) {
780 if (this->actionMask != nullptr) {
781 if (!this->actionMask->query(*this, actionIndex)) {
782 continue;
783 }
784 }
785 boost::optional<std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>>> optionalActiveCommandLists =
786 getActiveCommandsByActionIndex(actionIndex, commandFilter);
787
788 // Only process this action label, if there is at least one feasible solution.
789 if (optionalActiveCommandLists) {
790 std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>> const& activeCommandList = optionalActiveCommandLists.get();
791 std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>::const_iterator> iteratorList(activeCommandList.size());
792
793 // Initialize the list of iterators.
794 for (size_t i = 0; i < activeCommandList.size(); ++i) {
795 iteratorList[i] = activeCommandList[i].cbegin();
796 }
797
798 // As long as there is one feasible combination of commands, keep on expanding it.
799 bool done = false;
800 while (!done) {
801 // At this point, we applied all commands of the current command combination and newTargetStates
802 // contains all target states and their respective probabilities. That means we are now ready to
803 // add the choice to the list of transitions.
804 result.push_back(Choice<ValueType>(actionIndex));
805
806 // Now create the actual distribution.
807 Choice<ValueType>& choice = result.back();
808
809 // Remember the choice label and origins only if we were asked to.
810 if (this->options.isBuildChoiceLabelsSet()) {
811 choice.addLabel(program.getActionName(actionIndex));
812 }
813 if (this->options.isBuildChoiceOriginsSet()) {
814 CommandSet commandIndices;
815 for (uint_fast64_t i = 0; i < iteratorList.size(); ++i) {
816 commandIndices.insert(iteratorList[i]->get().getGlobalIndex());
817 }
818 choice.addOriginData(boost::any(std::move(commandIndices)));
819 }
820 choice.addProbability(stateToIdCallback(*this->state), storm::utility::one<ValueType>());
821
822 // Create the state-action reward for the newly created choice.
823 for (auto const& rewardModel : rewardModels) {
824 ValueType stateActionRewardValue = storm::utility::zero<ValueType>();
825 if (rewardModel.get().hasStateActionRewards()) {
826 for (auto const& stateActionReward : rewardModel.get().getStateActionRewards()) {
827 if (stateActionReward.getActionIndex() == choice.getActionIndex() &&
828 this->evaluator->asBool(stateActionReward.getStatePredicateExpression())) {
829 stateActionRewardValue += ValueType(this->evaluator->asRational(stateActionReward.getRewardValueExpression()));
830 }
831 }
832 }
833 choice.addReward(stateActionRewardValue);
834 }
835
836 // Now, check whether there is one more command combination to consider.
837 bool movedIterator = false;
838 for (int_fast64_t j = iteratorList.size() - 1; !movedIterator && j >= 0; --j) {
839 ++iteratorList[j];
840 if (iteratorList[j] != activeCommandList[j].end()) {
841 movedIterator = true;
842 } else {
843 // Reset the iterator to the beginning of the list.
844 iteratorList[j] = activeCommandList[j].begin();
845 }
846 }
847
848 done = !movedIterator;
849 }
850 }
851 }
852
853 return result;
854}
855
856template<typename ValueType, typename StateType>
857void PrismNextStateGenerator<ValueType, StateType>::generateSynchronizedDistribution(
858 storm::storage::BitVector const& state, ValueType const& probability, uint64_t position,
859 std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>::const_iterator> const& iteratorList,
860 storm::generator::Distribution<StateType, ValueType>& distribution, StateToIdCallback stateToIdCallback) {
861 if (storm::utility::isZero<ValueType>(probability)) {
862 return;
863 }
864
865 if (position >= iteratorList.size()) {
866 StateType id = stateToIdCallback(state);
867 distribution.add(id, std::move(probability));
868 } else {
869 storm::prism::Command const& command = *iteratorList[position];
870 for (uint_fast64_t j = 0; j < command.getNumberOfUpdates(); ++j) {
871 storm::prism::Update const& update = command.getUpdate(j);
872 ValueType updateProbability = evaluateLikelihoodExpression<ValueType>(update, *this->evaluator);
873 if constexpr (!std::is_same_v<ValueType, storm::RationalFunction>) {
874 if (this->options.isExplorationChecksSet()) {
875 STORM_LOG_THROW(updateProbability >= storm::utility::zero<ValueType>(), storm::exceptions::WrongFormatException,
876 "Probability expression in update '" << update << " evaluates to negative value " << updateProbability << ".");
877 STORM_LOG_THROW(!program.isDiscreteTimeModel() || updateProbability <= storm::utility::one<ValueType>(),
878 storm::exceptions::WrongFormatException,
879 "Probability expression in update '" << update << " evaluates to value " << updateProbability << " >1.");
880 }
881 }
882 updateProbability *= probability;
883 generateSynchronizedDistribution(applyUpdate(state, update), updateProbability, position + 1, iteratorList, distribution, stateToIdCallback);
884 }
885 }
886}
887
888template<typename ValueType, typename StateType>
889void PrismNextStateGenerator<ValueType, StateType>::addSynchronousChoices(std::vector<Choice<ValueType>>& choices, CompressedState const& state,
890 StateToIdCallback stateToIdCallback, CommandFilter const& commandFilter) {
891 for (uint_fast64_t actionIndex : program.getSynchronizingActionIndices()) {
892 if (this->actionMask != nullptr) {
893 if (!this->actionMask->query(*this, actionIndex)) {
894 continue;
895 }
896 }
897 boost::optional<std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>>> optionalActiveCommandLists =
898 getActiveCommandsByActionIndex(actionIndex, commandFilter);
899
900 // Only process this action label, if there is at least one feasible solution.
901 if (optionalActiveCommandLists) {
902 std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>> const& activeCommandList = optionalActiveCommandLists.get();
903 std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>::const_iterator> iteratorList(activeCommandList.size());
904
905 // Initialize the list of iterators.
906 for (size_t i = 0; i < activeCommandList.size(); ++i) {
907 iteratorList[i] = activeCommandList[i].cbegin();
908 }
909
910 storm::generator::Distribution<StateType, ValueType> distribution;
911
912 // As long as there is one feasible combination of commands, keep on expanding it.
913 bool done = false;
914 while (!done) {
915 distribution.clear();
916 generateSynchronizedDistribution(state, storm::utility::one<ValueType>(), 0, iteratorList, distribution, stateToIdCallback);
917 distribution.compress();
918
919 // At this point, we applied all commands of the current command combination and newTargetStates
920 // contains all target states and their respective probabilities. That means we are now ready to
921 // add the choice to the list of transitions.
922 choices.push_back(Choice<ValueType>(actionIndex));
923
924 // Now create the actual distribution.
925 Choice<ValueType>& choice = choices.back();
926
928 storm::storage::PlayerIndex const& playerOfAction = actionIndexToPlayerIndexMap.at(actionIndex);
929 STORM_LOG_THROW(playerOfAction != storm::storage::INVALID_PLAYER_INDEX, storm::exceptions::WrongFormatException,
930 "Action " << program.getActionName(actionIndex)
931 << " is not owned by any player but has at least one enabled, unlabeled (synchronized) command.");
932 choice.setPlayerIndex(playerOfAction);
933 }
934
935 // Remember the choice label and origins only if we were asked to.
936 if (this->options.isBuildChoiceLabelsSet()) {
937 choice.addLabel(program.getActionName(actionIndex));
938 }
939 if (this->options.isBuildChoiceOriginsSet()) {
940 CommandSet commandIndices;
941 for (uint_fast64_t i = 0; i < iteratorList.size(); ++i) {
942 commandIndices.insert(iteratorList[i]->get().getGlobalIndex());
943 }
944 choice.addOriginData(boost::any(std::move(commandIndices)));
945 }
946
947 // Add the probabilities/rates to the newly created choice.
949 choice.reserve(std::distance(distribution.begin(), distribution.end()));
950 for (auto const& stateProbability : distribution) {
951 choice.addProbability(stateProbability.getState(), stateProbability.getValue());
952 if (this->options.isExplorationChecksSet()) {
953 probabilitySum += stateProbability.getValue();
954 }
955 }
956
957 if (this->options.isExplorationChecksSet()) {
958 // Check that the resulting distribution is in fact a distribution.
959 STORM_LOG_THROW(!program.isDiscreteTimeModel() || !storm::utility::isConstant(probabilitySum) || this->comparator.isOne(probabilitySum),
960 storm::exceptions::WrongFormatException,
961 "Sum of update probabilities do not some to one for some command (actually sum to " << probabilitySum << ").");
962 }
963
964 // Create the state-action reward for the newly created choice.
965 for (auto const& rewardModel : rewardModels) {
966 ValueType stateActionRewardValue = storm::utility::zero<ValueType>();
967 if (rewardModel.get().hasStateActionRewards()) {
968 for (auto const& stateActionReward : rewardModel.get().getStateActionRewards()) {
969 if (stateActionReward.getActionIndex() == choice.getActionIndex() &&
970 this->evaluator->asBool(stateActionReward.getStatePredicateExpression())) {
971 stateActionRewardValue += ValueType(this->evaluator->asRational(stateActionReward.getRewardValueExpression()));
972 }
973 }
974 }
975 choice.addReward(stateActionRewardValue);
976 }
977
978 // Now, check whether there is one more command combination to consider.
979 bool movedIterator = false;
980 for (int_fast64_t j = iteratorList.size() - 1; !movedIterator && j >= 0; --j) {
981 ++iteratorList[j];
982 if (iteratorList[j] != activeCommandList[j].end()) {
983 movedIterator = true;
984 } else {
985 // Reset the iterator to the beginning of the list.
986 iteratorList[j] = activeCommandList[j].begin();
987 }
988 }
989
990 done = !movedIterator;
991 }
992 }
993 }
994}
995
996template<typename ValueType, typename StateType>
997std::map<std::string, storm::storage::PlayerIndex> PrismNextStateGenerator<ValueType, StateType>::getPlayerNameToIndexMap() const {
998 return program.getPlayerNameToIndexMapping();
999}
1000
1001template<typename ValueType, typename StateType>
1003 std::vector<StateType> const& initialStateIndices,
1004 std::vector<StateType> const& deadlockStateIndices,
1005 std::vector<StateType> const& unexploredStateIndices) {
1006 // Gather a vector of labels and their expressions.
1007 std::vector<std::pair<std::string, storm::expressions::Expression>> labels;
1008 if (this->options.isBuildAllLabelsSet()) {
1009 for (auto const& label : program.getLabels()) {
1010 labels.push_back(std::make_pair(label.getName(), label.getStatePredicateExpression()));
1011 }
1012 } else {
1013 for (auto const& labelName : this->options.getLabelNames()) {
1014 if (program.hasLabel(labelName)) {
1015 labels.push_back(std::make_pair(labelName, program.getLabelExpression(labelName)));
1016 } else {
1017 STORM_LOG_THROW(this->isSpecialLabel(labelName), storm::exceptions::InvalidArgumentException,
1018 "Cannot build labeling for unknown label '" << labelName << "'.");
1019 }
1020 }
1021 }
1022
1023 return NextStateGenerator<ValueType, StateType>::label(stateStorage, initialStateIndices, deadlockStateIndices, unexploredStateIndices, labels);
1024}
1025
1026template<typename ValueType, typename StateType>
1027storm::storage::BitVector PrismNextStateGenerator<ValueType, StateType>::evaluateObservationLabels(CompressedState const& state) const {
1028 // TODO consider to avoid reloading by computing these bitvectors in an earlier build stage
1030
1031 if (program.getNumberOfObservationLabels() == 0) {
1032 return result;
1033 }
1034 unpackStateIntoEvaluator(state, this->variableInformation, *this->evaluator);
1035 for (uint64_t i = 0; i < program.getNumberOfObservationLabels(); ++i) {
1036 result.setFromInt(64 * i, 64, this->evaluator->asInt(program.getObservationLabels()[i].getStatePredicateExpression()));
1037 }
1038 return result;
1039}
1040
1041template<typename ValueType, typename StateType>
1042void PrismNextStateGenerator<ValueType, StateType>::extendStateInformation(storm::json<BaseValueType>& result) const {
1043 for (uint64_t i = 0; i < program.getNumberOfObservationLabels(); ++i) {
1044 result[program.getObservationLabels()[i].getName()] = this->evaluator->asInt(program.getObservationLabels()[i].getStatePredicateExpression());
1045 }
1046}
1047
1048template<typename ValueType, typename StateType>
1050 return rewardModels.size();
1051}
1052
1053template<typename ValueType, typename StateType>
1055 storm::prism::RewardModel const& rewardModel = rewardModels[index].get();
1056 return storm::builder::RewardModelInformation(rewardModel.getName(), rewardModel.hasStateRewards(), rewardModel.hasStateActionRewards(),
1057 rewardModel.hasTransitionRewards());
1058}
1059
1060template<typename ValueType, typename StateType>
1061std::shared_ptr<storm::storage::sparse::ChoiceOrigins> PrismNextStateGenerator<ValueType, StateType>::generateChoiceOrigins(
1062 std::vector<boost::any>& dataForChoiceOrigins) const {
1063 if (!this->getOptions().isBuildChoiceOriginsSet()) {
1064 return nullptr;
1065 }
1066
1067 std::vector<uint_fast64_t> identifiers;
1068 identifiers.reserve(dataForChoiceOrigins.size());
1069
1070 std::map<CommandSet, uint_fast64_t> commandSetToIdentifierMap;
1071 // The empty commandset (i.e., the choices without origin) always has to get identifier getIdentifierForChoicesWithNoOrigin() -- which is assumed to be 0
1072 STORM_LOG_ASSERT(storm::storage::sparse::ChoiceOrigins::getIdentifierForChoicesWithNoOrigin() == 0, "The no origin identifier is assumed to be zero.");
1073 commandSetToIdentifierMap.insert(std::make_pair(CommandSet(), 0));
1074 uint_fast64_t currentIdentifier = 1;
1075 for (boost::any& originData : dataForChoiceOrigins) {
1076 STORM_LOG_ASSERT(originData.empty() || boost::any_cast<CommandSet>(&originData) != nullptr,
1077 "Origin data has unexpected type: " << originData.type().name() << ".");
1078
1079 CommandSet currentCommandSet = originData.empty() ? CommandSet() : boost::any_cast<CommandSet>(std::move(originData));
1080 auto insertionRes = commandSetToIdentifierMap.insert(std::make_pair(std::move(currentCommandSet), currentIdentifier));
1081 identifiers.push_back(insertionRes.first->second);
1082 if (insertionRes.second) {
1083 ++currentIdentifier;
1084 }
1085 }
1086
1087 std::vector<CommandSet> identifierToCommandSetMapping(currentIdentifier);
1088 for (auto const& setIdPair : commandSetToIdentifierMap) {
1089 identifierToCommandSetMapping[setIdPair.second] = setIdPair.first;
1090 }
1091
1092 return std::make_shared<storm::storage::sparse::PrismChoiceOrigins>(std::make_shared<storm::prism::Program>(program), std::move(identifiers),
1093 std::move(identifierToCommandSetMapping));
1094}
1095
1096template<typename ValueType, typename StateType>
1097bool PrismNextStateGenerator<ValueType, StateType>::isCommandPotentiallySynchronizing(const prism::Command& command) const {
1098 return program.getPossiblySynchronizingCommands().get(command.getGlobalIndex());
1099}
1100
1101template class PrismNextStateGenerator<double>;
1102template class PrismNextStateGenerator<storm::RationalNumber>;
1103template class PrismNextStateGenerator<storm::RationalFunction>;
1104template class PrismNextStateGenerator<storm::Interval>;
1105template class PrismNextStateGenerator<storm::RationalInterval>;
1106} // namespace generator
1107} // namespace storm
std::set< std::string > const & getRewardModelNames() const
Which reward models are built.
std::vector< std::pair< LabelOrExpression, bool > > const & getTerminalStates() const
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.
Action masks are arguments you can give to the state generator that limit which states are generated.
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.
NextStateGenerator(storm::expressions::ExpressionManager const &expressionManager, VariableInformation const &variableInformation, NextStateGeneratorOptions const &options, std::shared_ptr< ActionMask< ValueType, StateType > > const &=nullptr)
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.
std::string stateToString(CompressedState const &state) const
void postprocess(StateBehavior< ValueType, uint32_t > &result)
boost::optional< std::vector< uint64_t > > overlappingGuardStates
std::vector< std::pair< storm::expressions::Expression, bool > > terminalStates
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
storm::storage::FlatSet< uint_fast64_t > CommandSet
virtual std::vector< StateType > getInitialStates(StateToIdCallback const &stateToIdCallback) override
NextStateGenerator< ValueType, StateType >::StateToIdCallback StateToIdCallback
virtual StateBehavior< ValueType, StateType > expand(StateToIdCallback const &stateToIdCallback) override
virtual ModelType getModelType() const override
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
PrismNextStateGenerator(storm::prism::Program const &program, NextStateGeneratorOptions const &options=NextStateGeneratorOptions(), std::shared_ptr< ActionMask< ValueType, StateType > > const &=nullptr)
virtual std::size_t getNumberOfRewardModels() const override
virtual std::shared_ptr< storm::storage::sparse::ChoiceOrigins > generateChoiceOrigins(std::vector< boost::any > &dataForChoiceOrigins) const override
static bool canHandle(storm::prism::Program const &program)
A quick check to detect whether the given model is not supported.
bool evaluateBooleanExpressionInCurrentState(storm::expressions::Expression const &) const
virtual std::map< std::string, storm::storage::PlayerIndex > getPlayerNameToIndexMap() const override
virtual storm::builder::RewardModelInformation getRewardModelInformation(uint64_t const &index) const override
void addChoice(Choice< ValueType, StateType > &&choice)
Adds the given choice to the behavior of the state.
void addStateReward(ValueType const &stateReward)
Adds the given state reward to the behavior of the state.
void setExpanded(bool newValue=true)
Sets whether the state was expanded.
This class manages the labeling of the state space with a number of (atomic) labels.
bool isLabeled() const
Retrieves whether the command possesses a synchronization label.
Definition Command.cpp:82
std::size_t getNumberOfUpdates() const
Retrieves the number of updates associated with this command.
Definition Command.cpp:39
storm::prism::Update const & getUpdate(uint_fast64_t index) const
Retrieves a reference to the update with the given index.
Definition Command.cpp:43
storm::expressions::Expression const & getGuardExpression() const
Retrieves a reference to the guard of the command.
Definition Command.cpp:35
uint_fast64_t getActionIndex() const
Retrieves the action index of this command.
Definition Command.cpp:19
bool isMarkovian() const
Retrieves whether the command is a Markovian command, i.e.
Definition Command.cpp:23
uint_fast64_t getGlobalIndex() const
Retrieves the global index of the command, that is, a unique index over all modules.
Definition Command.cpp:56
bool hasActionIndex(uint_fast64_t actionIndex) const
Retrieves whether or not this module contains a command labeled with the given action index.
Definition Module.cpp:149
std::string const & getName() const
Retrieves the name of the module.
Definition Module.cpp:141
std::size_t getNumberOfCommands() const
Retrieves the number of commands of this module.
Definition Module.cpp:117
std::set< uint_fast64_t > const & getSynchronizingActionIndices() const
Retrieves the set of synchronizing action indices present in the program.
Definition Program.cpp:747
bool hasIntervalUpdates() const
Retrieves whether the program considers at least one update with an interval probability/rate.
Definition Program.cpp:706
ModelType getModelType() const
Retrieves the model type of the model.
Definition Program.cpp:243
std::vector< RewardModel > const & getRewardModels() const
Retrieves the reward models of the program.
Definition Program.cpp:817
RewardModel const & getRewardModel(std::string const &rewardModelName) const
Retrieves the reward model with the given name.
Definition Program.cpp:825
std::vector< std::reference_wrapper< Constant const > > getUndefinedConstants() const
Retrieves the undefined constants in the program.
Definition Program.cpp:364
std::string const & getActionName(uint_fast64_t actionIndex) const
Retrieves the action name of the given action index.
Definition Program.cpp:751
bool isDiscreteTimeModel() const
Retrieves whether the model is a discrete-time model, i.e.
Definition Program.cpp:247
storm::expressions::Expression const & getLabelExpression(std::string const &label) const
Retrieves the expression associated with the given label, if it exists.
Definition Program.cpp:865
std::vector< ObservationLabel > const & getObservationLabels() const
Retrieves all observation labels that are defined by this program.
Definition Program.cpp:918
Module const & getModule(uint_fast64_t index) const
Retrieves the module with the given index.
Definition Program.cpp:611
std::map< uint_fast64_t, storm::storage::PlayerIndex > buildActionIndexToPlayerIndexMap() const
Retrieves a vector whose i'th entry corresponds to the player controlling action with index i.
Definition Program.cpp:586
bool undefinedConstantsAreGraphPreserving() const
Checks that undefined constants (parameters) of the model preserve the graph of the underlying model.
Definition Program.cpp:290
std::size_t getNumberOfRewardModels() const
Retrieves the number of reward models in the program.
Definition Program.cpp:821
std::size_t getNumberOfObservationLabels() const
Retrieves the number of observation labels in the program.
Definition Program.cpp:922
bool hasLabel(std::string const &labelName) const
Checks whether the program has a label with the given name.
Definition Program.cpp:837
std::vector< storm::storage::PlayerIndex > buildModuleIndexToPlayerIndexMap() const
Retrieves a vector whose i'th entry corresponds to the player controlling module i.
Definition Program.cpp:574
std::string getUndefinedConstantsAsString() const
Retrieves the undefined constants in the program as a comma-separated string.
Definition Program.cpp:374
std::size_t getNumberOfModules() const
Retrieves the number of modules in the program.
Definition Program.cpp:607
bool hasUndefinedConstants() const
Retrieves whether there are undefined constants of any type in the program.
Definition Program.cpp:281
bool specifiesSystemComposition() const
Retrieves whether the program specifies a system composition in terms of process algebra operations o...
Definition Program.cpp:719
storm::storage::BitVector const & getPossiblySynchronizingCommands() const
Compute the (labelled) commands in the program that may be synchronizing.
Definition Program.cpp:926
bool hasRewardModel() const
Retrieves whether the program has reward models.
Definition Program.cpp:808
bool hasStateRewards() const
Retrieves whether there are any state rewards.
bool hasTransitionRewards() const
Retrieves whether there are any transition rewards.
std::string const & getName() const
Retrieves the name of the reward model.
bool hasStateActionRewards() const
Retrieves whether there are any state-action rewards.
bool isLikelihoodInterval() const
Definition Update.cpp:57
ExpressionPair const & getLikelihoodExpressionInterval() const
Retrieves the two expression for the interval likelihood of this update.
Definition Update.cpp:66
std::vector< storm::prism::Assignment > const & getAssignments() const
Retrieves a reference to the map of variable names to their respective assignments.
Definition Update.cpp:75
storm::expressions::Expression const & getLikelihoodExpression() const
Retrieves the expression for the likelihood of this update.
Definition Update.cpp:61
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.
bool get(uint64_t index) const
Retrieves the truth value of the bit at the given index and performs a bound check.
static uint_fast64_t getIdentifierForChoicesWithNoOrigin()
virtual std::unique_ptr< storm::solver::SmtSolver > create(storm::expressions::ExpressionManager &manager) const
Creates a new SMT solver instance.
Definition solver.cpp:159
#define STORM_LOG_DEBUG(message)
Definition logging.h:21
#define STORM_LOG_TRACE(message)
Definition logging.h:15
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:28
SFTBDDChecker::ValueType ValueType
void unpackStateIntoEvaluator(CompressedState const &state, VariableInformation const &variableInformation, storm::expressions::ExpressionEvaluator< ValueType > &evaluator)
Unpacks the compressed state into the evaluator.
storm::storage::BitVector CompressedState
ValueType evaluateLikelihoodExpression(storm::prism::Update const &update, storm::expressions::ExpressionEvaluator< BaseValueType > const &evaluator)
storm::builder::BuilderOptions NextStateGeneratorOptions
PlayerIndex const INVALID_PLAYER_INDEX
Definition PlayerIndex.h:8
uint64_t PlayerIndex
Definition PlayerIndex.h:7
void forEach(std::vector< IteratorType > const &its, std::vector< IteratorType > const &ites, std::function< void(uint64_t, decltype(*std::declval< IteratorType >()))> const &setValueCallback, std::function< bool()> const &newCombinationCallback)
std::vector< T > buildVectorForRange(T min, T max)
Constructs a vector [min, min+1, ...., max-1].
Definition vector.h:129
bool isConstant(ValueType const &)
bool isZero(ValueType const &a)
Definition constants.cpp:42
ValueType zero()
Definition constants.cpp:24
ValueType one()
Definition constants.cpp:19
TargetType convertNumber(SourceType const &number)
nlohmann::basic_json< std::map, std::vector, std::string, bool, int64_t, uint64_t, ValueType > json
Definition JsonForward.h:11
constexpr bool IsIntervalType
Helper to check if a type is an interval.
ActiveCommandData(storm::prism::Module const *modulePtr, std::set< uint_fast64_t > const *commandIndicesPtr, typename std::set< uint_fast64_t >::const_iterator currentCommandIndexIt)
std::set< uint_fast64_t >::const_iterator currentCommandIndexIt
std::set< uint_fast64_t > const * commandIndicesPtr
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 addLabels(std::set< std::string > const &labels)
Adds the given label set to the labels associated with this choice.
Definition Choice.cpp:82
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 addReward(ValueType const &value)
Adds the given value to the reward associated with this choice.
Definition Choice.cpp:164