I am trying to create an OWL ontology using Protege. I want to use inverse functional properties as a resemblance for primary keys from relational databases. For example, I have a property, that has a unique id as object, thus identifying the entity and no other entity should be allowed to use this value with that property.
As the object value is a string, it has to be a data property. But in Protege, you cannot assign the Inverse functional characteristic to a data property.
Why can't I declare a data property to be a inverse functional property and how else should I create the "unique key" logic if not like this?
Thanks in advance,
Frank
The restriction on datatype properties is purely due to computational complexity. Without the restriction, the logic of OWL 2 DL would not be decidable. However, it is possible to express a notion of unique key in OWL 2:
ex:key a owl:DatatypeProperty .
owl:Thing owl:hasKey ( ex:key ) .
However, there is a subtle difference between this and an inverse functional property. Consider the following:
ex:this a [
a owl:Restriction;
owl:onProperty ex:prop;
owl:minCardinality 2;
owl:onClass [
a owl:Restriction;
owl:onProperty ex:key;
owl:hasValue 1
]
] .
If ex:key is a key for owl:Thing, then this ontology is consistent. However, if ex:key could be an inverse functional property, then this ontology would not be consistent. The reason is due to the way keys work in OWL 2. For a key to identify something, the thing has to be named explicitly. There could be several unnamed things having the same key (here, the key is the number 1) and yet, they would not be considered equal as long as they are not declared explicitly in the ontology. However, with inverse functional property, it is not the case. As a result, we would be able to infer that everything having value 1 on property ex:key is the same thing, and therefore, ex:this cannot have 2 values for the property ex:prop.
Related
Sorry if this is a noob's and simple question, but it will help me resolve a conceptual confusion of mine! I have some guesses, but want to make sure.
I got the location of a part of brain via NeuroFMA ontology and the query below:
PREFIX fma: <http://sig.uw.edu/fma#>
select ?loc{
fma:Superior_temporal_gyrus fma:location ?loc}
The result was: fma:live_incus_fm_14056
I thought I might be able to get some more information on this item.
Question 1: Was there a difference if the result was a literal?
So, I used optional {?loc ?p ?o} and got some results.
However, I thought since this ontology also imported RDF and OWL, the following queries should work too, but it was not the case (hopefully these codes are correct)!
optional {?value rdfs:range ?loc}
optional {?loc rdfs:domain ?value}
optional {?loc rdf:type ?value}
Question 2 If the above queries are correct, are RDFS and OWL just a suggestion? Or do ontologies that import/ follow them have to use all their resources or at least expand on them?
Thanks!
An import declaration in OWL is, for the most part, just informative. It is typically used to signal that this ontology re-uses some of the concepts defined in the target (for example, it could define some additional subclasses of classes defined in the target data).
Whether the import results in any additional data being loaded into your dataset depends on what database/API/reasoner you use to process the ontology. Most tools don't automatically load the targets of import declarations, by default, so the presence or absence of the import-declaration will have no influence on what your queries return.
I thought since this ontology also imported RDF and OWL, the following queries should work too, but it was not the case (hopefully
these codes are correct)!
optional {?value rdfs:range ?loc}
optional {?loc rdfs:domain ?value}
optional {?loc rdfs:type ?value}
It's rdf:type, not rdfs:type. Apart from that, each of these individually look fine. However, judging from your broader query, ?loc is usually not a property, but a property value. Property values don't have domains and ranges. You could query for something like this, possibly:
optional { fma:location rdfs:domain ?value}
This asks "if the property fma:location has a domain declaration, return that declaration and bind it to the ?value variable".
More generally, whether these queries return any results has little or nothing to do with what import declaration are present in your ontology. If your ontology contains a range declaration for a property, the first pattern will return a result. If it contains a domain declaration, the second one will return a result.
And finally, if your ontology contains an instance of some class, the third pattern (corrected) will return a result. It's as simple as that.
There is no magic here: the query only returns what is present in your dataset. What is present in your dataset is determined by how you have loaded the data into your database, and (optionally) what form of reasoner you have enabled on top of your database.
I want to define multiple classes (with limited inferencing) as the range of an owl objecttypeproperty. Let me explain in detail by providing you an example.
I have two classes: Furniture and Device, which are not disjoint, i.e., another subclass/instance can inherit from both classes, e.g., Lamp can be a furniture and device.
Now I would like to define an OWL objecttypeproperty: hasComponent that can only accept range as either :Furniture or :Device, NOT both.
:hasComponent rdf:type owl:ObjectProperty ;
rdf:type owl:TransitiveProperty ;
rdfs:range :Furniture ,
:Device .
When I create an instance using the property:
:furniture1 rdf:type :furniture .
:device1 rdf:type :device .
:furtniture1 :hasComponent :lamp .
The inferencing engine will infer that :device1 is a :furniture, which I dont want, because I have already defined that device1 is a device.
One solution is to remove rdf:range and explicitly define the instance types, but I did not want to remove the range because it will limit the scope of the search space.
You have to create a union class of all the classes involved and subtract their intersection (example: ((Furniture or Device) and not (Furniture and Device))) and set that class as the range. The same approach needs to be used for domains.
You can declare this as a named class, or insert it (with the necessary RDF/XML structure around it) directly into the range axiom. I would think you'll probably need the same class in multiple places, so a named class might be the best solution.
I'd like to determine the most relevant properties / predicates (not objects) for any resource in DBPedia and Yago (e.g. the top 20). For instance, intuitively for a music artist you would be interested in his age, genre, music label, records etc.
What should a good algorithm look like to solve this problem?
My current naive approach is the following.
First I retreive all classes, ordered by their "size". (Warning, very expensive query!)
SELECT distinct ?class (count(distinct ?e) as ?c)
WHERE {
?e rdf:type ?class .
}
ORDER BY DESC(?c)
Then I make a query for each of those classes to get the number of entities within that class that have that certain property.
SELECT distinct ?prop (count(distinct ?e) as ?c)
WHERE {
?e rdf:type <--CLASS--> .
?e ?prop []
}
ORDER BY DESC(?c)
<--CLASS--> is replaced by the URI of the respective class. After some post-processing this gives me a list like this:
"dbo:Agent": {
"count": 1974654,
"properties": {
"http://www.w3.org/1999/02/22-rdf-syntax-ns#type": 399948,
"http://www.w3.org/2002/07/owl#sameAs": 67799,
"dbp:name": 22272,
"dbp:hasPhotoCollection": 13122,
"http://xmlns.com/foaf/0.1/givenName": 10799,
"dbo:birthPlace": 10055,
"dbo:birthDate": 9953,
"dbo:birthYear": 9735
}
},
"dbo:Person": {
count:
...
It tells me, which properties are most relevant for which class. Of course "meta" properties like http://www.w3.org/2002/07/owl#sameAs should be ignored in a later step.
However, entities are in multiple classes and potentially every of those is important and gives additional information. E.g. dbr:John_Lennon is (among others) in dbo:Person and dbo:MusicalArtist. I need to combine these classes' property rankings. I thought of the following approach, but I'm unsure if this is actually a reasonable solution.
So my idea was to compute relative weights for every property (e.g. propX in classA) by dividing the number of entities within classA that have propX by the total number of properties in classA. If I want to merge two classes then, e.g. classA and classB (or Person and MusicalArtist), I'd simply rank the properties of both classes in combination, ordered by their relative weights (is this a legit comparison?). If a property occurs in both classes, I'd compute the harmonic mean over both for the ranking.
Assuming the above steps would actually make sense (please let me know what you think), I got one more problem. I want to combine information from DBPedia and Yago, so for dbr:John_Lennon I want to fetch the equivalent (owl:sameAs) yr:John_Lennon from Yago. How can I merge the property ranking from both datasets to finally get a list of top 20 most relevant properties consisting of a mix of both DBP and Yago properties?
To me, the dcterms:identifier property seems like a legitimate inverse functional property. When two things share the same identifier, I think it is safe to conclude that it is the same thing.
Is there any compelling reason not to define it as such (owl:InverseFunctionalProperty) in my ontology?
If you need to stay in OWL 2 DL, then it's not a good idea to declare data properties to be inverse functional - only object properties can be declared as such without violating the constraints and end up in OWL 2 FULL.
dcterms:identifier has a range of rdfs:Literal defined here
You could use a HasKey axiom to achieve similar results: keys were introduced in OWL 2 for the purpose of identifying one or more properties whose values are identifiers for the referring individuals, and both object and data properties can be used.
According to the protege 4.x documentation the property chain exists for the object properties however in my case I need to include a data property as follow:
if builds(B, A) o has_name(A, "Holly wood") -> has_name(B, "Holly wood")
To explain a bit, imagine we have a street with a name "Holly wood". This street is built of several segments (a segment is a part of street between to junctions) whose name should be the same as street name "Holly wood".
Note that, the street concept is different from the segment so they are not subclasses but they have the above relation (builds).
One solution is to make the has_name an Object property, then each name should be an object (instance).
if is_name_of(name, A) o is_built_of(A, B) -> is_name_of(name, B)
This does not seem quite OK to me as I think it is better to use data-type.
the other solution is to use SWRL as below:
Thing(?p), Thing(?q), builds(?q, ?p), has_name(?p, ?name) -> has_name(?q, ?name)
this does not work!!!!
can you help me figure out why or find a proper solution?
I think that the SWRL rule is the proper solution here. As you've noted, you can't use the data property in a subproperty chain axiom, but you would need to in order to get the behavior you're looking for. The structural specification for object subproperty axioms and for data subproperty axioms are:
9.2.1 Object Subproperties
SubObjectPropertyOf := 'SubObjectPropertyOf' '(' axiomAnnotations subObjectPropertyExpression superObjectPropertyExpression ')'
subObjectPropertyExpression := ObjectPropertyExpression | propertyExpressionChain
propertyExpressionChain := 'ObjectPropertyChain' '(' ObjectPropertyExpression ObjectPropertyExpression { ObjectPropertyExpression } ')'
9.3.1 Data Subproperties
SubDataPropertyOf := 'SubDataPropertyOf' '(' axiomAnnotations subDataPropertyExpression superDataPropertyExpression ')'
subDataPropertyExpression := DataPropertyExpression
superDataPropertyExpression := DataPropertyExpression
OWL 2 simply doesn't have a property chain expression that mixes object and datatype properties. Thus, you'd need to use a SWRL rule. You can use a rule like this (there's no need to use Thing(?p) ∧ Thing(?q), since every individual is automatically an owl:Thing):
builds(?q, ?p) ∧ has_name(?p, ?name) → has_name(?q, ?name)