I'm having trouble expressing the following situation in my Ontology:
Suppose I have four people voting and four votes (so there is a one-to-one mapping between vote and voter). People can either vote yes or no. If I know the outcome of three people's votes, I ought to be able to deduce the fourth person's vote.
So to reiterate: John, Bob, Mary, and Carol each vote. Since there are four people there are four votes. The outcome of the vote is a tie (2 yes, and 2 no). Later on the reasoner determines that Bob and John voted no. The reasoner should then be able to deduce that Mary and Carol voted yes.
Currently I am building my ontology using Jena's java api and am inferencing with a Jena reasoner, so I'd rather use rules/semantics supported by Jena.
Antoine Zimmerman's answer is right in that this kind of thing is expressible in OWL, but that it requires a bit more work than you might expect, because you're trying to enforce a type of closed world reasoning and default reasoning, whereas OWL makes the open world assumption in which if you don't know something, it's not presumed to be either true or false, just unknown. Once the closure axioms are in place though, this isn't too hard of a scenario to write. I think it's worth working through this problem step by step to see all the axioms that need to be present in order to make this inference, and to do this in OWL DL. You didn't mention Protégé, but it's a very convenient OWL editor, and makes writing these axioms much easier. I'll show the final OWL (in RDF/XML and Turtle) at the end, too.
To represent just one instance, I'd declare a class Voters which has Alice, Bill, Cindy, and Dan as members, and then assert that those individuals are distinct:
Now, to relate each Voter to their vote, we can introduce a hasVote property with domain Voter and the enumerated range containing the strings, "yes", and "no".
We still need something that relates to the four Voters, to tie them together. Conceptually, this is the voting event in which the only participants were Alice, Bill, Cindy, and Dan, and in which each voter has only one vote. Let's introduce a class VotingEvent and an object property hasVoter which relates a VotingEvent to its participants. Now, there is some voting event, for which we'll introduce an individual votingEvent1. We'll assert that its participants are Alice, Bill, Cindy, and Dan, and only those, and that every voter in this event has exactly one vote. We also add two type constraints that say that the event has exactly 2 "yes" voters. This is sufficient, since the requirements that there are only the four enumerated voters, that they are distinct, that each has exactly one vote, and that each vote is either "yes" or "no", and the implicit knowledge that "yes" and "no" are distinct strings, is enough to ensure that the remaining two votes must be "no" votes.
Now, if we finally assert that Alice and Bill cast "yes" votes,
there is enough to infer with a reasoner that Cindy and Dan cast "no" votes, as we can see by running a DL query in Protégé:
Neither the Turtle nor RDF/XML serializations are particularly nice to look at, but I've attached them at the end, for easy copying and pasting. Now, this is a suitable representation for a single voting event, but note that we introduced a voting event in order to have something to which we added the restrictions about the four voters and the values of their votes. However, if there were multiple voting events, and some voters participated in more than one event, the simple restriction that a voter has exactly one vote is problematic because the voter might vote "yes" in one event and "no" in another. The "Voter x cast a vote of y in event z" is a tertiary relation, and really needs to be represented as such if multiple events are going to be present. The W3C Working Group Note, Defining N-ary Relations on the Semantic Web describes some approaches for this, but the basic idea is that you'd define a class whose members represent individuals cast votes, and are related to the voting event, the voter, and the vote cast. You can still write the appropriate restrictions for this kind of reasoning, though they'll be a little bit more complicated, probably involving property chains or nested class expressions.
Turtle Serialization
#prefix : <http://example.org/voters#> .
#prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
#prefix owl: <http://www.w3.org/2002/07/owl#> .
#prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
#prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
#prefix voters: <http://example.org/voters#> .
voters:Voter a owl:Class .
voters:VotingEvent a owl:Class .
voters:hasVote a owl:DatatypeProperty ;
rdfs:domain voters:Voter ;
rdfs:range [ a rdfs:Datatype ;
owl:oneOf [ a rdf:List ;
rdf:first "no" ;
rdf:rest [ a rdf:List ;
rdf:first "yes" ;
rdf:rest ()
]
]
] .
voters:Dan a owl:NamedIndividual , voters:Voter .
<http://example.org/voters>
a owl:Ontology .
voters:Alice a owl:NamedIndividual , voters:Voter ;
voters:hasVote "yes" .
voters:Bill a owl:NamedIndividual , voters:Voter ;
voters:hasVote "yes" .
voters:Cindy a owl:NamedIndividual , voters:Voter .
voters:hasVoter a owl:ObjectProperty ;
rdfs:domain voters:VotingEvent ;
rdfs:range voters:Voter .
voters:votingEvent1 a owl:NamedIndividual , voters:VotingEvent ;
a [ a owl:Restriction ;
owl:onClass [ a owl:Restriction ;
owl:hasValue "yes" ;
owl:onProperty voters:hasVote
] ;
owl:onProperty voters:hasVoter ;
owl:qualifiedCardinality "2"^^xsd:nonNegativeInteger
] ;
a [ a owl:Restriction ;
owl:allValuesFrom [ a owl:Class ;
owl:oneOf ( voters:Dan voters:Bill voters:Cindy voters:Alice )
] ;
owl:onProperty voters:hasVoter
] ;
a [ a owl:Restriction ;
owl:allValuesFrom [ a owl:Restriction ;
owl:onDataRange xsd:string ;
owl:onProperty voters:hasVote ;
owl:qualifiedCardinality "1"^^xsd:nonNegativeInteger
] ;
owl:onProperty voters:hasVoter
] ;
voters:hasVoter voters:Alice , voters:Bill , voters:Cindy , voters:Dan .
[ a owl:AllDifferent ;
owl:distinctMembers ( voters:Alice voters:Bill voters:Cindy voters:Dan )
] .
RDF/XML Serialization
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:owl="http://www.w3.org/2002/07/owl#"
xmlns:voters="http://example.org/voters#"
xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
<owl:Ontology rdf:about="http://example.org/voters"/>
<owl:Class rdf:about="http://example.org/voters#Voter"/>
<owl:Class rdf:about="http://example.org/voters#VotingEvent"/>
<owl:ObjectProperty rdf:about="http://example.org/voters#hasVoter">
<rdfs:range rdf:resource="http://example.org/voters#Voter"/>
<rdfs:domain rdf:resource="http://example.org/voters#VotingEvent"/>
</owl:ObjectProperty>
<owl:DatatypeProperty rdf:about="http://example.org/voters#hasVote">
<rdfs:domain rdf:resource="http://example.org/voters#Voter"/>
<rdfs:range>
<rdfs:Datatype>
<owl:oneOf>
<rdf:List>
<rdf:first>no</rdf:first>
<rdf:rest>
<rdf:List>
<rdf:first>yes</rdf:first>
<rdf:rest rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#nil"/>
</rdf:List>
</rdf:rest>
</rdf:List>
</owl:oneOf>
</rdfs:Datatype>
</rdfs:range>
</owl:DatatypeProperty>
<owl:AllDifferent>
<owl:distinctMembers rdf:parseType="Collection">
<owl:NamedIndividual rdf:about="http://example.org/voters#Alice">
<rdf:type rdf:resource="http://example.org/voters#Voter"/>
<voters:hasVote>yes</voters:hasVote>
</owl:NamedIndividual>
<owl:NamedIndividual rdf:about="http://example.org/voters#Bill">
<rdf:type rdf:resource="http://example.org/voters#Voter"/>
<voters:hasVote>yes</voters:hasVote>
</owl:NamedIndividual>
<owl:NamedIndividual rdf:about="http://example.org/voters#Cindy">
<rdf:type rdf:resource="http://example.org/voters#Voter"/>
</owl:NamedIndividual>
<owl:NamedIndividual rdf:about="http://example.org/voters#Dan">
<rdf:type rdf:resource="http://example.org/voters#Voter"/>
</owl:NamedIndividual>
</owl:distinctMembers>
</owl:AllDifferent>
<owl:NamedIndividual rdf:about="http://example.org/voters#votingEvent1">
<rdf:type rdf:resource="http://example.org/voters#VotingEvent"/>
<rdf:type>
<owl:Restriction>
<owl:onProperty rdf:resource="http://example.org/voters#hasVoter"/>
<owl:onClass>
<owl:Restriction>
<owl:onProperty rdf:resource="http://example.org/voters#hasVote"/>
<owl:hasValue>yes</owl:hasValue>
</owl:Restriction>
</owl:onClass>
<owl:qualifiedCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger"
>2</owl:qualifiedCardinality>
</owl:Restriction>
</rdf:type>
<rdf:type>
<owl:Restriction>
<owl:onProperty rdf:resource="http://example.org/voters#hasVoter"/>
<owl:allValuesFrom>
<owl:Class>
<owl:oneOf rdf:parseType="Collection">
<owl:NamedIndividual rdf:about="http://example.org/voters#Dan"/>
<owl:NamedIndividual rdf:about="http://example.org/voters#Bill"/>
<owl:NamedIndividual rdf:about="http://example.org/voters#Cindy"/>
<owl:NamedIndividual rdf:about="http://example.org/voters#Alice"/>
</owl:oneOf>
</owl:Class>
</owl:allValuesFrom>
</owl:Restriction>
</rdf:type>
<rdf:type>
<owl:Restriction>
<owl:onProperty rdf:resource="http://example.org/voters#hasVoter"/>
<owl:allValuesFrom>
<owl:Restriction>
<owl:onProperty rdf:resource="http://example.org/voters#hasVote"/>
<owl:qualifiedCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger"
>1</owl:qualifiedCardinality>
<owl:onDataRange rdf:resource="http://www.w3.org/2001/XMLSchema#string"/>
</owl:Restriction>
</owl:allValuesFrom>
</owl:Restriction>
</rdf:type>
<voters:hasVoter rdf:resource="http://example.org/voters#Alice"/>
<voters:hasVoter rdf:resource="http://example.org/voters#Bill"/>
<voters:hasVoter rdf:resource="http://example.org/voters#Cindy"/>
<voters:hasVoter rdf:resource="http://example.org/voters#Dan"/>
</owl:NamedIndividual>
</rdf:RDF>
You do this with an OWL ontology, but it's complicated. First, you have to model the fact that a person only has one vote. Second, you must know that John, Bob, Mary, and Carol voted. You have to know that there have been 2 "yes", and 2 "no". You have to know that Bob and John voted "no", and that they are two people, not one with a pseudonym. You have to know that Mary and Carol are different from John and Bob. And so, in Turtle:
:votes a owl:FunctionalProperty .
:John :votes "no" .
:Bob :votes "no";
owl:differentFrom :John .
:Mary a [
owl:onProperty :votes;
owl:minCardinality 1
];
owl:differentFrom :Bob, :John .
:Carol a [
owl:onProperty :votes;
owl:minCardinality 1
];
owl:differentFrom :Bob, :John .
_:someone a [ owl:onProperty :votes; owl:hasValue "no" ];
owl:differentFrom _:someoneelse .
_:someoneelse a [ owl:onProperty :votes; owl:hasValue "no" ] .
_:anotherone a [ owl:onProperty :votes; owl:hasValue "yes" ];
owl:differentFrom _:anotheroneelse .
_:anotheroneelse a [owl:onProperty :votes; owl:hasValue "yes" ] .
[ owl:onProperty :votes; owl:hasValue "no" ]
owl:oneOf ( _:someone _:someoneelse ) .
[ owl:onProperty :votes; owl:hasValue "yes" ]
owl:oneOf ( _:anotherone _:anotheroneelse ) .
The real difficulty here is to express that there are 2 "yes" and 2 "no". I've made it compact, and so it's not in OWL 2 DL, but it should be possible to make it an OWL 2 DL ontology.
Note that this does not allow modelling multiple polls or ballots. An even more complicated solution would be required (but still doable).
Related
I have an ontology where instance types are described with class restrictions. My goal is to use a reasoner to generate all other class restrictions which are valid for an instance. Consider this toy ontology example below:
#Class Hierarchy
:Highest_Class rdf:type owl:Class .
:Lower_Class rdf:type owl:Class ;
rdfs:subClassOf :Highest_Class .
:Lowest_Class rdf:type owl:Class ;
rdfs:subClassOf :Lower_Class .
#The class of named individuals
:ThingClass rdf:type owl:Class .
#Object property relationship
:related_to rdf:type owl:ObjectProperty ,
owl:TransitiveProperty .
#Instances in ontology
:thing_1 rdf:type owl:NamedIndividual ,
:ThingClass ,
[ rdf:type owl:Restriction ;
owl:onProperty :related_to ;
owl:someValuesFrom :Lowest_Class
] .
:thing_2 rdf:type owl:NamedIndividual ,
:ThingClass ,
[ rdf:type owl:Restriction ;
owl:onProperty :related_to ;
owl:someValuesFrom :Lower_Class
] .
This is what I would expect the reasoner to infer:
Since thing_1 is something that is related_to some Lowest_Class, and Lowest_Class is a subclass of Lower_Class and Highest_Class, then thing_1 is something that is related_to some Lower_Class and is related_to some Highest_Class.
Since thing_2 is something that is related_to some Lower_Class, and Lower_Class is a subclass of Highest_Class , then thing_2 is something that is "related_to some Highest_Class".
If I run a reasoner in Protege on this ontology and use the DL query tab to find individuals which are related_to some Highest_Class or related_to some Lower_Class, I see thing_1 and thing_2 listed, as expected. However, when I try to run a reasoner using the ROBOT tool or ELK command line tool, these class restriction axioms do not appear in the reasoned ontology.
Is there a way to produce such axioms into a reasoned ontology after running inference or is this not possible? Also, an explanation on how DL query is able to correctly associate individuals with the class restriction query would be very helpful.
I have a semantic network. Is it possible to use a jena framework to create a new object in the semantic web based on some rule. For example there is an object has a certain property, then you need to create a new object and make a connection between them. Is it possible?
Yes, this is possible in Jena's rule system. Typically, we create such nodes using the makeSkolem Reasoner Builtin Primitive:
[example:
(?a urn:ex:owns ?b)
makeSkolem(?ownership,?a,?b)
->
(?a urn:ex:hasOwnership ?ownership)
(?ownership urn:ex:of ?b)
]
This will create a new blank node in the graph that will be used to reify the <urn:ex:owns> triple. E.g., when given a graph containing the triple <urn:ex:a> <urn:ex:owns> <urn:ex:b> as input, the preceding rule will generate the following graph structure:
<urn:ex:a> <urn:ex:hasOwnership> [
<urn:ex:of> <urn:ex:b>
].
You can also construct URIs in your rule if you have some scheme for generating them.
Java Example
Assuming that so.rules exists on your classpath and contains the rule from above, the following java code will demonstrate custom rules for this task.
// Obtains a list of rules to pass to a rule-based reasoner
// These rules are read from a file.
// This is the most common case.
final List<Rule> rules;
try (final InputStream src = Resources.getResource("so.rules").openStream()) {
rules = Rule
.parseRules(Rule.rulesParserFromReader(new BufferedReader(new InputStreamReader(src))));
}
// Create a rule-based reasoner.
// There are multiple types of reasoners available.
// You may prefer some over others (e.g., when performing OWL inference in tandem with custom rules)
final GenericRuleReasoner reasoner =
(GenericRuleReasoner) GenericRuleReasonerFactory.theInstance().create(null);
reasoner.setRules(rules);
// Create a RDF Model to store data in.
// Create an inference model to interact with.
// The inference model will store any added data in the base model.
// The inference model will store inferred triples internally.
final Model baseModel = ModelFactory.createDefaultModel();
final InfModel model = ModelFactory.createInfModel(reasoner, baseModel);
model.prepare();
// Stimulate the rule by introducing the desired triples to the graph
// :a :owns :b
final Property owns = model.createProperty("urn:ex:", "owns");
final Property hasOwnership = model.createProperty("urn:ex:","hasOwnership");
final Property of = model.createProperty("urn:ex:","of");
final Resource a = model.createResource("urn:ex:a");
final Resource b = model.createResource("urn:ex:b");
model.add(a,owns,b);
// Verify that the rule has fired. That is, that we have created some node
// and that the node relates our other two resources
// -> :a :hasOwnership [ :of :b ]
assertTrue(a.hasProperty(hasOwnership));
final Resource createdObject = a.getPropertyResourceValue(hasOwnership);
assertTrue(createdObject.hasProperty(of,b));
If you needs are reasonably simple you can use SPARQL CONSTRUCT queries, i.e.
CONSTRUCT { ?p :hasGrandfather ?g . }
WHERE {
?p :hasParent ?parent .
?parent :hasParent ?g .
?g :gender :male .
}
will cause triples to be generated for stating grandfather relations.
If your needs are more sophisticated, you can achieve this with SHACL for which an implementation on top of Jena exists. I will give a brief example. Assume you have the following RDF data:
#prefix ex: <http://example.com/ns#> .
#prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
#prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
ex:InvalidRectangle
a ex:Rectangle .
ex:NonSquareRectangle
a ex:Rectangle ;
ex:height 2 ;
ex:width 3 .
ex:SquareRectangle
a ex:Rectangle ;
ex:height 4 ;
ex:width 4 .
for which you define the following shape file:
#prefix ex: <http://example.com/ns#> .
#prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
#prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
#prefix dash: <http://datashapes.org/dash#> .
#prefix sh: <http://www.w3.org/ns/shacl#> .
#prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
ex:Rectangle
a rdfs:Class, sh:NodeShape ;
rdfs:label "Rectangle" ;
sh:property [
sh:path ex:height ;
sh:datatype xsd:integer ;
sh:maxCount 1 ;
sh:minCount 1 ;
sh:name "height" ;
] ;
sh:property [
sh:path ex:width ;
sh:datatype xsd:integer ;
sh:maxCount 1 ;
sh:minCount 1 ;
sh:name "width" ;
] ;
sh:rule [
a sh:TripleRule ;
sh:subject sh:this ;
sh:predicate rdf:type ;
sh:object ex:Square ;
sh:condition ex:Rectangle ;
sh:condition [
sh:property [
sh:path ex:width ;
sh:equals ex:height ;
] ;
] ;
] .
It will generate the following RDF data:
#prefix owl: <http://www.w3.org/2002/07/owl#> .
#prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
#prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
#prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
<http://example.com/ns#SquareRectangle>
a <http://example.com/ns#Square> .
which you can add to your RDF store.
This example with code can be found here, as well as a more advanced example.
I'm serving a dataset containing 10-20 named graphs from a TDB dataset in Fuseki 2.
I'd like to use a reasoner to do inference on my data. The behaviour I'd like to see is that triples inferred within each graph should appear within those graphs (although it would be fine if the triples appear in the default graph too).
Is there a simple way of configuring this? I haven't found any configuration examples that match what I am trying to do.
The configuration I've tried is very similar to the following standard example.
DatasetTDB -> GraphTDB -> InfModel -> RDFDataset
The final view of the data I see is only a very tiny subset of the data (it appears that all the named graphs are dropped somewhere along this pipeline, and only the tiny default graph is left).
Using tdb:unionDefaultGraph seems to have no effect on this.
prefix : <#> .
#prefix fuseki: <http://jena.apache.org/fuseki#> .
#prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
#prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
#prefix tdb: <http://jena.hpl.hp.com/2008/tdb#> .
#prefix ja: <http://jena.hpl.hp.com/2005/11/Assembler#> .
# Example of a data service with SPARQL query and update on an
# inference model. Data is taken from TDB.
## ---------------------------------------------------------------
## Service with only SPARQL query on an inference model.
## Inference model base data is in TDB.
<#service2> rdf:type fuseki:Service ;
fuseki:name "inf" ; # http://host/inf
fuseki:serviceQuery "sparql" ; # SPARQL query service
fuseki:serviceUpdate "update" ;
fuseki:dataset <#dataset> ;
.
<#dataset> rdf:type ja:RDFDataset ;
ja:defaultGraph <#model_inf> ;
.
<#model_inf> a ja:InfModel ;
ja:baseModel <#tdbGraph> ;
ja:reasoner [
ja:reasonerURL <http://jena.hpl.hp.com/2003/OWLFBRuleReasoner>
] .
## Base data in TDB.
<#tdbDataset> rdf:type tdb:DatasetTDB ;
tdb:location "DB" ;
# If the unionDefaultGraph is used, then the "update" service should be removed.
# tdb:unionDefaultGraph true ;
.
<#tdbGraph> rdf:type tdb:GraphTDB ;
tdb:dataset <#tdbDataset> .
</code>
Does anyone have any thoughts on this?
Also, bonus points if there's a way to make the dataset writable. (On some level, what I'm trying to do is approach the default behaviour of Owlim/GraphDB, which keeps persistent named graphs, does inferencing, and also allows for updates.)
Thanks in advance.
I'm facing (or faced) the same problems on my code, but I have a partial solution. Unfortunately the link provided in the comments did not really help the issues I'm still facing, but this answers part of the problem.
The final view of the data I see is only a very tiny subset of the
data (it appears that all the named graphs are dropped somewhere along
this pipeline, and only the tiny default graph is left). Using
tdb:unionDefaultGraph seems to have no effect on this.
The workaround I found for this is to explicitly 'register' your named graphs in the configuration file. I don't really know if it is the best way (and did not found any documentation or example for this exact context). A working example on my setup (Fuseki 2.4):
[usual configuration start]
# TDB Dataset
:tdb_dataset_readwrite
a tdb:DatasetTDB ;
tdb:unionDefaultGraph true ;
#if you want all data to available in the default graph
#without 'FROM-NAMing them' in the SPARQL query
tdb:location "your/dataset/path" .
# Underlying RDF Dataset
<#dataset>
rdf:type ja:RDFDataset ;
ja:defaultGraph <#model> ;
ja:namedGraph [
ja:graphName <your/graph/URI> ;
ja:graph <#graphVar>
] ;
[repeat for other named graphs]
.
######
# Default Model : Inference rules (OWL, here)
<#model> a ja:InfModel;
ja:baseModel <#tdbGraph>;
ja:reasoner
[ ja:reasonerURL
<http://jena.hpl.hp.com/2003/OWLFBRuleReasoner>
]
.
# Graph for the default Model
<#tdbGraph> rdf:type tdb:GraphTDB;
tdb:dataset :tdb_dataset_readwrite .
######
# Named Graph
<#graphVar> rdf:type tdb:GraphTDB ;
tdb:dataset :tdb_dataset_readwrite ;
tdb:graphName <your/graph/URI>
.
Then, you can run a query like this one
[prefixes]
SELECT ?graph ?predicate ?object
WHERE {
GRAPH ?graph {[a specific entity identifier] ?predicate ?object}
}
LIMIT 50
And it will display (in this case) properties and values, and the source graph where they were found.
BUT: in this example, even if the default graph supposedly imported inference rules (that should be applied globally, especially since the unionDefaultGraph parameter is enabled), they are not applied in a "cross-graph" manner, and that is the problem I am still facing.
Normally, if you add the inference engine to every graph, this should work, according to Andy Seaborne's post here, but it doesn't work in my case.
Hope this helps nevertheless.
I've come across this issue many times myself but I've actually never seen a solution. However, I managed to figure it out after having read this in the documentation about "special graph names" in TDB datasets. From what I understand, setting the union default graph for a TDB dataset in the assembler file only changes what is returned when that particular dataset is queried. However, there is a special graph name that can be used to reference the union graph: <urn:x-arq:UnionGraph>. So, simply create GraphTDB, reference the TDB dataset and point it to this special graph.
The config file below does what is requested in the question: reasoning is performed over the default union graph, and the result is exposed in the TDB dataset as writable service. (Note that the reasoning service will not see any changes in the dataset until it is reloaded, since reasoning is all done in memory).
#prefix : <http://base/#> .
#prefix tdb: <http://jena.hpl.hp.com/2008/tdb#> .
#prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
#prefix ja: <http://jena.hpl.hp.com/2005/11/Assembler#> .
#prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
#prefix fuseki: <http://jena.apache.org/fuseki#> .
# TDB
tdb:DatasetTDB rdfs:subClassOf ja:RDFDataset .
tdb:GraphTDB rdfs:subClassOf ja:Model .
# Service 1: Dataset endpoint (no reasoning)
:dataService a fuseki:Service ;
fuseki:name "tdbEnpoint" ;
fuseki:serviceQuery "sparql", "query" ;
fuseki:serviceUpdate "update" ;
fuseki:dataset :tdbDataset ;
.
# Service 2: Reasoning endpoint
:reasoningService a fuseki:Service ;
fuseki:dataset :infDataset ;
fuseki:name "reasoningEndpoint" ;
fuseki:serviceQuery "query", "sparql" ;
fuseki:serviceReadGraphStore "get" ;
.
# Inference dataset
:infDataset rdf:type ja:RDFDataset ;
ja:defaultGraph :infModel ;
.
# Inference model
:infModel a ja:InfModel ;
ja:baseModel :g ;
ja:reasoner [
ja:reasonerURL <http://jena.hpl.hp.com/2003/OWLFBRuleReasoner> ;
] ;
.
# Intermediate graph referencing the default union graph
:g rdf:type tdb:GraphTDB ;
tdb:dataset :tdbDataset ;
tdb:graphName <urn:x-arq:UnionGraph> ;
.
# The location of the TDB dataset
:tdbDataset rdf:type tdb:DatasetTDB ;
tdb:location "/fuseki/databases/db" ;
tdb:unionDefaultGraph true ;
.
I have an ontology that contains plants and diseases and a property treat (a plant treats diseases). I have a lot of plants and diseases, but now I want to add a disease that is treatable by a combination of two or more plants. For instance, how can I represent the following sentence?
Disease X is treatable by the combination of plant A and plant B, but not by plant A or plant B alone.
I've been thinking to obtain this using a reasoner, but I have no idea how.
It sounds like you have, at the moment, an ontology with a classes Disease and Plant, and a property treats with domain Plant and range Disease. As I understand it, the problem is that what should treat some Diseases are not individual Plants, but rather combinations of them. In these cases, we might say that a plant is used in the treatment of a disease, but does not, itself, treat the disease. It is probably reasonable to say, too, that if a plant, by itself, treats a disease, then it also is used in the treatment of a disease.
So, you have a class of individuals that you haven't considered before, that is combinations of plants, so why not introduce a class PlantCombination and a property hasComponent that relates PlantCombinations to the plants in the combination? You can also add a restriction that says that each plant combination has at least two plants, if you like, by saying PlantCombination SubClassOf hasComponent min 2 Plant. Since both Plants and PlantCombinations can treat Diseases, you'll want to change the domain of treats to be Plant or PlantCombination. To ensure that a reasoner can infer that if plant82 treats disease92 then plant82 isUsedToTreat disease92, you can assert that treats SubPropertyOf isUsedToTreat. (This will also mean that a plant combination that treats a disease is also used to treat that disease.) To ensure that when a combination with a component plant23 treats a disease, that plant23 is used to treat the disease, you can add the assertion that inverse(hasComponent) o treats SubPropertyOf isUsedToTreat. Here's an ontology that does just that:
N3 Format
#prefix : <http://www.example.org/plantsAndDiseases#> .
#prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
#prefix owl: <http://www.w3.org/2002/07/owl#> .
#prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
#prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
<http://www.example.org/plantsAndDiseases>
a owl:Ontology .
:Plant
a owl:Class .
:Disease
a owl:Class .
:PlantCombination
a owl:Class ;
rdfs:subClassOf
[ a owl:Restriction ;
owl:minQualifiedCardinality
"2"^^xsd:nonNegativeInteger ;
owl:onClass :Plant ;
owl:onProperty :hasComponent
] .
:hasComponent
a owl:ObjectProperty ;
rdfs:domain :PlantCombination ;
rdfs:range :Plant .
:isUsedToTreat
a owl:ObjectProperty ;
rdfs:comment "X isUsedToTreat Y means that X is used in the treatment of Y. X may either treat Y, or may be a component of a combination that treats Y." ;
rdfs:domain
[ a owl:Class ;
owl:unionOf (:Plant :PlantCombination)
] ;
rdfs:range :Disease ;
owl:propertyChainAxiom
([ owl:inverseOf :hasComponent
] :treats) .
:treats
a owl:ObjectProperty ;
rdfs:comment "X treats Y means that X is a sufficient treatment for Y." ;
rdfs:domain
[ a owl:Class ;
owl:unionOf (:Plant :PlantCombination)
] ;
rdfs:range :Disease ;
rdfs:subPropertyOf :isUsedToTreat .
OWL Functional Syntax
Prefix(xsd:=<http://www.w3.org/2001/XMLSchema#>)
Prefix(owl:=<http://www.w3.org/2002/07/owl#>)
Prefix(xml:=<http://www.w3.org/XML/1998/namespace>)
Prefix(rdf:=<http://www.w3.org/1999/02/22-rdf-syntax-ns#>)
Prefix(rdfs:=<http://www.w3.org/2000/01/rdf-schema#>)
Ontology(<http://www.example.org/plantsAndDiseases>
Declaration(Class(<http://www.example.org/plantsAndDiseases#Disease>))
Declaration(Class(<http://www.example.org/plantsAndDiseases#Plant>))
Declaration(Class(<http://www.example.org/plantsAndDiseases#PlantCombination>))
SubClassOf(<http://www.example.org/plantsAndDiseases#PlantCombination> ObjectMinCardinality(2 <http://www.example.org/plantsAndDiseases#hasComponent> <http://www.example.org/plantsAndDiseases#Plant>))
Declaration(ObjectProperty(<http://www.example.org/plantsAndDiseases#hasComponent>))
ObjectPropertyDomain(<http://www.example.org/plantsAndDiseases#hasComponent> <http://www.example.org/plantsAndDiseases#PlantCombination>)
ObjectPropertyRange(<http://www.example.org/plantsAndDiseases#hasComponent> <http://www.example.org/plantsAndDiseases#Plant>)
Declaration(ObjectProperty(<http://www.example.org/plantsAndDiseases#isUsedToTreat>))
AnnotationAssertion(rdfs:comment <http://www.example.org/plantsAndDiseases#isUsedToTreat> "X isUsedToTreat Y means that X is used in the treatment of Y. X may either treat Y, or may be a component of a combination that treats Y.")
ObjectPropertyDomain(<http://www.example.org/plantsAndDiseases#isUsedToTreat> ObjectUnionOf(<http://www.example.org/plantsAndDiseases#PlantCombination> <http://www.example.org/plantsAndDiseases#Plant>))
ObjectPropertyRange(<http://www.example.org/plantsAndDiseases#isUsedToTreat> <http://www.example.org/plantsAndDiseases#Disease>)
Declaration(ObjectProperty(<http://www.example.org/plantsAndDiseases#treats>))
AnnotationAssertion(rdfs:comment <http://www.example.org/plantsAndDiseases#treats> "X treats Y means that X is a sufficient treatment for Y.")
SubObjectPropertyOf(<http://www.example.org/plantsAndDiseases#treats> <http://www.example.org/plantsAndDiseases#isUsedToTreat>)
ObjectPropertyDomain(<http://www.example.org/plantsAndDiseases#treats> ObjectUnionOf(<http://www.example.org/plantsAndDiseases#PlantCombination> <http://www.example.org/plantsAndDiseases#Plant>))
ObjectPropertyRange(<http://www.example.org/plantsAndDiseases#treats> <http://www.example.org/plantsAndDiseases#Disease>)
SubObjectPropertyOf(ObjectPropertyChain(ObjectInverseOf(<http://www.example.org/plantsAndDiseases#hasComponent>) <http://www.example.org/plantsAndDiseases#treats>) <http://www.example.org/plantsAndDiseases#isUsedToTreat>)
)
Alternative to Joshua's answer: You can represent diseases and plants as OWL classes, as here you refer to sets of plants (not particular instances, which you would find in the nature). You can then link classes with existential restrictions (some - common pattern in biology).
You need also to introduce a supplementary step in your modelling, as mentioned: Plants can be for example ingredients of treatments, diseases being treatable by treatments.
Then if you consider the following commented (#) ontology (Manchester syntax), I describe the axioms for the modelling. You can save the file and open it with Protege.
Prefix: xsd: <http://www.w3.org/2001/XMLSchema#>
Prefix: owl: <http://www.w3.org/2002/07/owl#>
Prefix: : <http://www.example.org/demo.owl#>
Prefix: xml: <http://www.w3.org/XML/1998/namespace>
Prefix: rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
Prefix: rdfs: <http://www.w3.org/2000/01/rdf-schema#>
Ontology: <http://www.example.org/demo.owl>
ObjectProperty: has-ingredient
ObjectProperty: treatableBy
Class: owl:Thing
Class: PlantA
SubClassOf:
Plant
Class: Treatment
#Your treatment obtained by mixing some
#of the plant B and some of the plant A
Class: TreatmentAB
SubClassOf:
Treatment,
(has-ingredient some PlantA)
and (has-ingredient some PlantB)
Class: PlantB
SubClassOf:
Plant
#This treatment has ingredient the plant A
Class: TreatmentA
SubClassOf:
has-ingredient some PlantA,
Treatment
#This treatment is made from plant B (among other things)
Class: TreatmentB
SubClassOf:
Treatment,
has-ingredient some PlantB
Class: Disease
Class: Plant
# This disease is treatable by the TreatmentAB
Class: DiseaseA
SubClassOf:
treatableBy some TreatmentAB,
Disease
Class: DiseaseB
SubClassOf:
treatableBy some TreatmentB,
Disease
Now if we were reasoning over the ontology and ask for the subclasses of treatableBy some TreatmentA we wouldn't get any class. The expression treatableBy some TreatmentAB returns DiseaseA as expected.
I am currently messing around with Jena trying to create rules to add to my generic reasoner. I created the following simple rule:
[rule1: (?x ?rdf:type ?y),(?y rdfs:subClassOf ?z)-> (?x rdfs:type ?z)]
This works fine, and fires correctly, giving me everything I expect. After I did this I wanted to assign a timestamp to ?x, so that I'd know when the conclusion was come to, I wanted to used the Now builtin as follows.
[rule1: (?x ?rdf:type ?y),(?y rdfs:subClassOf ?z),now(?x)-> (?x rdfs:type ?z)]
This unfortunately does not work, it does nor throw an error, it just does not seem to bind any time value to ?x and also it seems to prevent the rule from being fired correctly as in the first case. The ontology I am using is below. Any ideas?
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xmlns:owl="http://www.w3.org/2002/07/owl#">
<owl:Class rdf:about="livingthing"></owl:Class>
<owl:Class rdf:about="Human">
<rdfs:subClassOf rdf:resource="livingthing"/>
</owl:Class>
<rdf:Description rdf:about="john">
<rdf:type rdf:resource="Human"/>
</rdf:Description>
</rdf:RDF>
According to the javadoc, Now will “Bind the first arg to the current date time in the current locale and timezone.” I haven't tried using Now, but I'd expect that "the current date time in the current locale and timezone" is a some sort of RDF literal (perhaps a date or datetime)? Literals cannot be the subjects of triples in RDF. For the rule
[rule1: (?x ?rdf:type ?y),(?y rdfs:subClassOf ?z),now(?x) -> (?x rdfs:type ?z)]
to match in the graph, the variable ?x will be bound to the date literal, say, "2013-05-24" (but as I mentioned, I don't know exactly what Now will produce) but there cannot be any matches for (?x rdf:type ?y) because these would have the form "2013-05-24" rdf:type ?y and there cannot be triples of that form. This means that the rule cannot fire.
If you want to add a timestamp to something, you probably want an n-ary relation, so that you're actually representing (or representing, in addition to ?x rdf:type ?z) a relation inferredSubclassRule(?super,?sub,?time ), which can be represented in RDF by a blank node like:
[] a :inferredSubclassRule ;
:hasSuperClass ?super ;
:hasSubClass ?sub ;
:hasTimestamp ?time .