Skip to content

Commit

Permalink
Merge pull request #870 from qudt/feature/shacl-for-scientific-notation
Browse files Browse the repository at this point in the history
Add SHACL shapes ensuring correctness of scientific notation values
  • Loading branch information
steveraysteveray authored Mar 11, 2024
2 parents ec619a2 + 6860da0 commit 9635912
Show file tree
Hide file tree
Showing 4 changed files with 285 additions and 31 deletions.
179 changes: 178 additions & 1 deletion collections/COLLECTION_QUDT_QA_TESTS_ALL-v2.1.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,12 @@
sh:namespace "http://www.w3.org/ns/shacl#"^^xsd:anyURI ;
sh:prefix "sh" ;
] ;
.
sh:declare [
a sh:PrefixDeclaration ;
sh:namespace "http://www.w3.org/2001/XMLSchema#"^^xsd:anyURI ;
sh:prefix "xsd" ;
]
.
qudt:ClosedWorldShape
a sh:NodeShape ;
rdfs:isDefinedBy <http://qudt.org/2.1/collection/qa/all> ;
Expand Down Expand Up @@ -392,3 +397,175 @@ FILTER (?udv != ?qdv) .
] ;
sh:targetClass qudt:Unit ;
.

qudt:conversionMultiplierSnShape
sh:targetClass qudt:Unit;
rdfs:comment "qudt:conversionMultiplier must match qudt:conversionMultiplierSN if present" ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:minCount 1;
sh:message """{$this} qudt:conversionMultiplier is {?valueDecimalNoTrailingZeros}, which does not match the value of qudt:conversionMultiplierSN, {?valueSN}, converted to decimal notation, {?valueSNStringValue} . """ ;
sh:prefixes <http://qudt.org/2.1/collection/qa/all> ;
sh:select """
SELECT $this ?valueDecimal ?valueDecimalNoTrailingZeros ?valueSN ?m ?e ?exponent ?mNoDot ?mDigitCount ?valueSNStringValue ?onePos ?lastMantissaDigit ?leftStart ?leftEnd ?rightStart ?rightEnd ?result
WHERE {
# select both values
$this
qudt:conversionMultiplier ?valueDecimal ;
qudt:conversionMultiplierSN ?valueSN .
BIND(REPLACE(STR(?valueDecimal), "(\\\\.\\\\d)(\\\\d*[1-9])?0*$", "$1$2") as ?valueDecimalNoTrailingZeros) # remove trailing zeros from valueDecimal (string)
BIND(REPLACE(STR(?valueSN), "[eE]-?\\\\d+$", "") as ?m) # extract the mantissa (string)
BIND(REPLACE(STR(?valueSN), "^\\\\d(\\\\.\\\\d+)?[eE]", "") as ?e) # extract the exponent (string)
BIND(REPLACE(STR(?m),"(\\\\.|0+$)","") AS ?mNoDot) # remove the comma and trailing zeros from the mantissa
BIND(STRLEN(?mNoDot) as ?mDigitCount) # count the mantissa's characters
BIND(xsd:integer(?e) as ?exponent) # cast e to an integer, called exponent
# prepare a string with 200 zeros padding left and right, mantissa in the middle
BIND(CONCAT(
"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
?mNoDot,
"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") as ?baseStr)
# first char has index 1 !!!
BIND(201 + ?exponent as ?onePos)
BIND(200 + ?mDigitCount as ?lastMantissaDigit)
BIND(IF(?onePos > 201, 201, ?onePos) as ?leftStart)
BIND(?onePos+1 as ?leftEnd)
BIND(?onePos+1 as ?rightStart)
BIND(IF(?lastMantissaDigit+1 > ?onePos+2, ?lastMantissaDigit+1, ?onePos+2) as ?rightEnd)
# determine the expected string
BIND(CONCAT(SUBSTR(?baseStr, ?leftStart, ?leftEnd-?leftStart), ".", SUBSTR(?baseStr, ?rightStart, ?rightEnd - ?rightStart)) as ?valueSNStringValue)
# compare with actual decimal value
BIND(IF(?valueSNStringValue = STR(?valueDecimalNoTrailingZeros), "match!", "no match!") as ?result)
# only generate message if no match (useful during development)
FILTER(?result != "match!")
}
""" ;
] .

qudt:conversionOffsetSnShape
sh:targetClass qudt:Unit;
rdfs:comment "qudt:conversionOffset must match qudt:conversionOffsetSN if present" ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:minCount 1;
sh:message """{$this} qudt:conversionOffset is {?valueDecimalNoTrailingZeros}, which does not match the value of qudt:conversionOffsetSN, {?valueSN}, converted to decimal notation, {?valueSNStringValue} . """ ;
sh:prefixes <http://qudt.org/2.1/collection/qa/all> ;
sh:select """
SELECT $this ?valueDecimal ?valueDecimalNoTrailingZeros ?valueSN ?m ?e ?exponent ?mNoDot ?mDigitCount ?valueSNStringValue ?onePos ?lastMantissaDigit ?leftStart ?leftEnd ?rightStart ?rightEnd ?result
WHERE {
# select both values
$this
qudt:conversionOffset ?valueDecimal ;
qudt:conversionOffsetSN ?valueSN .
BIND(REPLACE(STR(?valueDecimal), "(\\\\.\\\\d)(\\\\d*[1-9])?0*$", "$1$2") as ?valueDecimalNoTrailingZeros) # remove trailing zeros from valueDecimal (string)
BIND(REPLACE(STR(?valueSN), "[eE]-?\\\\d+$", "") as ?m) # extract the mantissa (string)
BIND(REPLACE(STR(?valueSN), "^\\\\d(\\\\.\\\\d+)?[eE]", "") as ?e) # extract the exponent (string)
BIND(REPLACE(STR(?m),"(\\\\.|0+$)","") AS ?mNoDot) # remove the comma and trailing zeros from the mantissa
BIND(STRLEN(?mNoDot) as ?mDigitCount) # count the mantissa's characters
BIND(xsd:integer(?e) as ?exponent) # cast e to an integer, called exponent
# prepare a string with 200 zeros padding left and right, mantissa in the middle
BIND(CONCAT(
"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
?mNoDot,
"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") as ?baseStr)
# first char has index 1 !!!
BIND(201 + ?exponent as ?onePos)
BIND(200 + ?mDigitCount as ?lastMantissaDigit)
BIND(IF(?onePos > 201, 201, ?onePos) as ?leftStart)
BIND(?onePos+1 as ?leftEnd)
BIND(?onePos+1 as ?rightStart)
BIND(IF(?lastMantissaDigit+1 > ?onePos+2, ?lastMantissaDigit+1, ?onePos+2) as ?rightEnd)
# determine the expected string
BIND(CONCAT(SUBSTR(?baseStr, ?leftStart, ?leftEnd-?leftStart), ".", SUBSTR(?baseStr, ?rightStart, ?rightEnd - ?rightStart)) as ?valueSNStringValue)
# compare with actual decimal value
BIND(IF(?valueSNStringValue = STR(?valueDecimalNoTrailingZeros), "match!", "no match!") as ?result)
# only generate message if no match (useful during development)
FILTER(?result != "match!")
}
""" ;
] .

qudt:standardUncertaintySnShape
sh:targetClass qudt:ConstantValue;
rdfs:comment "qudt:standardUncertainty must match qudt:standardUncertaintySN if present" ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:minCount 1;
sh:message """{$this} qudt:standardUncertainty is {?valueDecimalNoTrailingZeros}, which does not match the value of qudt:standardUncertaintySN, {?valueSN}, converted to decimal notation, {?valueSNStringValue} . """ ;
sh:prefixes <http://qudt.org/2.1/collection/qa/all> ;
sh:select """
SELECT $this ?valueDecimal ?valueDecimalNoTrailingZeros ?valueSN ?m ?e ?exponent ?mNoDot ?mDigitCount ?valueSNStringValue ?onePos ?lastMantissaDigit ?leftStart ?leftEnd ?rightStart ?rightEnd ?result
WHERE {
# select both values
$this
qudt:standardUncertainty ?valueDecimal ;
qudt:standardUncertaintySN ?valueSN .
BIND(REPLACE(STR(?valueDecimal), "(\\\\.\\\\d)(\\\\d*[1-9])?0*$", "$1$2") as ?valueDecimalNoTrailingZeros) # remove trailing zeros from valueDecimal (string)
BIND(REPLACE(STR(?valueSN), "[eE]-?\\\\d+$", "") as ?m) # extract the mantissa (string)
BIND(REPLACE(STR(?valueSN), "^\\\\d(\\\\.\\\\d+)?[eE]", "") as ?e) # extract the exponent (string)
BIND(REPLACE(STR(?m),"(\\\\.|0+$)","") AS ?mNoDot) # remove the comma and trailing zeros from the mantissa
BIND(STRLEN(?mNoDot) as ?mDigitCount) # count the mantissa's characters
BIND(xsd:integer(?e) as ?exponent) # cast e to an integer, called exponent
# prepare a string with 200 zeros padding left and right, mantissa in the middle
BIND(CONCAT(
"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
?mNoDot,
"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") as ?baseStr)
# first char has index 1 !!!
BIND(201 + ?exponent as ?onePos)
BIND(200 + ?mDigitCount as ?lastMantissaDigit)
BIND(IF(?onePos > 201, 201, ?onePos) as ?leftStart)
BIND(?onePos+1 as ?leftEnd)
BIND(?onePos+1 as ?rightStart)
BIND(IF(?lastMantissaDigit+1 > ?onePos+2, ?lastMantissaDigit+1, ?onePos+2) as ?rightEnd)
# determine the expected string
BIND(CONCAT(SUBSTR(?baseStr, ?leftStart, ?leftEnd-?leftStart), ".", SUBSTR(?baseStr, ?rightStart, ?rightEnd - ?rightStart)) as ?valueSNStringValue)
# compare with actual decimal value
BIND(IF(?valueSNStringValue = STR(?valueDecimalNoTrailingZeros), "match!", "no match!") as ?result)
# only generate message if no match (useful during development)
FILTER(?result != "match!")
}
""" ;
] .

qudt:valueSnShape
sh:targetClass qudt:ConstantValue;
rdfs:comment "qudt:value must match qudt:valueSN if present" ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:minCount 1;
sh:message """{$this} qudt:value is {?valueDecimalNoTrailingZeros}, which does not match the value of qudt:valueSN, {?valueSN}, converted to decimal notation, {?valueSNStringValue} . """ ;
sh:prefixes <http://qudt.org/2.1/collection/qa/all> ;
sh:select """
SELECT $this ?valueDecimal ?valueDecimalNoTrailingZeros ?valueSN ?m ?e ?exponent ?mNoDot ?mDigitCount ?valueSNStringValue ?onePos ?lastMantissaDigit ?leftStart ?leftEnd ?rightStart ?rightEnd ?result
WHERE {
# select both values
$this
qudt:value ?valueDecimal ;
qudt:valueSN ?valueSN .
BIND(REPLACE(STR(?valueDecimal), "(\\\\.\\\\d)(\\\\d*[1-9])?0*$", "$1$2") as ?valueDecimalNoTrailingZeros) # remove trailing zeros from valueDecimal (string)
BIND(REPLACE(STR(?valueSN), "[eE]-?\\\\d+$", "") as ?m) # extract the mantissa (string)
BIND(REPLACE(STR(?valueSN), "^\\\\d(\\\\.\\\\d+)?[eE]", "") as ?e) # extract the exponent (string)
BIND(REPLACE(STR(?m),"(\\\\.|0+$)","") AS ?mNoDot) # remove the comma and trailing zeros from the mantissa
BIND(STRLEN(?mNoDot) as ?mDigitCount) # count the mantissa's characters
BIND(xsd:integer(?e) as ?exponent) # cast e to an integer, called exponent
# prepare a string with 200 zeros padding left and right, mantissa in the middle
BIND(CONCAT(
"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
?mNoDot,
"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") as ?baseStr)
# first char has index 1 !!!
BIND(201 + ?exponent as ?onePos)
BIND(200 + ?mDigitCount as ?lastMantissaDigit)
BIND(IF(?onePos > 201, 201, ?onePos) as ?leftStart)
BIND(?onePos+1 as ?leftEnd)
BIND(?onePos+1 as ?rightStart)
BIND(IF(?lastMantissaDigit+1 > ?onePos+2, ?lastMantissaDigit+1, ?onePos+2) as ?rightEnd)
# determine the expected string
BIND(CONCAT(SUBSTR(?baseStr, ?leftStart, ?leftEnd-?leftStart), ".", SUBSTR(?baseStr, ?rightStart, ?rightEnd - ?rightStart)) as ?valueSNStringValue)
# compare with actual decimal value
BIND(IF(?valueSNStringValue = STR(?valueDecimalNoTrailingZeros), "match!", "no match!") as ?result)
# only generate message if no match (useful during development)
FILTER(?result != "match!")
}
""" ;
] .
53 changes: 50 additions & 3 deletions schema/SCHEMA_QUDT-v2.1.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -1037,9 +1037,14 @@ qudt:Quantifiable
] ;
rdfs:subClassOf [
a owl:Restriction ;
owl:allValuesFrom xsd:double ;
owl:allValuesFrom xsd:decimal ;
owl:onProperty qudt:standardUncertainty ;
] ;
rdfs:subClassOf [
a owl:Restriction ;
owl:allValuesFrom xsd:double ;
owl:onProperty qudt:standardUncertaintySN ;
] ;
rdfs:subClassOf [
a owl:Restriction ;
owl:maxCardinality "1"^^xsd:int ;
Expand Down Expand Up @@ -1075,6 +1080,11 @@ qudt:Quantifiable
owl:maxCardinality "1"^^xsd:nonNegativeInteger ;
owl:onProperty qudt:value ;
] ;
rdfs:subClassOf [
a owl:Restriction ;
owl:maxCardinality "1"^^xsd:nonNegativeInteger ;
owl:onProperty qudt:valueSN ;
] ;
.
qudt:Quantity
a owl:Class ;
Expand Down Expand Up @@ -1859,6 +1869,16 @@ qudt:Unit
owl:maxCardinality "1"^^xsd:int ;
owl:onProperty qudt:conversionOffset ;
] ;
rdfs:subClassOf [
a owl:Restriction ;
owl:maxCardinality "1"^^xsd:int ;
owl:onProperty qudt:conversionMultiplierSN ;
] ;
rdfs:subClassOf [
a owl:Restriction ;
owl:maxCardinality "1"^^xsd:int ;
owl:onProperty qudt:conversionOffsetSN ;
] ;
rdfs:subClassOf [
a owl:Restriction ;
owl:maxCardinality "1"^^xsd:nonNegativeInteger ;
Expand Down Expand Up @@ -2175,14 +2195,28 @@ qudt:conversionMultiplier
a owl:FunctionalProperty ;
rdfs:isDefinedBy <http://qudt.org/2.1/schema/qudt> ;
rdfs:label "conversion multiplier" ;
rdfs:range dtype:numericUnion ;
rdfs:range xsd:decimal ;
.
qudt:conversionOffset
a owl:DatatypeProperty ;
a owl:FunctionalProperty ;
rdfs:isDefinedBy <http://qudt.org/2.1/schema/qudt> ;
rdfs:label "conversion offset" ;
rdfs:range dtype:numericUnion ;
rdfs:range xsd:decimal ;
.
qudt:conversionMultiplierSN
a owl:DatatypeProperty ;
a owl:FunctionalProperty ;
rdfs:isDefinedBy <http://qudt.org/2.1/schema/qudt> ;
rdfs:label "conversion multiplier scientific" ;
rdfs:range xsd:decimal ;
.
qudt:conversionOffsetSN
a owl:DatatypeProperty ;
a owl:FunctionalProperty ;
rdfs:isDefinedBy <http://qudt.org/2.1/schema/qudt> ;
rdfs:label "conversion offset scientific" ;
rdfs:range xsd:double ;
.
qudt:currencyCode
a owl:DatatypeProperty ;
Expand Down Expand Up @@ -3039,6 +3073,13 @@ qudt:standardUncertainty
dcterms:description "The standard uncertainty of a quantity is the estimated standard deviation of the mean taken from a series of measurements."^^rdf:HTML ;
rdfs:isDefinedBy <http://qudt.org/2.1/schema/qudt> ;
rdfs:label "standard uncertainty" ;
rdfs:range xsd:decimal ;
.
qudt:standardUncertaintySN
a owl:DatatypeProperty ;
dcterms:description "The standard uncertainty of a quantity is the estimated standard deviation of the mean taken from a series of measurements."^^rdf:HTML ;
rdfs:isDefinedBy <http://qudt.org/2.1/schema/qudt> ;
rdfs:label "standard uncertainty scientific" ;
rdfs:range xsd:double ;
.
qudt:symbol
Expand Down Expand Up @@ -3148,6 +3189,12 @@ qudt:value
rdfs:isDefinedBy <http://qudt.org/2.1/schema/qudt> ;
rdfs:label "value" ;
.
qudt:valueSN
a owl:DatatypeProperty ;
dcterms:description "A property to relate an observable thing with a value of any kind"^^rdf:HTML ;
rdfs:isDefinedBy <http://qudt.org/2.1/schema/qudt> ;
rdfs:label "value scientific" ;
.
qudt:valueQuantity
a owl:ObjectProperty ;
rdfs:isDefinedBy <http://qudt.org/2.1/schema/qudt> ;
Expand Down
Loading

0 comments on commit 9635912

Please sign in to comment.