117 auto const& index = umbModel.
index;
122 auto checkNum = [&err](uint64_t num,
auto&& name, uint64_t lowerBound = 0) {
124 err <<
"Number of " << name <<
" is not set.\n";
126 }
else if (num < lowerBound) {
127 err <<
"Number of " << name <<
" is " << num <<
" which is below lower bound " << lowerBound <<
".\n";
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");
142 if (tsIndex.branchProbabilityType) {
145 err <<
"Branch probability type must be a continuous numeric type.\n";
149 if (tsIndex.exitRateType) {
152 err <<
"Exit rate type must be a continuous numeric type.\n";
156 if (tsIndex.observationProbabilityType) {
159 err <<
"Observation probability type must be a continuous numeric type.\n";
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";
170 if (index.annotations) {
171 for (
auto const& [annotationType, annotationMap] : index.annotations.value()) {
172 for (
auto const& [name, annotation] : annotationMap) {
174 if (annotation.probabilityType) {
177 err <<
"Probability type for annotation '" << name <<
"' must be a continuous numeric type.\n";
181 if (annotationType ==
"aps") {
183 err <<
"Atomic proposition annotation '" << name <<
"' must be of boolean type.\n";
186 }
else if (annotationType ==
"rewards") {
188 err <<
"Reward annotation '" << name <<
"' must have numeric type.\n";
196 if (index.valuations) {
197 boost::pfr::for_each_field(index.valuations.value(), [&isValid, &err](
auto const& description) {
198 if (!description.has_value()) {
201 if (description->classes.empty()) {
202 err <<
"A valuation description has no classes.\n";
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";
213 isValid &= validation::validateTypeDeclaration(variable.type, false, err);
216 if (descr.sizeInBits() % 8 != 0) {
217 err <<
"A valuation description has size " << descr.sizeInBits() <<
" bits which is not a multiple of 8.\n";
229 auto isExpectedBoolVectorSize = [](uint64_t
const actual, uint64_t
const expected) {
230 return actual == expected || actual == ((expected + 63) / 64) * 64;
232 auto isExpectedTypedVectorSize = [](uint64_t
const actual,
storm::umb::SizedType const& type, uint64_t
const expected) {
234 if (actual == expected) {
241 return actual == ((expected + 63) / 64) * 64;
246 return actual == 2 * expected;
249 return actual == expected * type.bitSize() / 64;
252 return actual == (expected * type.bitSize() / 64);
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";
266 }
else if (umbModel.stateToPlayer->size() != tsIndex.numStates) {
267 err <<
"state-to-player mapping has invalid size: " << umbModel.stateToPlayer->size() <<
" != #states=" << tsIndex.numStates <<
".\n";
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";
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";
279 }
else if (!isExpectedBoolVectorSize(umbModel.stateIsMarkovian->size(), tsIndex.numStates)) {
280 err <<
"state-is-markovian has invalid size: " << umbModel.stateIsMarkovian->size() <<
" != #states=" << tsIndex.numStates <<
".\n";
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";
289 if (!tsIndex.exitRateType.has_value()) {
290 err <<
"state-to-exit-rate mapping is given but exit rate type is not declared.\n";
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";
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";
302 isValid &= validation::validateCsr(umbModel.choiceToBranches,
"choice-to-branch", tsIndex.numChoices, tsIndex.numBranches, err);
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";
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";
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";
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
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";
331 }
else if (al.values->size() != numEntity) {
332 err <<
"actions/" << entityName <<
"/values has invalid size: " << al.values->size() <<
" != #" << entityName <<
"=" << numEntity <<
".\n";
336 if (al.stringMapping.has_value() && numEntityActions == 0) {
337 err <<
"actions/" << entityName <<
"/string-mapping given but the number of " << entityName <<
"-actions is zero.\n";
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";
345 if (al.stringMapping.has_value()) {
347 validation::validateCsr(al.stringMapping, std::string(
"actions/") + entityName +
"/string-mapping", numEntityActions, al.strings->size(), err);
350 if (umbModel.choiceActions.has_value()) {
351 validateActionLabels(umbModel.choiceActions.value(),
"choices", tsIndex.numChoices, tsIndex.numChoiceActions);
353 if (umbModel.branchActions.has_value()) {
354 validateActionLabels(umbModel.branchActions.value(),
"branches", tsIndex.numBranches, tsIndex.numBranchActions);
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";
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";
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";
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";
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";
388 if (umbModel.stateObservations.has_value()) {
389 validateObservations(umbModel.stateObservations.value(),
"states", tsIndex.numStates, tsIndex.numObservations, tsIndex.observationProbabilityType);
391 if (umbModel.branchObservations.has_value()) {
392 validateObservations(umbModel.branchObservations.value(),
"branches", tsIndex.numBranches, tsIndex.numObservations, tsIndex.observationProbabilityType);
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";
405 }
else if (!isExpectedTypedVectorSize(av.values.size(), ai.type, numAnnotationValues)) {
406 err << context <<
"/values has invalid size: " << av.values.size() <<
" != #" << entityName <<
"-annotation-values=" << numAnnotationValues;
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";
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";
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";
424 if (av.stringMapping.has_value()) {
425 isValid &= validation::validateCsr(av.stringMapping, context +
"/string-mapping", ai.numStrings.value(), av.strings->size(), err);
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";
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()
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";
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";
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";
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";
462 validateAnnotationValues(annotationValues.states.value(), annotationType, annotationId,
"states", tsIndex.numStates, annotationIndex);
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";
470 validateAnnotationValues(annotationValues.choices.value(), annotationType, annotationId,
"choices", tsIndex.numChoices, annotationIndex);
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";
478 validateAnnotationValues(annotationValues.branches.value(), annotationType, annotationId,
"branches", tsIndex.numBranches, annotationIndex);
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";
486 validateAnnotationValues(annotationValues.observations.value(), annotationType, annotationId,
"observations", tsIndex.numObservations,
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";
495 validateAnnotationValues(annotationValues.players.value(), annotationType, annotationId,
"players", tsIndex.numPlayers, annotationIndex);
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";
508 if ((!v.valuationToClass.has_value() && !descr.classes.empty()) || descr.classes.size() == 1) {
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";
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";
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";
526 if (v.stringMapping.has_value()) {
527 isValid &= validation::validateCsr(v.stringMapping, context +
"/string-mapping", descr.numStrings.value(), v.strings->size(), err);
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";
535 validateValuation(umbModel.valuations.states.value(),
"states", tsIndex.numStates, umbModel.index.valuations->states.value());
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";
543 validateValuation(umbModel.valuations.choices.value(),
"choices", tsIndex.numChoices, umbModel.index.valuations->choices.value());
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";
551 validateValuation(umbModel.valuations.branches.value(),
"branches", tsIndex.numBranches, umbModel.index.valuations->branches.value());
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";
559 validateValuation(umbModel.valuations.observations.value(),
"observations", tsIndex.numObservations,
560 umbModel.index.valuations->observations.value());
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";
568 validateValuation(umbModel.valuations.players.value(),
"players", tsIndex.numPlayers, umbModel.index.valuations->players.value());