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