Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
Validation.cpp
Go to the documentation of this file.
2
3#include <sstream>
4#include <string_view>
5
8
11
12namespace storm::umb {
13
14namespace validation {
15bool validateCsr(auto const& csr, std::string_view const name, uint64_t numMappedElements, std::optional<uint64_t> expectedlastEntry, std::ostream& err) {
16 std::stringstream err_reason;
17 if (csr) {
18 // Check if csr has expected length and the form {0, ..., expectedlastEntry}
19 if (csr->size() != numMappedElements + 1) {
20 err_reason << "CSR has unexpected size: " << csr->size() << " != " << (numMappedElements + 1) << ".";
21 }
22 if (csr.value()[0] != 0) {
23 err_reason << "CSR has unexpected first entry: " << csr.value()[0] << " != 0"
24 << ".";
25 }
26 if (expectedlastEntry.has_value() && csr.value()[numMappedElements] != expectedlastEntry.value()) {
27 err_reason << "CSR has unexpected last entry: " << csr.value()[numMappedElements] << " != " << expectedlastEntry.value() << ".";
28 }
29 } else if (expectedlastEntry.has_value() && numMappedElements != expectedlastEntry.value()) { // we assume a 1:1 mapping
30 err_reason << "CSR is not given and the default 1:1 mapping {0, ... ," << numMappedElements << "} does not match. Expected the mapping to end with '"
31 << expectedlastEntry.value() << "'"
32 << ".";
33 }
34 if (!err_reason.view().empty()) {
35 err << "Validation error in CSR mapping '" << name << "':\n\t" << err_reason.str() << "\n";
36 return false;
37 }
38 return true;
39}
40
41bool validateTypeDeclaration(storm::umb::SizedType const& type, bool requireStandardSize, std::ostream& err) {
42 using enum storm::umb::Type;
43 uint64_t const size = type.bitSize();
44 if (size == 0) {
45 err << "Type declaration " << type.toString() << " has size 0.\n";
46 return false;
47 }
48 uint64_t const defaultSize = defaultBitSize(type.type);
49 bool sizeError = false;
50 switch ((storm::umb::Type)type.type) {
51 case Double:
52 case DoubleInterval:
53 case String:
54 // types that always must be their default size
55 sizeError = size != defaultSize;
56 break;
57 case Bool:
58 case Int:
59 case Uint:
60 case IntInterval:
61 case UintInterval:
62 // types that occasionally must be their default size
63 sizeError = requireStandardSize && (size != defaultSize);
64 break;
65 // types that occasionally must be a multiple of their default size
66 case Rational:
68 // types that occasionally must be their default size
69 sizeError = requireStandardSize && ((size % defaultSize) != 0);
70 break;
71 }
72 if (isIntervalType(type.type)) {
73 // interval type sizes must be multiples of four or two
74 sizeError = sizeError || (size % (type.type == RationalInterval ? 4 : 2) != 0);
75 }
76 if (sizeError) {
77 err << "Type declaration " << type.toString() << " has invalid bit size: " << size << " (default size is " << defaultSize << ").\n";
78 return false;
79 }
80 return true;
81}
82
84 if (!vector.hasValue()) {
85 return true; // no values are given so nothing wrong with the vector.
86 }
87
88 using enum storm::umb::Type;
89 switch ((storm::umb::Type)type.type) {
90 case Bool:
91 return vector.isType<bool>();
92 case Int:
93 case IntInterval:
94 return vector.isType<int64_t>();
95 case Uint:
96 case UintInterval:
97 return vector.isType<uint64_t>();
98 case Double:
99 return vector.isType<double>();
100 case DoubleInterval:
101 return vector.isType<double>() || vector.isType<storm::Interval>();
102 case Rational:
103 return vector.isType<storm::RationalNumber>() || vector.isType<uint64_t>(); // rationals might be encoded as uint64_t
104 case RationalInterval:
105 return vector.isType<storm::RationalInterval>() || vector.isType<uint64_t>(); // rationals might be encoded as uint64_t
106 case String:
107 return vector.isType<uint64_t>(); // strings are encoded by their indices
108 }
109 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unhandled type.");
110}
111} // namespace validation
112
113bool validate(storm::umb::UmbModel const& umbModel, std::ostream& err) {
115 // Index
117 auto const& index = umbModel.index;
118 auto const& tsIndex = index.transitionSystem;
119 bool isValid = true;
120
121 // validate counts
122 auto checkNum = [&err](uint64_t num, auto&& name, uint64_t lowerBound = 0) {
124 err << "Number of " << name << " is not set.\n";
125 return false;
126 } else if (num < lowerBound) {
127 err << "Number of " << name << " is " << num << " which is below lower bound " << lowerBound << ".\n";
128 return false;
129 }
130 return true;
131 };
132 isValid &= checkNum(tsIndex.numPlayers, "players");
133 isValid &= checkNum(tsIndex.numStates, "states", 1u);
134 isValid &= checkNum(tsIndex.numInitialStates, "initial-states");
135 isValid &= checkNum(tsIndex.numChoices, "choices");
136 isValid &= checkNum(tsIndex.numChoiceActions, "choice-actions");
137 isValid &= checkNum(tsIndex.numBranches, "branches");
138 isValid &= checkNum(tsIndex.numBranchActions, "branch-actions");
139 isValid &= checkNum(tsIndex.numObservations, "observations");
140
141 // validate types
142 if (tsIndex.branchProbabilityType) {
143 isValid &= validation::validateTypeDeclaration(tsIndex.branchProbabilityType.value(), true, err);
144 if (!isContinuousNumericType(tsIndex.branchProbabilityType->type)) {
145 err << "Branch probability type must be a continuous numeric type.\n";
146 isValid = false;
147 }
148 }
149 if (tsIndex.exitRateType) {
150 isValid &= validation::validateTypeDeclaration(tsIndex.exitRateType.value(), true, err);
151 if (!isContinuousNumericType(tsIndex.exitRateType->type)) {
152 err << "Exit rate type must be a continuous numeric type.\n";
153 isValid = false;
154 }
155 }
156 if (tsIndex.observationProbabilityType) {
157 isValid &= validation::validateTypeDeclaration(tsIndex.observationProbabilityType.value(), true, err);
158 if (!isContinuousNumericType(tsIndex.observationProbabilityType->type)) {
159 err << "Observation probability type must be a continuous numeric type.\n";
160 isValid = false;
161 }
162 }
163
164 if (bool const hasObservations = tsIndex.numObservations > 0; hasObservations != tsIndex.observationsApplyTo.has_value()) {
165 err << "observations-apply-to is " << (tsIndex.observationsApplyTo.has_value() ? "set" : "not set") << " although the number of observations is "
166 << tsIndex.numObservations << ".\n";
167 isValid = false;
168 }
169
170 if (index.annotations) {
171 for (auto const& [annotationType, annotationMap] : index.annotations.value()) {
172 for (auto const& [name, annotation] : annotationMap) {
173 isValid &= validation::validateTypeDeclaration(annotation.type, true, err);
174 if (annotation.probabilityType) {
175 isValid &= validation::validateTypeDeclaration(annotation.probabilityType.value(), true, err);
176 if (!isContinuousNumericType(annotation.probabilityType->type)) {
177 err << "Probability type for annotation '" << name << "' must be a continuous numeric type.\n";
178 isValid = false;
179 }
180 }
181 if (annotationType == "aps") {
182 if (!isBooleanType(annotation.type.type)) {
183 err << "Atomic proposition annotation '" << name << "' must be of boolean type.\n";
184 isValid = false;
185 }
186 } else if (annotationType == "rewards") {
187 if (!isNumericType(annotation.type.type)) {
188 err << "Reward annotation '" << name << "' must have numeric type.\n";
189 isValid = false;
190 }
191 }
192 }
193 }
194 }
195
196 if (index.valuations) {
197 boost::pfr::for_each_field(index.valuations.value(), [&isValid, &err](auto const& description) {
198 if (!description.has_value()) {
199 return;
200 }
201 if (description->classes.empty()) {
202 err << "A valuation description has no classes.\n";
203 isValid = false;
204 }
205 for (auto const& descr : description->classes) {
206 for (auto const& var : descr.variables) {
207 if (std::holds_alternative<storm::storage::sparse::ValuationClassDescription::Variable>(var)) {
208 auto const& variable = std::get<storm::storage::sparse::ValuationClassDescription::Variable>(var);
209 if (variable.name.empty()) {
210 err << "A valuation description has a variable with an empty name.\n";
211 isValid = false;
212 }
213 isValid &= validation::validateTypeDeclaration(variable.type, false, err);
214 }
215 }
216 if (descr.sizeInBits() % 8 != 0) {
217 err << "A valuation description has size " << descr.sizeInBits() << " bits which is not a multiple of 8.\n";
218 isValid = false;
219 }
220 }
221 });
222 }
223
225 // Files
227
228 // Validate the expected size of TO1<..> and SEQ<..> vectors.
229 auto isExpectedBoolVectorSize = [](uint64_t const actual, uint64_t const expected) {
230 return actual == expected || actual == ((expected + 63) / 64) * 64; // size might be rounded up to the nearest multiple of 64
231 };
232 auto isExpectedTypedVectorSize = [](uint64_t const actual, storm::umb::SizedType const& type, uint64_t const expected) {
233 // Either the size matches exactly or we have a type-dependend encoding where other sizes are possible, too.
234 if (actual == expected) {
235 return true;
236 } else {
237 using enum storm::umb::Type;
238 switch (type.type) {
239 case Bool:
240 // size might be rounded up to the nearest multiple of 64
241 return actual == ((expected + 63) / 64) * 64;
242 case IntInterval:
243 case UintInterval:
244 case DoubleInterval:
245 // vector might be encoded by storing lower and upper separately
246 return actual == 2 * expected;
247 case Rational:
248 // might be encoded as uint64
249 return actual == expected * type.bitSize() / 64;
250 case RationalInterval:
251 // might be encoded as uint64
252 return actual == (expected * type.bitSize() / 64);
253 default:
254 // for all other types, the size must match exactly which we have checked above already
255 return false;
256 }
257 }
258 };
259
260 // States
261 isValid &= validation::validateCsr(umbModel.stateToChoices, "state-to-choice", tsIndex.numStates, tsIndex.numChoices, err);
262 if (umbModel.stateToPlayer.has_value()) {
263 if (tsIndex.numPlayers == 0) {
264 err << "state-to-player mapping is given but the model has no players.\n";
265 isValid = false;
266 } else if (umbModel.stateToPlayer->size() != tsIndex.numStates) {
267 err << "state-to-player mapping has invalid size: " << umbModel.stateToPlayer->size() << " != #states=" << tsIndex.numStates << ".\n";
268 isValid = false;
269 }
270 }
271 if (umbModel.stateIsInitial.has_value() && !isExpectedBoolVectorSize(umbModel.stateIsInitial->size(), tsIndex.numStates)) {
272 err << "state-is-initial has invalid size: " << umbModel.stateIsInitial->size() << " != #states=" << tsIndex.numStates << ".\n";
273 isValid = false;
274 }
275 if (umbModel.stateIsMarkovian.has_value()) {
276 if (tsIndex.time != ModelIndex::TransitionSystem::Time::UrgentStochastic) {
277 err << "state-is-markovian is given but the model does not have urgent-stochastic time.\n";
278 isValid = false;
279 } else if (!isExpectedBoolVectorSize(umbModel.stateIsMarkovian->size(), tsIndex.numStates)) {
280 err << "state-is-markovian has invalid size: " << umbModel.stateIsMarkovian->size() << " != #states=" << tsIndex.numStates << ".\n";
281 isValid = false;
282 }
283 }
284 if (umbModel.stateToExitRate.hasValue()) {
285 if (tsIndex.time == ModelIndex::TransitionSystem::Time::Discrete) {
286 err << "state-to-exit-rate mapping is given but the model has discrete time.\n";
287 isValid = false;
288 }
289 if (!tsIndex.exitRateType.has_value()) {
290 err << "state-to-exit-rate mapping is given but exit rate type is not declared.\n";
291 isValid = false;
292 } else if (!validation::vectorMatchesType(umbModel.stateToExitRate, tsIndex.exitRateType.value())) {
293 err << "state-to-exit-rate mapping has values that do not match the declared exit rate type " << tsIndex.exitRateType->toString() << ".\n";
294 isValid = false;
295 } else if (!isExpectedTypedVectorSize(umbModel.stateToExitRate.size(), tsIndex.exitRateType.value(), tsIndex.numStates)) {
296 err << "state-to-exit-rate mapping has invalid size: " << umbModel.stateToExitRate.size() << " != #states=" << tsIndex.numStates << ".\n";
297 isValid = false;
298 }
299 }
300
301 // Choices
302 isValid &= validation::validateCsr(umbModel.choiceToBranches, "choice-to-branch", tsIndex.numChoices, tsIndex.numBranches, err);
303
304 // Branches
305 if (umbModel.branchToTarget.has_value() && umbModel.branchToTarget->size() != tsIndex.numBranches) {
306 err << "branch-to-target mapping has invalid size: " << umbModel.branchToTarget->size() << " != #branches=" << tsIndex.numBranches << ".\n";
307 isValid = false;
308 }
309 if (umbModel.branchToProbability.hasValue()) {
310 if (!tsIndex.branchProbabilityType.has_value()) {
311 err << "branch-to-probability mapping is given but branch probability type is not declared.\n";
312 isValid = false;
313 } else if (!validation::vectorMatchesType(umbModel.branchToProbability, tsIndex.branchProbabilityType.value())) {
314 err << "branch-to-probability mapping has values that do not match the declared branch probability type "
315 << tsIndex.branchProbabilityType->toString() << ".\n";
316 isValid = false;
317 } else if (!isExpectedTypedVectorSize(umbModel.branchToProbability.size(), tsIndex.branchProbabilityType.value(), tsIndex.numBranches)) {
318 err << "branch-to-probability mapping has invalid size: " << umbModel.branchToProbability.size() << " != #branches=" << tsIndex.numBranches
319 << ".\n";
320 isValid = false;
321 }
322 }
323
324 // Action labels
325 auto validateActionLabels = [&isValid, &err](storm::umb::UmbModel::ActionLabels const& al, auto const& entityName, uint64_t const numEntity,
326 uint64_t const numEntityActions) {
327 if (al.values.has_value()) {
328 if (numEntityActions == 0) {
329 err << "actions/" << entityName << "/values given but the number of " << entityName << "-actions is zero.\n";
330 isValid = false;
331 } else if (al.values->size() != numEntity) {
332 err << "actions/" << entityName << "/values has invalid size: " << al.values->size() << " != #" << entityName << "=" << numEntity << ".\n";
333 isValid = false;
334 }
335 }
336 if (al.stringMapping.has_value() && numEntityActions == 0) {
337 err << "actions/" << entityName << "/string-mapping given but the number of " << entityName << "-actions is zero.\n";
338 isValid = false;
339 }
340 if (al.stringMapping.has_value() != al.strings.has_value()) {
341 err << "actions/" << entityName << "/string-mapping is " << (al.stringMapping.has_value() ? "set" : "not set") << " although actions/" << entityName
342 << "/strings is " << (al.strings.has_value() ? "set" : "not set") << ".\n";
343 isValid = false;
344 }
345 if (al.stringMapping.has_value()) {
346 isValid &=
347 validation::validateCsr(al.stringMapping, std::string("actions/") + entityName + "/string-mapping", numEntityActions, al.strings->size(), err);
348 }
349 };
350 if (umbModel.choiceActions.has_value()) {
351 validateActionLabels(umbModel.choiceActions.value(), "choices", tsIndex.numChoices, tsIndex.numChoiceActions);
352 }
353 if (umbModel.branchActions.has_value()) {
354 validateActionLabels(umbModel.branchActions.value(), "branches", tsIndex.numBranches, tsIndex.numBranchActions);
355 }
356
357 // Observations
358 auto validateObservations = [&isValid, &err, &isExpectedTypedVectorSize](storm::umb::UmbModel::Observations const& obs, auto const& entityName,
359 uint64_t const numEntity, uint64_t const numObservations,
360 std::optional<storm::umb::SizedType> const& obsProbType) {
361 if (obs.values.has_value()) {
362 if (numObservations == 0) {
363 err << "observations/" << entityName << "/values given but the number of observations is zero.\n";
364 isValid = false;
365 } else if (obs.probabilities.hasValue() || obs.values->size() != numEntity) {
366 err << "observations/" << entityName << "/values has invalid size: " << obs.values->size() << " != #" << entityName
367 << "-observation-values=" << numEntity << ".\n";
368 isValid = false;
369 }
370 isValid &= validation::validateCsr(obs.distributionMapping, std::string("observations/") + entityName + "/distribution-mapping", numEntity,
371 obs.values->size(), err);
372 if (obs.probabilities.hasValue()) {
373 if (!obsProbType.has_value()) {
374 err << "observations/" << entityName << "/probabilities given but observation probability type is not declared.\n";
375 isValid = false;
376 } else if (!validation::vectorMatchesType(obs.probabilities, obsProbType.value())) {
377 err << "observations/" << entityName << "/probabilities has values that do not match the declared observation probability type "
378 << obsProbType->toString() << ".\n";
379 isValid = false;
380 } else if (!isExpectedTypedVectorSize(obs.probabilities.size(), obsProbType.value(), numObservations)) {
381 err << "observations/" << entityName << "/probabilities has invalid size: " << obs.probabilities.size()
382 << " != #observations=" << numObservations << ".\n";
383 isValid = false;
384 }
385 }
386 }
387 };
388 if (umbModel.stateObservations.has_value()) {
389 validateObservations(umbModel.stateObservations.value(), "states", tsIndex.numStates, tsIndex.numObservations, tsIndex.observationProbabilityType);
390 }
391 if (umbModel.branchObservations.has_value()) {
392 validateObservations(umbModel.branchObservations.value(), "branches", tsIndex.numBranches, tsIndex.numObservations, tsIndex.observationProbabilityType);
393 }
394
395 // Annotations
396 auto validateAnnotationValues = [&isValid, &err, &isExpectedTypedVectorSize](storm::umb::UmbModel::AnnotationValues const& av, auto const& group,
397 auto const& id, auto const& entityName, uint64_t const numEntity,
399 auto const context = std::string("annotations/") + group + "/" + id + "/" + entityName;
400 uint64_t const numAnnotationValues = ai.numProbabilities.value_or(numEntity);
401 if (av.values.hasValue()) {
402 if (!validation::vectorMatchesType(av.values, ai.type)) {
403 err << context << "/values has values that do not match the declared annotation type " << ai.type.toString() << ".\n";
404 isValid = false;
405 } else if (!isExpectedTypedVectorSize(av.values.size(), ai.type, numAnnotationValues)) {
406 err << context << "/values has invalid size: " << av.values.size() << " != #" << entityName << "-annotation-values=" << numAnnotationValues;
407 isValid = false;
408 }
409 }
410 if (isStringType(ai.type.type) != av.stringMapping.has_value()) {
411 err << context << "/string-mapping is " << (av.stringMapping.has_value() ? "set" : "not set") << " although the annotation type is "
412 << ai.type.toString() << ".\n";
413 isValid = false;
414 }
415 if (av.stringMapping.has_value() && ai.numStrings.value_or(0) == 0) {
416 err << context << "/string-mapping given but the number of strings is zero.\n";
417 isValid = false;
418 }
419 if (av.stringMapping.has_value() != av.strings.has_value()) {
420 err << context << "/string-mapping is " << (av.stringMapping.has_value() ? "set" : "not set") << " although " << context << "/strings is "
421 << (av.strings.has_value() ? "set" : "not set") << ".\n";
422 isValid = false;
423 }
424 if (av.stringMapping.has_value()) {
425 isValid &= validation::validateCsr(av.stringMapping, context + "/string-mapping", ai.numStrings.value(), av.strings->size(), err);
426 }
427 isValid &= validation::validateCsr(av.distributionMapping, context + "/distribution-mapping", numEntity, numAnnotationValues, err);
428 if (av.probabilities.hasValue()) {
429 if (!ai.probabilityType.has_value()) {
430 err << context << "/probabilities given but annotation probability type is not declared.\n";
431 isValid = false;
432 } else if (!validation::vectorMatchesType(av.probabilities, ai.probabilityType.value())) {
433 err << context << "/probabilities has values that do not match the declared annotation probability type " << ai.probabilityType->toString()
434 << ".\n";
435 isValid = false;
436 } else if (!isExpectedTypedVectorSize(av.probabilities.size(), ai.probabilityType.value(), numAnnotationValues)) {
437 err << context << "/probabilities has invalid size: " << av.probabilities.size() << " != #" << entityName
438 << "-annotation-values=" << numAnnotationValues << ".\n";
439 isValid = false;
440 }
441 }
442 };
443 for (auto const& [annotationType, annotationMap] : umbModel.annotations) {
444 if (!umbModel.index.annotations.has_value() || !umbModel.index.annotations->contains(annotationType)) {
445 err << "Annotation '" << annotationType << "' is given but not declared in the index.\n";
446 isValid = false;
447 continue;
448 }
449 for (auto const& [annotationId, annotationValues] : annotationMap) {
450 if (!umbModel.index.annotations->at(annotationType).contains(annotationId)) {
451 err << "Annotation '" << annotationId << "' of type '" << annotationType << "' is given but not declared in the index.\n";
452 isValid = false;
453 continue;
454 }
455 auto const& annotationIndex = index.annotations->at(annotationType).at(annotationId);
456 if (annotationValues.states.has_value()) {
457 if (!annotationIndex.appliesToStates()) {
458 err << "Annotation '" << annotationId << "' of type '" << annotationType
459 << "' has states values but does not apply to states according to the index.\n";
460 isValid = false;
461 }
462 validateAnnotationValues(annotationValues.states.value(), annotationType, annotationId, "states", tsIndex.numStates, annotationIndex);
463 }
464 if (annotationValues.choices.has_value()) {
465 if (!annotationIndex.appliesToChoices()) {
466 err << "Annotation '" << annotationId << "' of type '" << annotationType
467 << "' has choices values but does not apply to choices according to the index.\n";
468 isValid = false;
469 }
470 validateAnnotationValues(annotationValues.choices.value(), annotationType, annotationId, "choices", tsIndex.numChoices, annotationIndex);
471 }
472 if (annotationValues.branches.has_value()) {
473 if (!annotationIndex.appliesToBranches()) {
474 err << "Annotation '" << annotationId << "' of type '" << annotationType
475 << "' has branches values but does not apply to branches according to the index.\n";
476 isValid = false;
477 }
478 validateAnnotationValues(annotationValues.branches.value(), annotationType, annotationId, "branches", tsIndex.numBranches, annotationIndex);
479 }
480 if (annotationValues.observations.has_value()) {
481 if (!annotationIndex.appliesToObservations()) {
482 err << "Annotation '" << annotationId << "' of type '" << annotationType
483 << "' has observations values but does not apply to observations according to the index.\n";
484 isValid = false;
485 }
486 validateAnnotationValues(annotationValues.observations.value(), annotationType, annotationId, "observations", tsIndex.numObservations,
487 annotationIndex);
488 }
489 if (annotationValues.players.has_value()) {
490 if (!annotationIndex.appliesToPlayers()) {
491 err << "Annotation '" << annotationId << "' of type '" << annotationType
492 << "' has players values but does not apply to players according to the index.\n";
493 isValid = false;
494 }
495 validateAnnotationValues(annotationValues.players.value(), annotationType, annotationId, "players", tsIndex.numPlayers, annotationIndex);
496 }
497 }
498 }
499
500 // Valuations
501 auto validateValuation = [&isValid, &err](storm::umb::UmbModel::Valuation const& v, auto const& entityName, uint64_t const numEntity,
503 auto const context = std::string("valuations/") + entityName;
504 if (v.valuationToClass.has_value() && v.valuationToClass->size() != numEntity) {
505 err << context << "/valuation-to-class has invalid size: " << v.valuationToClass->size() << " != #" << entityName << "=" << numEntity << ".\n";
506 isValid = false;
507 }
508 if ((!v.valuationToClass.has_value() && !descr.classes.empty()) || descr.classes.size() == 1) {
509 // common case: all entities have the same valuation class
510 auto const& classDescr = descr.classes.front();
511 if (v.valuations.has_value() && v.valuations->size() * 8 < classDescr.sizeInBits() * numEntity) {
512 err << context << "/valuations has invalid size: " << v.valuations->size() << " != size of one valuation class (" << classDescr.sizeInBits()
513 << " bits) * 8 * #entities=" << (classDescr.sizeInBits() * 8 * numEntity) << ".\n";
514 isValid = false;
515 }
516 }
517 if (v.stringMapping.has_value() && descr.numStrings.value_or(0) == 0) {
518 err << context << "/string-mapping given but the number of strings is zero.\n";
519 isValid = false;
520 }
521 if (v.stringMapping.has_value() != v.strings.has_value()) {
522 err << context << "/string-mapping is " << (v.stringMapping.has_value() ? "set" : "not set") << " although " << context << "/strings is "
523 << (v.strings.has_value() ? "set" : "not set") << ".\n";
524 isValid = false;
525 }
526 if (v.stringMapping.has_value()) {
527 isValid &= validation::validateCsr(v.stringMapping, context + "/string-mapping", descr.numStrings.value(), v.strings->size(), err);
528 }
529 };
530 if (umbModel.valuations.states.has_value()) {
531 if (!umbModel.index.valuations.has_value() || !umbModel.index.valuations->states.has_value()) {
532 err << "State valuations are given but no valuation descriptions are declared in the index.\n";
533 isValid = false;
534 } else {
535 validateValuation(umbModel.valuations.states.value(), "states", tsIndex.numStates, umbModel.index.valuations->states.value());
536 }
537 }
538 if (umbModel.valuations.choices.has_value()) {
539 if (!umbModel.index.valuations.has_value() || !umbModel.index.valuations->choices.has_value()) {
540 err << "Choice valuations are given but no valuation descriptions are declared in the index.\n";
541 isValid = false;
542 } else {
543 validateValuation(umbModel.valuations.choices.value(), "choices", tsIndex.numChoices, umbModel.index.valuations->choices.value());
544 }
545 }
546 if (umbModel.valuations.branches.has_value()) {
547 if (!umbModel.index.valuations.has_value() || !umbModel.index.valuations->branches.has_value()) {
548 err << "Branch valuations are given but no valuation descriptions are declared in the index.\n";
549 isValid = false;
550 } else {
551 validateValuation(umbModel.valuations.branches.value(), "branches", tsIndex.numBranches, umbModel.index.valuations->branches.value());
552 }
553 }
554 if (umbModel.valuations.observations.has_value()) {
555 if (!umbModel.index.valuations.has_value() || !umbModel.index.valuations->observations.has_value()) {
556 err << "Observation valuations are given but no valuation descriptions are declared in the index.\n";
557 isValid = false;
558 } else {
559 validateValuation(umbModel.valuations.observations.value(), "observations", tsIndex.numObservations,
560 umbModel.index.valuations->observations.value());
561 }
562 }
563 if (umbModel.valuations.players.has_value()) {
564 if (!umbModel.index.valuations.has_value() || !umbModel.index.valuations->players.has_value()) {
565 err << "Player valuations are given but no valuation descriptions are declared in the index.\n";
566 isValid = false;
567 } else {
568 validateValuation(umbModel.valuations.players.value(), "players", tsIndex.numPlayers, umbModel.index.valuations->players.value());
569 }
570 }
571
572 return isValid;
573}
574
576 std::stringstream errors;
577 STORM_LOG_THROW(validate(umbModel, errors), storm::exceptions::WrongFormatException,
578 "UMB model " << umbModel.getShortModelInformation() << " is invalid:\n"
579 << errors.str() << ".");
580}
581
582} // namespace storm::umb
Represents a model in the UMB format.
Definition UmbModel.h:21
std::string getShortModelInformation() const
Retrieves a short string that can be used to refer to the model in user output.
Definition UmbModel.cpp:11
ModelIndex index
Definition UmbModel.h:24
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:28
bool validateCsr(auto const &csr, std::string_view const name, uint64_t numMappedElements, std::optional< uint64_t > expectedlastEntry, std::ostream &err)
bool validateTypeDeclaration(storm::umb::SizedType const &type, bool requireStandardSize, std::ostream &err)
Validates a single type declaration against the UMB specification, writing potential errors to the gi...
bool vectorMatchesType(storm::umb::GenericVector const &vector, storm::umb::SizedType const &type)
Import and export of umb files.
bool isIntervalType(Type const type)
Definition Type.cpp:42
bool isNumericType(Type const type)
Definition Type.cpp:38
bool isBooleanType(Type const type)
Definition Type.cpp:8
uint64_t defaultBitSize(Type const type)
Returns the default size (in bits) of a type, if available.
Definition Type.cpp:59
bool isStringType(Type const type)
Definition Type.cpp:55
bool isContinuousNumericType(Type const type)
Definition Type.cpp:25
bool validate(storm::umb::UmbModel const &umbModel, std::ostream &err)
Validates the given UMB model and writes potential errors to the given output stream.
void validateOrThrow(storm::umb::UmbModel const &umbModel)
Validates the given UMB model.
carl::Interval< storm::RationalNumber > RationalInterval
carl::Interval< double > Interval
Interval type.
Describes all valuation classes for a set of entities (e.g.
struct storm::umb::ModelIndex::TransitionSystem transitionSystem
uint64_t bitSize() const
Definition Type.cpp:87
std::string toString() const
Definition Type.cpp:91
storm::SerializedEnum< storm::umb::TypeDeclaration > type
Definition Type.h:59