Storm
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
ExplicitModelBuilder.cpp
Go to the documentation of this file.
2
3#include <map>
4
6
9
13
16
22
25
31
37#include "storm/utility/prism.h"
38
39namespace storm {
40namespace builder {
41
42template<typename StateType>
43StateType ExplicitStateLookup<StateType>::lookup(std::map<storm::expressions::Variable, storm::expressions::Expression> const& stateDescription) const {
44 auto cs = storm::generator::createCompressedState(this->varInfo, stateDescription, true);
45 // TODO search once
46 if (!stateToId.contains(cs)) {
47 return static_cast<StateType>(this->size());
48 }
49 return this->stateToId.getValue(cs);
50}
51
52template<typename StateType>
54 return this->stateToId.size();
55}
56
57template<typename ValueType, typename RewardModelType, typename StateType>
60 explorationOrder = buildSettings.getExplorationOrder();
61 fixDeadlocks = !buildSettings.isDontFixDeadlocksSet();
62 if (buildSettings.isExplorationStateLimitSet()) {
63 explorationStateLimit = buildSettings.getExplorationStateLimit();
64 }
65}
66
67template<typename ValueType, typename RewardModelType, typename StateType>
69 std::shared_ptr<storm::generator::NextStateGenerator<ValueType, StateType>> const& generator, Options const& options)
70 : generator(generator), options(options), stateStorage(generator->getStateSize()) {
71 // Intentionally left empty.
72}
73
74template<typename ValueType, typename RewardModelType, typename StateType>
76 storm::generator::NextStateGeneratorOptions const& generatorOptions,
77 Options const& builderOptions)
78 : ExplicitModelBuilder(std::make_shared<storm::generator::PrismNextStateGenerator<ValueType, StateType>>(program, generatorOptions), builderOptions) {
79 // Intentionally left empty.
80}
81
82template<typename ValueType, typename RewardModelType, typename StateType>
84 storm::generator::NextStateGeneratorOptions const& generatorOptions,
85 Options const& builderOptions)
86 : ExplicitModelBuilder(std::make_shared<storm::generator::JaniNextStateGenerator<ValueType, StateType>>(model, generatorOptions), builderOptions) {
87 // Intentionally left empty.
88}
89
90template<typename ValueType, typename RewardModelType, typename StateType>
91std::shared_ptr<storm::models::sparse::Model<ValueType, RewardModelType>> ExplicitModelBuilder<ValueType, RewardModelType, StateType>::build() {
92 STORM_LOG_DEBUG("Exploration order is: " << options.explorationOrder);
93
94 switch (generator->getModelType()) {
107 default:
108 STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Error while creating model: cannot handle this model type.");
109 }
110
111 return nullptr;
112}
113
114template<typename ValueType, typename RewardModelType, typename StateType>
116 StateType newIndex = static_cast<StateType>(stateStorage.getNumberOfStates());
117
118 // Check, if the state was already registered.
119 std::pair<StateType, std::size_t> actualIndexBucketPair = stateStorage.stateToId.findOrAddAndGetBucket(state, newIndex);
120
121 StateType actualIndex = actualIndexBucketPair.first;
122
123 if (actualIndex == newIndex) {
124 if (options.explorationOrder == ExplorationOrder::Dfs) {
125 statesToExplore.emplace_front(state, actualIndex);
126
127 // Reserve one slot for the new state in the remapping.
128 stateRemapping.get().push_back(storm::utility::zero<StateType>());
129 } else if (options.explorationOrder == ExplorationOrder::Bfs) {
130 statesToExplore.emplace_back(state, actualIndex);
131 } else {
132 STORM_LOG_ASSERT(false, "Invalid exploration order.");
133 }
134 }
135
136 return actualIndex;
137}
138
139template<typename ValueType, typename RewardModelType, typename StateType>
141 return ExplicitStateLookup<StateType>(this->generator->getVariableInformation(), this->stateStorage.stateToId);
142}
143
144template<typename ValueType, typename RewardModelType, typename StateType>
146 storm::storage::SparseMatrixBuilder<ValueType>& transitionMatrixBuilder,
147 std::vector<RewardModelBuilder<typename RewardModelType::ValueType>>& rewardModelBuilders,
148 StateAndChoiceInformationBuilder& stateAndChoiceInformationBuilder) {
149 // Initialize building state valuations (if necessary)
150 if (stateAndChoiceInformationBuilder.isBuildStateValuations()) {
151 stateAndChoiceInformationBuilder.stateValuationsBuilder() = generator->initializeStateValuationsBuilder();
152 }
153
154 // Create a callback for the next-state generator to enable it to request the index of states.
155 std::function<StateType(CompressedState const&)> stateToIdCallback =
156 std::bind(&ExplicitModelBuilder<ValueType, RewardModelType, StateType>::getOrAddStateIndex, this, std::placeholders::_1);
157
158 // If the exploration order is something different from breadth-first, we need to keep track of the remapping
159 // from state ids to row groups. For this, we actually store the reversed mapping of row groups to state-ids
160 // and later reverse it.
161 if (options.explorationOrder != ExplorationOrder::Bfs) {
162 stateRemapping = std::vector<uint_fast64_t>();
163 }
164
165 // Let the generator create all initial states.
166 this->stateStorage.initialStateIndices = generator->getInitialStates(stateToIdCallback);
167 STORM_LOG_THROW(!this->stateStorage.initialStateIndices.empty(), storm::exceptions::WrongFormatException,
168 "The model does not have a single initial state.");
169
170 // Now explore the current state until there is no more reachable state.
171 uint_fast64_t currentRowGroup = 0;
172 uint_fast64_t currentRow = 0;
173
174 auto timeOfStart = std::chrono::high_resolution_clock::now();
175 auto timeOfLastMessage = std::chrono::high_resolution_clock::now();
176 uint64_t numberOfExploredStates = 0;
177 uint64_t numberOfExploredStatesSinceLastMessage = 0;
178
179 // Perform a search through the model.
180 while (!statesToExplore.empty()) {
181 // Get the first state in the queue.
182 CompressedState currentState = statesToExplore.front().first;
183 StateType currentIndex = statesToExplore.front().second;
184 statesToExplore.pop_front();
185
186 // If the exploration order differs from breadth-first, we remember that this row group was actually
187 // filled with the transitions of a different state.
188 if (options.explorationOrder != ExplorationOrder::Bfs) {
189 stateRemapping.get()[currentIndex] = currentRowGroup;
190 }
191
192 if (currentIndex % 100000 == 0) {
193 STORM_LOG_TRACE("Exploring state with id " << currentIndex << ".");
194 }
195
196 generator->load(currentState);
197 if (stateAndChoiceInformationBuilder.isBuildStateValuations()) {
198 generator->addStateValuation(currentIndex, stateAndChoiceInformationBuilder.stateValuationsBuilder());
199 }
200
202 // If the exploration state limit is set and the limit is reached, we stop the exploration.
203 bool const stateLimitExceeded = options.explorationStateLimit.has_value() && stateStorage.getNumberOfStates() >= options.explorationStateLimit.value();
204 if (!stateLimitExceeded) {
205 behavior = generator->expand(stateToIdCallback);
206 }
207
208 if (behavior.empty()) {
209 // There are three possible cases for missing behavior:
210 if (behavior.wasExpanded()) {
211 // (a) The state is a deadlock state, i.e. there is no behavior even though the state was expanded
212 STORM_LOG_THROW(options.fixDeadlocks, storm::exceptions::WrongFormatException,
213 "Error while creating sparse matrix from probabilistic program: found deadlock state ("
214 << generator->stateToString(currentState) << "). For fixing these, please provide the appropriate option.");
215 this->stateStorage.deadlockStateIndices.push_back(currentIndex);
216 } else {
217 if (stateLimitExceeded) {
218 // (b) The state was not expanded because the state limit is reached
219 this->stateStorage.unexploredStateIndices.push_back(currentIndex);
220 }
221 // (c) the state was not expanded because it is terminal, i.e., exploration from that state is not required for the given property/ies
222 }
223
224 // In all cases, we need to add a self-loop to the transition matrix.
225
226 if (!generator->isDeterministicModel()) {
227 transitionMatrixBuilder.newRowGroup(currentRow);
228 }
229
230 transitionMatrixBuilder.addNextValue(currentRow, currentIndex, storm::utility::one<ValueType>());
231
232 for (auto& rewardModelBuilder : rewardModelBuilders) {
233 if (rewardModelBuilder.hasStateRewards()) {
234 rewardModelBuilder.addStateReward(storm::utility::zero<ValueType>());
235 }
236
237 if (rewardModelBuilder.hasStateActionRewards()) {
238 rewardModelBuilder.addStateActionReward(storm::utility::zero<ValueType>());
239 }
240 }
241
242 // This state shall be Markovian (to not introduce Zeno behavior)
243 if (stateAndChoiceInformationBuilder.isBuildMarkovianStates()) {
244 stateAndChoiceInformationBuilder.addMarkovianState(currentRowGroup);
245 }
246 // Other state-based information does not need to be treated, in particular:
247 // * StateValuations have already been set above
248 // * The associated player shall be the "default" player, i.e. INVALID_PLAYER_INDEX
249
250 ++currentRow;
251 ++currentRowGroup;
252 } else {
253 // Add the state rewards to the corresponding reward models.
254 auto stateRewardIt = behavior.getStateRewards().begin();
255 for (auto& rewardModelBuilder : rewardModelBuilders) {
256 if (rewardModelBuilder.hasStateRewards()) {
257 rewardModelBuilder.addStateReward(*stateRewardIt);
258 }
259 ++stateRewardIt;
260 }
261
262 // If the model is nondeterministic, we need to open a row group.
263 if (!generator->isDeterministicModel()) {
264 transitionMatrixBuilder.newRowGroup(currentRow);
265 }
266
267 // Now add all choices.
268 bool firstChoiceOfState = true;
269 for (auto const& choice : behavior) {
270 // add the generated choice information
271 if (stateAndChoiceInformationBuilder.isBuildChoiceLabels() && choice.hasLabels()) {
272 for (auto const& label : choice.getLabels()) {
273 stateAndChoiceInformationBuilder.addChoiceLabel(label, currentRow);
274 }
275 }
276 if (stateAndChoiceInformationBuilder.isBuildChoiceOrigins() && choice.hasOriginData()) {
277 stateAndChoiceInformationBuilder.addChoiceOriginData(choice.getOriginData(), currentRow);
278 }
279 if (stateAndChoiceInformationBuilder.isBuildStatePlayerIndications() && choice.hasPlayerIndex()) {
281 firstChoiceOfState || stateAndChoiceInformationBuilder.hasStatePlayerIndicationBeenSet(choice.getPlayerIndex(), currentRowGroup),
282 "There is a state where different players have an enabled choice."); // Should have been detected in generator, already
283 if (firstChoiceOfState) {
284 stateAndChoiceInformationBuilder.addStatePlayerIndication(choice.getPlayerIndex(), currentRowGroup);
285 }
286 }
287 if (stateAndChoiceInformationBuilder.isBuildMarkovianStates() && choice.isMarkovian()) {
288 stateAndChoiceInformationBuilder.addMarkovianState(currentRowGroup);
289 }
290
291 // Add the probabilistic behavior to the matrix.
292 for (auto const& stateProbabilityPair : choice) {
293 transitionMatrixBuilder.addNextValue(currentRow, stateProbabilityPair.first, stateProbabilityPair.second);
294 }
295
296 // Add the rewards to the reward models.
297 auto choiceRewardIt = choice.getRewards().begin();
298 for (auto& rewardModelBuilder : rewardModelBuilders) {
299 if (rewardModelBuilder.hasStateActionRewards()) {
300 rewardModelBuilder.addStateActionReward(*choiceRewardIt);
301 }
302 ++choiceRewardIt;
303 }
304 ++currentRow;
305 firstChoiceOfState = false;
306 }
307
308 ++currentRowGroup;
309 }
310
311 ++numberOfExploredStates;
312 if (generator->getOptions().isShowProgressSet()) {
313 ++numberOfExploredStatesSinceLastMessage;
314
315 auto now = std::chrono::high_resolution_clock::now();
316 auto durationSinceLastMessage = std::chrono::duration_cast<std::chrono::seconds>(now - timeOfLastMessage).count();
317 if (static_cast<uint64_t>(durationSinceLastMessage) >= generator->getOptions().getShowProgressDelay()) {
318 auto statesPerSecond = numberOfExploredStatesSinceLastMessage / durationSinceLastMessage;
319 auto durationSinceStart = std::chrono::duration_cast<std::chrono::seconds>(now - timeOfStart).count();
320 std::cout << "Explored " << numberOfExploredStates << " states in " << durationSinceStart << " seconds (currently " << statesPerSecond
321 << " states per second).\n";
322 timeOfLastMessage = std::chrono::high_resolution_clock::now();
323 numberOfExploredStatesSinceLastMessage = 0;
324 }
325 }
326
328 auto durationSinceStart = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::high_resolution_clock::now() - timeOfStart).count();
329 std::cout << "Explored " << numberOfExploredStates << " states in " << durationSinceStart << " seconds before abort.\n";
330 STORM_LOG_THROW(false, storm::exceptions::AbortException, "Aborted in state space exploration.");
331 break;
332 }
333 }
334
335 // If the exploration order was not breadth-first, we need to fix the entries in the matrix according to
336 // (reversed) mapping of row groups to indices.
337 if (options.explorationOrder != ExplorationOrder::Bfs) {
338 STORM_LOG_ASSERT(stateRemapping, "Unable to fix columns without mapping.");
339 std::vector<uint_fast64_t> const& remapping = stateRemapping.get();
340
341 // We need to fix the following entities:
342 // (a) the transition matrix
343 // (b) the initial states
344 // (c) the hash map storing the mapping states -> ids
345 // (d) fix remapping for state-generation labels
346
347 // Fix (a).
348 transitionMatrixBuilder.replaceColumns(remapping, 0);
349
350 // Fix (b).
351 std::vector<StateType> newInitialStateIndices(this->stateStorage.initialStateIndices.size());
352 std::transform(this->stateStorage.initialStateIndices.begin(), this->stateStorage.initialStateIndices.end(), newInitialStateIndices.begin(),
353 [&remapping](StateType const& state) { return remapping[state]; });
354 std::sort(newInitialStateIndices.begin(), newInitialStateIndices.end());
355 this->stateStorage.initialStateIndices = std::move(newInitialStateIndices);
356
357 // Fix (c).
358 this->stateStorage.stateToId.remap([&remapping](StateType const& state) { return remapping[state]; });
359
360 this->generator->remapStateIds([&remapping](StateType const& state) { return remapping[state]; });
361 }
362}
363
364template<typename ValueType, typename RewardModelType, typename StateType>
365storm::storage::sparse::ModelComponents<ValueType, RewardModelType> ExplicitModelBuilder<ValueType, RewardModelType, StateType>::buildModelComponents() {
366 // Determine whether we have to combine different choices to one or whether this model can have more than
367 // one choice per state.
368 bool deterministicModel = generator->isDeterministicModel();
369
370 // Prepare the component builders
371 storm::storage::SparseMatrixBuilder<ValueType> transitionMatrixBuilder(0, 0, 0, false, !deterministicModel, 0);
372 std::vector<RewardModelBuilder<typename RewardModelType::ValueType>> rewardModelBuilders;
373 for (uint64_t i = 0; i < generator->getNumberOfRewardModels(); ++i) {
374 rewardModelBuilders.emplace_back(generator->getRewardModelInformation(i));
375 }
376 StateAndChoiceInformationBuilder stateAndChoiceInformationBuilder;
377 stateAndChoiceInformationBuilder.setBuildChoiceLabels(generator->getOptions().isBuildChoiceLabelsSet());
378 stateAndChoiceInformationBuilder.setBuildChoiceOrigins(generator->getOptions().isBuildChoiceOriginsSet());
379 stateAndChoiceInformationBuilder.setBuildStatePlayerIndications(generator->getModelType() == storm::generator::ModelType::SMG);
380 stateAndChoiceInformationBuilder.setBuildMarkovianStates(generator->getModelType() == storm::generator::ModelType::MA);
381 stateAndChoiceInformationBuilder.setBuildStateValuations(generator->getOptions().isBuildStateValuationsSet());
382
383 buildMatrices(transitionMatrixBuilder, rewardModelBuilders, stateAndChoiceInformationBuilder);
384
385 // Initialize the model components with the obtained information.
387 transitionMatrixBuilder.build(0, transitionMatrixBuilder.getCurrentRowGroupCount()), buildStateLabeling(),
388 std::unordered_map<std::string, RewardModelType>(), !generator->isDiscreteTimeModel());
389
390 uint_fast64_t numStates = modelComponents.transitionMatrix.getColumnCount();
391 uint_fast64_t numChoices = modelComponents.transitionMatrix.getRowCount();
392
393 // Now finalize all reward models.
394 for (auto& rewardModelBuilder : rewardModelBuilders) {
395 modelComponents.rewardModels.emplace(rewardModelBuilder.getName(),
396 rewardModelBuilder.build(numChoices, modelComponents.transitionMatrix.getColumnCount(), numStates));
397 }
398 // Build the player assignment
399 if (stateAndChoiceInformationBuilder.isBuildStatePlayerIndications()) {
400 modelComponents.statePlayerIndications = stateAndChoiceInformationBuilder.buildStatePlayerIndications(numStates);
401 modelComponents.playerNameToIndexMap = generator->getPlayerNameToIndexMap();
402 }
403 // Build Markovian states
404 if (stateAndChoiceInformationBuilder.isBuildMarkovianStates()) {
405 modelComponents.markovianStates = stateAndChoiceInformationBuilder.buildMarkovianStates(numStates);
406 }
407 // Build the choice labeling
408 if (stateAndChoiceInformationBuilder.isBuildChoiceLabels()) {
409 modelComponents.choiceLabeling = stateAndChoiceInformationBuilder.buildChoiceLabeling(numChoices);
410 }
411 // If requested, build the state valuations and choice origins
412 if (stateAndChoiceInformationBuilder.isBuildStateValuations()) {
413 modelComponents.stateValuations = stateAndChoiceInformationBuilder.stateValuationsBuilder().build();
414 }
415 if (stateAndChoiceInformationBuilder.isBuildChoiceOrigins()) {
416 auto originData = stateAndChoiceInformationBuilder.buildDataOfChoiceOrigins(numChoices);
417 modelComponents.choiceOrigins = generator->generateChoiceOrigins(originData);
418 }
419 if (generator->isPartiallyObservable()) {
420 std::vector<uint32_t> classes(stateStorage.getNumberOfStates());
421 std::unordered_map<uint32_t, std::vector<std::pair<std::vector<std::string>, uint32_t>>> observationActions;
422 for (auto const& bitVectorIndexPair : stateStorage.stateToId) {
423 uint32_t varObservation = generator->observabilityClass(bitVectorIndexPair.first);
424 classes[bitVectorIndexPair.second] = varObservation;
425 }
426
427 modelComponents.observabilityClasses = classes;
428 if (generator->getOptions().isBuildObservationValuationsSet()) {
429 modelComponents.observationValuations = generator->makeObservationValuation();
430 }
431 }
432 return modelComponents;
433}
434
435template<typename ValueType, typename RewardModelType, typename StateType>
436storm::models::sparse::StateLabeling ExplicitModelBuilder<ValueType, RewardModelType, StateType>::buildStateLabeling() {
437 return generator->label(stateStorage, stateStorage.initialStateIndices, stateStorage.deadlockStateIndices, stateStorage.unexploredStateIndices);
438}
439
440// Explicitly instantiate the class.
441template class ExplicitModelBuilder<double, storm::models::sparse::StandardRewardModel<double>, uint32_t>;
442template class ExplicitStateLookup<uint32_t>;
443
444#ifdef STORM_HAVE_CARL
445template class ExplicitModelBuilder<RationalNumber, storm::models::sparse::StandardRewardModel<RationalNumber>, uint32_t>;
446template class ExplicitModelBuilder<RationalFunction, storm::models::sparse::StandardRewardModel<RationalFunction>, uint32_t>;
447template class ExplicitModelBuilder<double, storm::models::sparse::StandardRewardModel<storm::Interval>, uint32_t>;
448#endif
449} // namespace builder
450} // namespace storm
ExplicitStateLookup< StateType > exportExplicitStateLookup() const
Export a wrapper that contains (a copy of) the internal information that maps states to ids.
ExplicitModelBuilder(std::shared_ptr< storm::generator::NextStateGenerator< ValueType, StateType > > const &generator, Options const &options=Options())
Creates an explicit model builder that uses the provided generator.
std::shared_ptr< storm::models::sparse::Model< ValueType, RewardModelType > > build()
Convert the program given at construction time to an abstract model.
StateType lookup(std::map< storm::expressions::Variable, storm::expressions::Expression > const &stateDescription) const
Lookup state.
uint64_t size() const
How many states have been stored?
A structure that is used to keep track of a reward model currently being built.
This class collects information regarding the states and choices during model building.
storm::storage::sparse::StateValuationsBuilder & stateValuationsBuilder()
bool hasStatePlayerIndicationBeenSet(storm::storage::PlayerIndex expectedPlayer, uint_fast64_t stateIndex) const
void addStatePlayerIndication(storm::storage::PlayerIndex player, uint_fast64_t stateIndex)
void addChoiceLabel(std::string const &label, uint_fast64_t choiceIndex)
void addChoiceOriginData(boost::any const &originData, uint_fast64_t choiceIndex)
bool empty() const
Retrieves whether the behavior is empty in the sense that there are no available choices.
bool wasExpanded() const
Retrieves whether the state was expanded.
std::vector< ValueType > const & getStateRewards() const
Retrieves the list of state rewards under selected reward models.
This class manages the labeling of the state space with a number of (atomic) labels.
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:18
bool get(uint_fast64_t index) const
Retrieves the truth value of the bit at the given index and performs a bound check.
A class that can be used to build a sparse matrix by adding value by value.
index_type getCurrentRowGroupCount() const
Retrieves the current row group count.
void addNextValue(index_type row, index_type column, value_type const &value)
Sets the matrix entry at the given row and column to the given value.
void replaceColumns(std::vector< index_type > const &replacements, index_type offset)
Replaces all columns with id > offset according to replacements.
void newRowGroup(index_type startingRow)
Starts a new row group in the matrix.
SparseMatrix< value_type > build(index_type overriddenRowCount=0, index_type overriddenColumnCount=0, index_type overriddenRowGroupCount=0)
#define STORM_LOG_DEBUG(message)
Definition logging.h:23
#define STORM_LOG_TRACE(message)
Definition logging.h:17
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:11
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
CompressedState createCompressedState(VariableInformation const &varInfo, std::map< storm::expressions::Variable, storm::expressions::Expression > const &stateDescription, bool checkOutOfBounds)
storm::storage::BitVector CompressedState
SettingsType const & getModule()
Get module.
std::shared_ptr< storm::models::sparse::Model< ValueType, RewardModelType > > buildModelFromComponents(storm::models::ModelType modelType, storm::storage::sparse::ModelComponents< ValueType, RewardModelType > &&components)
Definition builder.cpp:19
bool isTerminate()
Check whether the program should terminate (due to some abort signal).
LabParser.cpp.
Definition cli.cpp:18
Options()
Creates an object representing the default building options.