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 result.clear();
275
276 // First, construct the state rewards, as we may return early if there are no choices later and we already
277 // need the state rewards then.
278 for (auto const& rewardModel : rewardModels) {
279 ValueType stateRewardValue = storm::utility::zero<ValueType>();
280 if (rewardModel.get().hasStateRewards()) {
281 for (auto const& stateReward : rewardModel.get().getStateRewards()) {
282 if (this->evaluator->asBool(stateReward.getStatePredicateExpression())) {
283 stateRewardValue += ValueType(this->evaluator->asRational(stateReward.getRewardValueExpression()));
284 }
285 }
286 }
287 result.addStateReward(stateRewardValue);
288 }
289
290 // If a terminal expression was set, we must not expand this state
291 if (!this->terminalStates.empty()) {
292 for (auto const& expressionBool : this->terminalStates) {
293 if (this->evaluator->asBool(expressionBool.first) == expressionBool.second) {
294 if (!isPartiallyObservable()) {
295 // If the model is not partially observable, return.
296 return result;
297 } else {
298 // for partially observable models, we need to add self-loops for all enabled actions.
299 result.setExpanded();
300 std::vector<Choice<ValueType>> allChoices = getSelfLoopsForAllActions(*this->state, stateToIdCallback);
301 if (allChoices.size() != 0) {
302 for (auto& choice : allChoices) {
303 result.addChoice(std::move(choice));
304 }
305
306 this->postprocess(result);
307 }
308 return result;
309 }
310 }
311 }
312 }
313
314 // Get all choices for the state.
315 result.setExpanded();
316
317 std::vector<Choice<ValueType>> allChoices;
318 if (this->getOptions().isApplyMaximalProgressAssumptionSet()) {
319 // First explore only edges without a rate
320 allChoices = getAsynchronousChoices(*this->state, stateToIdCallback, CommandFilter::Probabilistic);
321 addSynchronousChoices(allChoices, *this->state, stateToIdCallback, CommandFilter::Probabilistic);
322 if (allChoices.empty()) {
323 // Expand the Markovian edges if there are no probabilistic ones.
324 allChoices = getAsynchronousChoices(*this->state, stateToIdCallback, CommandFilter::Markovian);
325 addSynchronousChoices(allChoices, *this->state, stateToIdCallback, CommandFilter::Markovian);
326 }
327 } else {
328 allChoices = getAsynchronousChoices(*this->state, stateToIdCallback);
329 addSynchronousChoices(allChoices, *this->state, stateToIdCallback);
330 }
331
332 // If there is not a single choice, we return immediately, because the state has no behavior (other than
333 // the state reward).
334 if (allChoices.empty()) {
335 return result;
336 }
337
338 // If the model is a deterministic model, we need to fuse the choices into one.
339 if (this->isDeterministicModel() && allChoices.size() > 1) {
340 Choice<ValueType> globalChoice;
341
342 if (this->options.isAddOverlappingGuardLabelSet()) {
343 this->overlappingGuardStates->push_back(stateToIdCallback(*this->state));
344 }
345
346 // For CTMCs, we need to keep track of the total exit rate to scale the action rewards later. For DTMCs
347 // this is equal to the number of choices, which is why we initialize it like this here.
348 ValueType const totalNumberOfChoices = storm::utility::convertNumber<ValueType, uint64_t>(allChoices.size());
349 ValueType totalExitRate = this->isDiscreteTimeModel() ? totalNumberOfChoices : storm::utility::zero<ValueType>();
350
351 // Iterate over all choices and combine the probabilities/rates into one choice.
352 for (auto const& choice : allChoices) {
353 for (auto const& stateProbabilityPair : choice) {
354 if (this->isDiscreteTimeModel()) {
355 globalChoice.addProbability(stateProbabilityPair.first, stateProbabilityPair.second / totalNumberOfChoices);
356 } else {
357 globalChoice.addProbability(stateProbabilityPair.first, stateProbabilityPair.second);
358 }
359 }
360
361 if (hasStateActionRewards && !this->isDiscreteTimeModel()) {
362 totalExitRate += choice.getTotalMass();
363 }
364
365 if (this->options.isBuildChoiceLabelsSet() && choice.hasLabels()) {
366 globalChoice.addLabels(choice.getLabels());
367 }
368
369 if (this->options.isBuildChoiceOriginsSet() && choice.hasOriginData()) {
370 globalChoice.addOriginData(choice.getOriginData());
371 }
372 }
373
374 // Now construct the state-action reward for all selected reward models.
375 for (auto const& rewardModel : rewardModels) {
376 ValueType stateActionRewardValue = storm::utility::zero<ValueType>();
377 if (rewardModel.get().hasStateActionRewards()) {
378 for (auto const& stateActionReward : rewardModel.get().getStateActionRewards()) {
379 for (auto const& choice : allChoices) {
380 if (stateActionReward.getActionIndex() == choice.getActionIndex() &&
381 this->evaluator->asBool(stateActionReward.getStatePredicateExpression())) {
382 stateActionRewardValue +=
383 ValueType(this->evaluator->asRational(stateActionReward.getRewardValueExpression())) * choice.getTotalMass();
384 }
385 }
386 }
387 }
388 if (hasStateActionRewards) {
389 globalChoice.addReward(stateActionRewardValue / totalExitRate);
390 }
391 }
392
393 // Move the newly fused choice in place.
394 allChoices.clear();
395 allChoices.push_back(std::move(globalChoice));
396 }
397
398 // For SMG we check whether the state has a unique player
399 if (program.getModelType() == storm::prism::Program::ModelType::SMG && allChoices.size() > 1) {
400 auto choiceIt = allChoices.begin();
401 STORM_LOG_ASSERT(choiceIt->hasPlayerIndex(),
402 "State '" << this->stateToString(*this->state)
403 << "' features a choice without player index."); // This should have been catched while creating the choice already
404 storm::storage::PlayerIndex statePlayerIndex = choiceIt->getPlayerIndex();
406 "State '" << this->stateToString(*this->state)
407 << "' features a choice with invalid player index."); // This should have been catched while creating the choice already
408 for (++choiceIt; choiceIt != allChoices.end(); ++choiceIt) {
409 STORM_LOG_ASSERT(choiceIt->hasPlayerIndex(),
410 "State '" << this->stateToString(*this->state)
411 << "' features a choice without player index."); // This should have been catched while creating the choice already
412 STORM_LOG_ASSERT(choiceIt->getPlayerIndex() != storm::storage::INVALID_PLAYER_INDEX,
413 "State '" << this->stateToString(*this->state)
414 << "' features a choice with invalid player index."); // This should have been catched while creating the choice already
415 STORM_LOG_THROW(statePlayerIndex == choiceIt->getPlayerIndex(), storm::exceptions::WrongFormatException,
416 "The player for state '" << this->stateToString(*this->state) << "' is not unique. At least one choice is owned by player '"
417 << statePlayerIndex << "' while another is owned by player '" << choiceIt->getPlayerIndex() << "'.");
418 }
419 }
420
421 // Move all remaining choices in place.
422 for (auto& choice : allChoices) {
423 result.addChoice(std::move(choice));
424 }
425
426 this->postprocess(result);
427
428 return result;
429}
430
431template<typename ValueType, typename StateType>
435
436template<typename ValueType, typename StateType>
437CompressedState PrismNextStateGenerator<ValueType, StateType>::applyUpdate(CompressedState const& state, storm::prism::Update const& update) {
438 CompressedState newState(state);
439
440 // NOTE: the following process assumes that the assignments of the update are ordered in such a way that the
441 // assignments to boolean variables precede the assignments to all integer variables and that within the
442 // types, the assignments to variables are ordered (in ascending order) by the expression variables.
443 // This is guaranteed for PRISM models, by sorting the assignments as soon as an update is created.
444
445 auto assignmentIt = update.getAssignments().begin();
446 auto assignmentIte = update.getAssignments().end();
447
448 // Iterate over all boolean assignments and carry them out.
449 auto boolIt = this->variableInformation.booleanVariables.begin();
450 for (; assignmentIt != assignmentIte && assignmentIt->getExpression().hasBooleanType(); ++assignmentIt) {
451 while (assignmentIt->getVariable() != boolIt->variable) {
452 ++boolIt;
453 }
454 newState.set(boolIt->bitOffset, this->evaluator->asBool(assignmentIt->getExpression()));
455 }
456
457 // Iterate over all integer assignments and carry them out.
458 auto integerIt = this->variableInformation.integerVariables.begin();
459 for (; assignmentIt != assignmentIte && assignmentIt->getExpression().hasIntegerType(); ++assignmentIt) {
460 while (assignmentIt->getVariable() != integerIt->variable) {
461 ++integerIt;
462 }
463 int_fast64_t assignedValue = this->evaluator->asInt(assignmentIt->getExpression());
464 if (this->options.isAddOutOfBoundsStateSet()) {
465 if (assignedValue < integerIt->lowerBound || assignedValue > integerIt->upperBound) {
466 return this->outOfBoundsState;
467 }
468 } else if (integerIt->forceOutOfBoundsCheck || this->options.isExplorationChecksSet()) {
469 STORM_LOG_THROW(assignedValue >= integerIt->lowerBound, storm::exceptions::WrongFormatException,
470 "The update " << update << " leads to an out-of-bounds value (" << assignedValue << ") for the variable '"
471 << assignmentIt->getVariableName() << "'.");
472 STORM_LOG_THROW(assignedValue <= integerIt->upperBound, storm::exceptions::WrongFormatException,
473 "The update " << update << " leads to an out-of-bounds value (" << assignedValue << ") for the variable '"
474 << assignmentIt->getVariableName() << "'.");
475 }
476 newState.setFromInt(integerIt->bitOffset, integerIt->bitWidth, assignedValue - integerIt->lowerBound);
477 STORM_LOG_ASSERT(static_cast<int_fast64_t>(newState.getAsInt(integerIt->bitOffset, integerIt->bitWidth)) + integerIt->lowerBound == assignedValue,
478 "Writing to the bit vector bucket failed (read " << newState.getAsInt(integerIt->bitOffset, integerIt->bitWidth) << " but wrote "
479 << assignedValue << ").");
480 }
481
482 // Check that we processed all assignments.
483 STORM_LOG_ASSERT(assignmentIt == assignmentIte, "Not all assignments were consumed.");
484
485 return newState;
486}
487
490 typename std::set<uint_fast64_t>::const_iterator currentCommandIndexIt)
492 // Intentionally left empty
493 }
495 std::set<uint_fast64_t> const* commandIndicesPtr;
496 typename std::set<uint_fast64_t>::const_iterator currentCommandIndexIt;
497};
498
499template<typename ValueType, typename StateType>
500boost::optional<std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>>>
501PrismNextStateGenerator<ValueType, StateType>::getActiveCommandsByActionIndex(uint_fast64_t const& actionIndex, CommandFilter const& commandFilter) {
502 // First check whether there is at least one enabled command at each module
503 // This avoids evaluating unnecessarily many guards.
504 // If we find one module without an enabled command, we return boost::none.
505 // At the same time, we store pointers to the relevant modules, the relevant command sets and the first enabled command within each set.
506
507 // Iterate over all modules.
508 std::vector<ActiveCommandData> activeCommands;
509 for (uint_fast64_t i = 0; i < program.getNumberOfModules(); ++i) {
510 storm::prism::Module const& module = program.getModule(i);
511
512 // If the module has no command labeled with the given action, we can skip this module.
513 if (!module.hasActionIndex(actionIndex)) {
514 continue;
515 }
516
517 std::set<uint_fast64_t> const& commandIndices = module.getCommandIndicesByActionIndex(actionIndex);
518
519 // If the module contains the action, but there is no command in the module that is labeled with
520 // this action, we don't have any feasible command combinations.
521 if (commandIndices.empty()) {
522 return boost::none;
523 }
524
525 // Look up commands by their indices and check if the guard evaluates to true in the given state.
526 bool hasOneEnabledCommand = false;
527 for (auto commandIndexIt = commandIndices.begin(), commandIndexIte = commandIndices.end(); commandIndexIt != commandIndexIte; ++commandIndexIt) {
528 storm::prism::Command const& command = module.getCommand(*commandIndexIt);
529 if (!isCommandPotentiallySynchronizing(command)) {
530 continue;
531 }
532 if (commandFilter != CommandFilter::All) {
533 STORM_LOG_ASSERT(commandFilter == CommandFilter::Markovian || commandFilter == CommandFilter::Probabilistic, "Unexpected command filter.");
534 if ((commandFilter == CommandFilter::Markovian) != command.isMarkovian()) {
535 continue;
536 }
537 }
538 if (this->evaluator->asBool(command.getGuardExpression())) {
539 // Found the first enabled command for this module.
540 hasOneEnabledCommand = true;
541 activeCommands.emplace_back(&module, &commandIndices, commandIndexIt);
542 break;
543 }
544 }
545
546 if (!hasOneEnabledCommand) {
547 return boost::none;
548 }
549 }
550
551 // If we reach this point, there has to be at least one active command for each relevant module.
552 std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>> result;
553
554 // Iterate over all command sets.
555 for (auto const& activeCommand : activeCommands) {
556 std::vector<std::reference_wrapper<storm::prism::Command const>> commands;
557
558 auto commandIndexIt = activeCommand.currentCommandIndexIt;
559 // The command at the current position is already known to be enabled
560 commands.push_back(activeCommand.modulePtr->getCommand(*commandIndexIt));
561
562 // Look up commands by their indices and add them if the guard evaluates to true in the given state.
563 auto commandIndexIte = activeCommand.commandIndicesPtr->end();
564 for (++commandIndexIt; commandIndexIt != commandIndexIte; ++commandIndexIt) {
565 storm::prism::Command const& command = activeCommand.modulePtr->getCommand(*commandIndexIt);
566 if (commandFilter != CommandFilter::All) {
567 STORM_LOG_ASSERT(commandFilter == CommandFilter::Markovian || commandFilter == CommandFilter::Probabilistic, "Unexpected command filter.");
568 if ((commandFilter == CommandFilter::Markovian) != command.isMarkovian()) {
569 continue;
570 }
571 }
572 if (this->evaluator->asBool(command.getGuardExpression())) {
573 commands.push_back(command);
574 }
575 }
576
577 result.push_back(std::move(commands));
578 }
579
580 STORM_LOG_ASSERT(!result.empty(), "Expected non-empty list.");
581 return result;
582}
583
584template<typename ValueType, typename BaseValueType>
586 if constexpr (storm::IsIntervalType<ValueType>) {
587 if (update.isLikelihoodInterval()) {
588 BaseValueType lower = evaluator.asRational(update.getLikelihoodExpressionInterval().first);
589 BaseValueType upper = evaluator.asRational(update.getLikelihoodExpressionInterval().second);
590 return ValueType(lower, storm::BoundType::WEAK, upper, storm::BoundType::WEAK);
591 } else {
592 return evaluator.asRational(update.getLikelihoodExpression());
593 }
594 } else {
595 // We don't expect to see an interval likelihood now. This case should have been catched earlier.
596 STORM_LOG_ASSERT(!update.isLikelihoodInterval(), "Unexpected interval likelihood for non-interval model in update " << update << ".");
597 return evaluator.asRational(update.getLikelihoodExpression());
598 }
599}
600
601template<typename ValueType, typename StateType>
602std::vector<Choice<ValueType>> PrismNextStateGenerator<ValueType, StateType>::getAsynchronousChoices(CompressedState const& state,
603 StateToIdCallback const& stateToIdCallback,
604 CommandFilter const& commandFilter) {
605 std::vector<Choice<ValueType>> result;
606
607 // Iterate over all modules.
608 for (uint_fast64_t i = 0; i < program.getNumberOfModules(); ++i) {
609 storm::prism::Module const& module = program.getModule(i);
610
611 // Iterate over all commands.
612 for (uint_fast64_t j = 0; j < module.getNumberOfCommands(); ++j) {
613 storm::prism::Command const& command = module.getCommand(j);
614
615 // Only consider commands that are not possibly synchronizing.
616 if (isCommandPotentiallySynchronizing(command)) {
617 continue;
618 }
619
620 if (commandFilter != CommandFilter::All) {
621 STORM_LOG_ASSERT(commandFilter == CommandFilter::Markovian || commandFilter == CommandFilter::Probabilistic, "Unexpected command filter.");
622 if ((commandFilter == CommandFilter::Markovian) != command.isMarkovian()) {
623 continue;
624 }
625 }
626 if (this->actionMask != nullptr) {
627 if (!this->actionMask->query(*this, command.getActionIndex())) {
628 continue;
629 }
630 }
631
632 // Skip the command, if it is not enabled.
633 if (!this->evaluator->asBool(command.getGuardExpression())) {
634 continue;
635 }
636
637 result.push_back(Choice<ValueType>(command.getActionIndex(), command.isMarkovian()));
638 Choice<ValueType>& choice = result.back();
639
640 // Remember the choice origin only if we were asked to.
641 if (this->options.isBuildChoiceOriginsSet()) {
642 CommandSet commandIndex{command.getGlobalIndex()};
643 choice.addOriginData(boost::any(std::move(commandIndex)));
644 }
645
646 // Iterate over all updates of the current command.
648 for (uint_fast64_t k = 0; k < command.getNumberOfUpdates(); ++k) {
649 storm::prism::Update const& update = command.getUpdate(k);
650
651 ValueType probability = evaluateLikelihoodExpression<ValueType>(update, *this->evaluator);
652 if (probability != storm::utility::zero<ValueType>()) {
653 // Obtain target state index and add it to the list of known states. If it has not yet been
654 // seen, we also add it to the set of states that have yet to be explored.
655 StateType stateIndex = stateToIdCallback(applyUpdate(state, update));
656
657 // Update the choice by adding the probability/target state to it.
658 choice.addProbability(stateIndex, probability);
659 if (this->options.isExplorationChecksSet()) {
660 if constexpr (!std::is_same_v<ValueType, storm::RationalFunction>) {
661 STORM_LOG_THROW(probability > storm::utility::zero<ValueType>(), storm::exceptions::WrongFormatException,
662 "Probability expression in update '" << update << " evaluates to negative value " << probability << ".");
664 storm::exceptions::WrongFormatException,
665 "Probability expression in update '" << update << " evaluates to value " << probability << " >1.");
666 }
667 probabilitySum += probability;
668 }
669 }
670 }
671
672 // Create the state-action reward for the newly created choice.
673 for (auto const& rewardModel : rewardModels) {
674 ValueType stateActionRewardValue = storm::utility::zero<ValueType>();
675 if (rewardModel.get().hasStateActionRewards()) {
676 for (auto const& stateActionReward : rewardModel.get().getStateActionRewards()) {
677 if (stateActionReward.getActionIndex() == choice.getActionIndex() &&
678 this->evaluator->asBool(stateActionReward.getStatePredicateExpression())) {
679 stateActionRewardValue += ValueType(this->evaluator->asRational(stateActionReward.getRewardValueExpression()));
680 }
681 }
682 }
683 choice.addReward(stateActionRewardValue);
684 }
685
686 if (this->options.isBuildChoiceLabelsSet() && command.isLabeled()) {
687 choice.addLabel(program.getActionName(command.getActionIndex()));
688 }
689
691 if (command.getActionIndex() == 0) {
692 // Unlabeled command
693 auto const playerOfModule = moduleIndexToPlayerIndexMap.at(i);
694 STORM_LOG_THROW(playerOfModule != storm::storage::INVALID_PLAYER_INDEX, storm::exceptions::WrongFormatException,
695 "Module " << module.getName() << " is not owned by any player but has at least one enabled, unlabeled command.");
696 choice.setPlayerIndex(playerOfModule);
697 } else {
698 // Labelled command (that happens to not synchronize with another command
699 auto const playerOfAction = actionIndexToPlayerIndexMap.at(command.getActionIndex());
700 STORM_LOG_THROW(playerOfAction != storm::storage::INVALID_PLAYER_INDEX, storm::exceptions::WrongFormatException,
701 "Command with action label '" << program.getActionName(command.getActionIndex()) << "' is not owned by any player.");
702 choice.setPlayerIndex(playerOfAction);
703 }
704 }
705
706 if (this->options.isExplorationChecksSet()) {
707 // Check that the resulting distribution is in fact a distribution.
708 STORM_LOG_THROW(!program.isDiscreteTimeModel() || !storm::utility::isConstant(probabilitySum) || this->comparator.isOne(probabilitySum),
709 storm::exceptions::WrongFormatException,
710 "Probabilities do not sum to one for command '" << command << "' (actually sum to " << probabilitySum << ").");
711 }
712 }
713 }
714
715 return result;
716}
717
718template<typename ValueType, typename StateType>
719std::vector<Choice<ValueType>> PrismNextStateGenerator<ValueType, StateType>::getSelfLoopsForAllActions(CompressedState const& state,
720 StateToIdCallback const& stateToIdCallback,
721 CommandFilter const& commandFilter) {
722 std::vector<Choice<ValueType>> result;
723
724 // Asynchronous actions
725 // Iterate over all modules.
726 for (uint_fast64_t i = 0; i < program.getNumberOfModules(); ++i) {
727 storm::prism::Module const& module = program.getModule(i);
728
729 // Iterate over all commands.
730 for (uint_fast64_t j = 0; j < module.getNumberOfCommands(); ++j) {
731 storm::prism::Command const& command = module.getCommand(j);
732
733 // Only consider commands that are not possibly synchronizing.
734 if (isCommandPotentiallySynchronizing(command)) {
735 continue;
736 }
737
738 if (this->actionMask != nullptr) {
739 if (!this->actionMask->query(*this, command.getActionIndex())) {
740 continue;
741 }
742 }
743
744 // Skip the command, if it is not enabled.
745 if (!this->evaluator->asBool(command.getGuardExpression())) {
746 continue;
747 }
748
749 result.push_back(Choice<ValueType>(command.getActionIndex(), command.isMarkovian()));
750 Choice<ValueType>& choice = result.back();
751
752 // Remember the choice origin only if we were asked to.
753 if (this->options.isBuildChoiceOriginsSet()) {
754 CommandSet commandIndex{command.getGlobalIndex()};
755 choice.addOriginData(boost::any(std::move(commandIndex)));
756 }
757 choice.addProbability(stateToIdCallback(*this->state), storm::utility::one<ValueType>());
758
759 // Create the state-action reward for the newly created choice.
760 for (auto const& rewardModel : rewardModels) {
761 ValueType stateActionRewardValue = storm::utility::zero<ValueType>();
762 if (rewardModel.get().hasStateActionRewards()) {
763 for (auto const& stateActionReward : rewardModel.get().getStateActionRewards()) {
764 if (stateActionReward.getActionIndex() == choice.getActionIndex() &&
765 this->evaluator->asBool(stateActionReward.getStatePredicateExpression())) {
766 stateActionRewardValue += ValueType(this->evaluator->asRational(stateActionReward.getRewardValueExpression()));
767 }
768 }
769 }
770 choice.addReward(stateActionRewardValue);
771 }
772
773 if (this->options.isBuildChoiceLabelsSet() && command.isLabeled()) {
774 choice.addLabel(program.getActionName(command.getActionIndex()));
775 }
776 }
777 }
778
779 // Synchronizing actions
780 for (uint_fast64_t actionIndex : program.getSynchronizingActionIndices()) {
781 if (this->actionMask != nullptr) {
782 if (!this->actionMask->query(*this, actionIndex)) {
783 continue;
784 }
785 }
786 boost::optional<std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>>> optionalActiveCommandLists =
787 getActiveCommandsByActionIndex(actionIndex, commandFilter);
788
789 // Only process this action label, if there is at least one feasible solution.
790 if (optionalActiveCommandLists) {
791 std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>> const& activeCommandList = optionalActiveCommandLists.get();
792 std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>::const_iterator> iteratorList(activeCommandList.size());
793
794 // Initialize the list of iterators.
795 for (size_t i = 0; i < activeCommandList.size(); ++i) {
796 iteratorList[i] = activeCommandList[i].cbegin();
797 }
798
799 // As long as there is one feasible combination of commands, keep on expanding it.
800 bool done = false;
801 while (!done) {
802 // At this point, we applied all commands of the current command combination and newTargetStates
803 // contains all target states and their respective probabilities. That means we are now ready to
804 // add the choice to the list of transitions.
805 result.push_back(Choice<ValueType>(actionIndex));
806
807 // Now create the actual distribution.
808 Choice<ValueType>& choice = result.back();
809
810 // Remember the choice label and origins only if we were asked to.
811 if (this->options.isBuildChoiceLabelsSet()) {
812 choice.addLabel(program.getActionName(actionIndex));
813 }
814 if (this->options.isBuildChoiceOriginsSet()) {
815 CommandSet commandIndices;
816 for (uint_fast64_t i = 0; i < iteratorList.size(); ++i) {
817 commandIndices.insert(iteratorList[i]->get().getGlobalIndex());
818 }
819 choice.addOriginData(boost::any(std::move(commandIndices)));
820 }
821 choice.addProbability(stateToIdCallback(*this->state), storm::utility::one<ValueType>());
822
823 // Create the state-action reward for the newly created choice.
824 for (auto const& rewardModel : rewardModels) {
825 ValueType stateActionRewardValue = storm::utility::zero<ValueType>();
826 if (rewardModel.get().hasStateActionRewards()) {
827 for (auto const& stateActionReward : rewardModel.get().getStateActionRewards()) {
828 if (stateActionReward.getActionIndex() == choice.getActionIndex() &&
829 this->evaluator->asBool(stateActionReward.getStatePredicateExpression())) {
830 stateActionRewardValue += ValueType(this->evaluator->asRational(stateActionReward.getRewardValueExpression()));
831 }
832 }
833 }
834 choice.addReward(stateActionRewardValue);
835 }
836
837 // Now, check whether there is one more command combination to consider.
838 bool movedIterator = false;
839 for (int_fast64_t j = iteratorList.size() - 1; !movedIterator && j >= 0; --j) {
840 ++iteratorList[j];
841 if (iteratorList[j] != activeCommandList[j].end()) {
842 movedIterator = true;
843 } else {
844 // Reset the iterator to the beginning of the list.
845 iteratorList[j] = activeCommandList[j].begin();
846 }
847 }
848
849 done = !movedIterator;
850 }
851 }
852 }
853
854 return result;
855}
856
857template<typename ValueType, typename StateType>
858void PrismNextStateGenerator<ValueType, StateType>::generateSynchronizedDistribution(
859 storm::storage::BitVector const& state, ValueType const& probability, uint64_t position,
860 std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>::const_iterator> const& iteratorList,
861 storm::generator::Distribution<StateType, ValueType>& distribution, StateToIdCallback const& stateToIdCallback) {
862 if (storm::utility::isZero<ValueType>(probability)) {
863 return;
864 }
865
866 if (position >= iteratorList.size()) {
867 StateType id = stateToIdCallback(state);
868 distribution.add(id, std::move(probability));
869 } else {
870 storm::prism::Command const& command = *iteratorList[position];
871 for (uint_fast64_t j = 0; j < command.getNumberOfUpdates(); ++j) {
872 storm::prism::Update const& update = command.getUpdate(j);
873 ValueType updateProbability = evaluateLikelihoodExpression<ValueType>(update, *this->evaluator);
874 if constexpr (!std::is_same_v<ValueType, storm::RationalFunction>) {
875 if (this->options.isExplorationChecksSet()) {
876 STORM_LOG_THROW(updateProbability >= storm::utility::zero<ValueType>(), storm::exceptions::WrongFormatException,
877 "Probability expression in update '" << update << " evaluates to negative value " << updateProbability << ".");
878 STORM_LOG_THROW(!program.isDiscreteTimeModel() || updateProbability <= storm::utility::one<ValueType>(),
879 storm::exceptions::WrongFormatException,
880 "Probability expression in update '" << update << " evaluates to value " << updateProbability << " >1.");
881 }
882 }
883 updateProbability *= probability;
884 generateSynchronizedDistribution(applyUpdate(state, update), updateProbability, position + 1, iteratorList, distribution, stateToIdCallback);
885 }
886 }
887}
888
889template<typename ValueType, typename StateType>
890void PrismNextStateGenerator<ValueType, StateType>::addSynchronousChoices(std::vector<Choice<ValueType>>& choices, CompressedState const& state,
891 StateToIdCallback const& stateToIdCallback, CommandFilter const& commandFilter) {
892 for (uint_fast64_t actionIndex : program.getSynchronizingActionIndices()) {
893 if (this->actionMask != nullptr) {
894 if (!this->actionMask->query(*this, actionIndex)) {
895 continue;
896 }
897 }
898 boost::optional<std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>>> optionalActiveCommandLists =
899 getActiveCommandsByActionIndex(actionIndex, commandFilter);
900
901 // Only process this action label, if there is at least one feasible solution.
902 if (optionalActiveCommandLists) {
903 std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>> const& activeCommandList = optionalActiveCommandLists.get();
904 std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>::const_iterator> iteratorList(activeCommandList.size());
905
906 // Initialize the list of iterators.
907 for (size_t i = 0; i < activeCommandList.size(); ++i) {
908 iteratorList[i] = activeCommandList[i].cbegin();
909 }
910
911 storm::generator::Distribution<StateType, ValueType> distribution;
912
913 // As long as there is one feasible combination of commands, keep on expanding it.
914 bool done = false;
915 while (!done) {
916 distribution.clear();
917 generateSynchronizedDistribution(state, storm::utility::one<ValueType>(), 0, iteratorList, distribution, stateToIdCallback);
918 distribution.compress();
919
920 // At this point, we applied all commands of the current command combination and newTargetStates
921 // contains all target states and their respective probabilities. That means we are now ready to
922 // add the choice to the list of transitions.
923 choices.push_back(Choice<ValueType>(actionIndex));
924
925 // Now create the actual distribution.
926 Choice<ValueType>& choice = choices.back();
927
929 storm::storage::PlayerIndex const& playerOfAction = actionIndexToPlayerIndexMap.at(actionIndex);
930 STORM_LOG_THROW(playerOfAction != storm::storage::INVALID_PLAYER_INDEX, storm::exceptions::WrongFormatException,
931 "Action " << program.getActionName(actionIndex)
932 << " is not owned by any player but has at least one enabled, unlabeled (synchronized) command.");
933 choice.setPlayerIndex(playerOfAction);
934 }
935
936 // Remember the choice label and origins only if we were asked to.
937 if (this->options.isBuildChoiceLabelsSet()) {
938 choice.addLabel(program.getActionName(actionIndex));
939 }
940 if (this->options.isBuildChoiceOriginsSet()) {
941 CommandSet commandIndices;
942 for (uint_fast64_t i = 0; i < iteratorList.size(); ++i) {
943 commandIndices.insert(iteratorList[i]->get().getGlobalIndex());
944 }
945 choice.addOriginData(boost::any(std::move(commandIndices)));
946 }
947
948 // Add the probabilities/rates to the newly created choice.
950 choice.reserve(std::distance(distribution.begin(), distribution.end()));
951 for (auto const& stateProbability : distribution) {
952 choice.addProbability(stateProbability.getState(), stateProbability.getValue());
953 if (this->options.isExplorationChecksSet()) {
954 probabilitySum += stateProbability.getValue();
955 }
956 }
957
958 if (this->options.isExplorationChecksSet()) {
959 // Check that the resulting distribution is in fact a distribution.
960 STORM_LOG_THROW(!program.isDiscreteTimeModel() || !storm::utility::isConstant(probabilitySum) || this->comparator.isOne(probabilitySum),
961 storm::exceptions::WrongFormatException,
962 "Sum of update probabilities do not some to one for some command (actually sum to " << probabilitySum << ").");
963 }
964
965 // Create the state-action reward for the newly created choice.
966 for (auto const& rewardModel : rewardModels) {
967 ValueType stateActionRewardValue = storm::utility::zero<ValueType>();
968 if (rewardModel.get().hasStateActionRewards()) {
969 for (auto const& stateActionReward : rewardModel.get().getStateActionRewards()) {
970 if (stateActionReward.getActionIndex() == choice.getActionIndex() &&
971 this->evaluator->asBool(stateActionReward.getStatePredicateExpression())) {
972 stateActionRewardValue += ValueType(this->evaluator->asRational(stateActionReward.getRewardValueExpression()));
973 }
974 }
975 }
976 choice.addReward(stateActionRewardValue);
977 }
978
979 // Now, check whether there is one more command combination to consider.
980 bool movedIterator = false;
981 for (int_fast64_t j = iteratorList.size() - 1; !movedIterator && j >= 0; --j) {
982 ++iteratorList[j];
983 if (iteratorList[j] != activeCommandList[j].end()) {
984 movedIterator = true;
985 } else {
986 // Reset the iterator to the beginning of the list.
987 iteratorList[j] = activeCommandList[j].begin();
988 }
989 }
990
991 done = !movedIterator;
992 }
993 }
994 }
995}
996
997template<typename ValueType, typename StateType>
998std::map<std::string, storm::storage::PlayerIndex> PrismNextStateGenerator<ValueType, StateType>::getPlayerNameToIndexMap() const {
999 return program.getPlayerNameToIndexMapping();
1000}
1001
1002template<typename ValueType, typename StateType>
1004 std::vector<StateType> const& initialStateIndices,
1005 std::vector<StateType> const& deadlockStateIndices,
1006 std::vector<StateType> const& unexploredStateIndices) {
1007 // Gather a vector of labels and their expressions.
1008 std::vector<std::pair<std::string, storm::expressions::Expression>> labels;
1009 if (this->options.isBuildAllLabelsSet()) {
1010 for (auto const& label : program.getLabels()) {
1011 labels.push_back(std::make_pair(label.getName(), label.getStatePredicateExpression()));
1012 }
1013 } else {
1014 for (auto const& labelName : this->options.getLabelNames()) {
1015 if (program.hasLabel(labelName)) {
1016 labels.push_back(std::make_pair(labelName, program.getLabelExpression(labelName)));
1017 } else {
1018 STORM_LOG_THROW(this->isSpecialLabel(labelName), storm::exceptions::InvalidArgumentException,
1019 "Cannot build labeling for unknown label '" << labelName << "'.");
1020 }
1021 }
1022 }
1023
1024 return NextStateGenerator<ValueType, StateType>::label(stateStorage, initialStateIndices, deadlockStateIndices, unexploredStateIndices, labels);
1025}
1026
1027template<typename ValueType, typename StateType>
1028storm::storage::BitVector PrismNextStateGenerator<ValueType, StateType>::evaluateObservationLabels(CompressedState const& state) const {
1029 // TODO consider to avoid reloading by computing these bitvectors in an earlier build stage
1031
1032 if (program.getNumberOfObservationLabels() == 0) {
1033 return result;
1034 }
1035 unpackStateIntoEvaluator(state, this->variableInformation, *this->evaluator);
1036 for (uint64_t i = 0; i < program.getNumberOfObservationLabels(); ++i) {
1037 result.setFromInt(64 * i, 64, this->evaluator->asInt(program.getObservationLabels()[i].getStatePredicateExpression()));
1038 }
1039 return result;
1040}
1041
1042template<typename ValueType, typename StateType>
1043void PrismNextStateGenerator<ValueType, StateType>::extendStateInformation(storm::json<BaseValueType>& result) const {
1044 for (uint64_t i = 0; i < program.getNumberOfObservationLabels(); ++i) {
1045 result[program.getObservationLabels()[i].getName()] = this->evaluator->asInt(program.getObservationLabels()[i].getStatePredicateExpression());
1046 }
1047}
1048
1049template<typename ValueType, typename StateType>
1051 return rewardModels.size();
1052}
1053
1054template<typename ValueType, typename StateType>
1056 storm::prism::RewardModel const& rewardModel = rewardModels[index].get();
1057 return storm::builder::RewardModelInformation(rewardModel.getName(), rewardModel.hasStateRewards(), rewardModel.hasStateActionRewards(),
1058 rewardModel.hasTransitionRewards());
1059}
1060
1061template<typename ValueType, typename StateType>
1062std::shared_ptr<storm::storage::sparse::ChoiceOrigins> PrismNextStateGenerator<ValueType, StateType>::generateChoiceOrigins(
1063 std::vector<boost::any>& dataForChoiceOrigins) const {
1064 if (!this->getOptions().isBuildChoiceOriginsSet()) {
1065 return nullptr;
1066 }
1067
1068 std::vector<uint_fast64_t> identifiers;
1069 identifiers.reserve(dataForChoiceOrigins.size());
1070
1071 std::map<CommandSet, uint_fast64_t> commandSetToIdentifierMap;
1072 // The empty commandset (i.e., the choices without origin) always has to get identifier getIdentifierForChoicesWithNoOrigin() -- which is assumed to be 0
1073 STORM_LOG_ASSERT(storm::storage::sparse::ChoiceOrigins::getIdentifierForChoicesWithNoOrigin() == 0, "The no origin identifier is assumed to be zero.");
1074 commandSetToIdentifierMap.insert(std::make_pair(CommandSet(), 0));
1075 uint_fast64_t currentIdentifier = 1;
1076 for (boost::any& originData : dataForChoiceOrigins) {
1077 STORM_LOG_ASSERT(originData.empty() || boost::any_cast<CommandSet>(&originData) != nullptr,
1078 "Origin data has unexpected type: " << originData.type().name() << ".");
1079
1080 CommandSet currentCommandSet = originData.empty() ? CommandSet() : boost::any_cast<CommandSet>(std::move(originData));
1081 auto insertionRes = commandSetToIdentifierMap.insert(std::make_pair(std::move(currentCommandSet), currentIdentifier));
1082 identifiers.push_back(insertionRes.first->second);
1083 if (insertionRes.second) {
1084 ++currentIdentifier;
1085 }
1086 }
1087
1088 std::vector<CommandSet> identifierToCommandSetMapping(currentIdentifier);
1089 for (auto const& setIdPair : commandSetToIdentifierMap) {
1090 identifierToCommandSetMapping[setIdPair.second] = setIdPair.first;
1091 }
1092
1093 return std::make_shared<storm::storage::sparse::PrismChoiceOrigins>(std::make_shared<storm::prism::Program>(program), std::move(identifiers),
1094 std::move(identifierToCommandSetMapping));
1095}
1096
1097template<typename ValueType, typename StateType>
1098bool PrismNextStateGenerator<ValueType, StateType>::isCommandPotentiallySynchronizing(const prism::Command& command) const {
1099 return program.getPossiblySynchronizingCommands().get(command.getGlobalIndex());
1100}
1101
1102template class PrismNextStateGenerator<double>;
1103template class PrismNextStateGenerator<storm::RationalNumber>;
1104template class PrismNextStateGenerator<storm::RationalFunction>;
1105template class PrismNextStateGenerator<storm::Interval>;
1106template class PrismNextStateGenerator<storm::RationalInterval>;
1107} // namespace generator
1108} // 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 > const & 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 clear()
Resets this to an empty behavior, i.e.
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:173
#define STORM_LOG_DEBUG(message)
Definition logging.h:28
#define STORM_LOG_TRACE(message)
Definition logging.h:22
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:39
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:130
bool isConstant(ValueType const &)
bool isZero(ValueType const &a)
Definition constants.cpp:44
ValueType zero()
Definition constants.cpp:26
ValueType one()
Definition constants.cpp:21
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:129
void addLabels(std::set< std::string > const &labels)
Adds the given label set to the labels associated with this choice.
Definition Choice.cpp:95
void addProbability(StateType const &state, ValueType const &value)
Adds the given probability value to the given state in the underlying distribution.
Definition Choice.cpp:171
void addReward(ValueType const &value)
Adds the given value to the reward associated with this choice.
Definition Choice.cpp:177