I'm trying to make OWL inferences with Jena. As a start, my goal is merely to infer that if an educational institution is of type dbo:EducationalInstitution, then it is also dbo:institution
Here's the java code (adapted from the jena doc) :
package test;
import org.apache.jena.util.FileManager;
import org.apache.jena.util.PrintUtil;
import org.apache.jena.rdf.model.*;
import org.apache.jena.reasoner.Reasoner;
import org.apache.jena.reasoner.ReasonerRegistry;
public class Test {
public static void main(String[] args){
Model schema = FileManager.get().loadModel("file:/home/mica/Downloads/sparql/ontology.ttl");
Model data = FileManager.get().loadModel("file:/home/mica/Downloads/sparql/result.ttl");
Reasoner reasoner = ReasonerRegistry.getOWLReasoner();
reasoner = reasoner.bindSchema(schema);
InfModel infmodel = ModelFactory.createInfModel(reasoner, data);
Resource nForce = infmodel.getResource("<0762762P>");
System.out.println(nForce);
System.out.println("nForce *:");
printStatements(infmodel, nForce, null, null);
}
public static void printStatements(Model m, Resource s, Property p, Resource o) {
for (StmtIterator i = m.listStatements(s,p,o); i.hasNext(); ) {
Statement stmt = i.nextStatement();
System.out.println(" - " + PrintUtil.print(stmt));
}
}
}
An example of my data :
#prefix dbo: <http://dbpedia.org/ontology/> .
#prefix ex: <http://ex.org/a#> .
#prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
#prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
#prefix dbf: <http://fr.dbpedia.org/resource/> .
#prefix foaf: <http://xmlns.com/foaf/0.1/> .
<0762762P> rdf:type dbo:Event ;
dbo:status "En cours" ;
dbo:Place _:b0 .
_:b0 dbf:Ville "Rouen" ;
rdf:type dbo:EducationalInstitution ;
foaf:name "Université du Havre" .
And the following ontology:
#prefix dbo: <http://dbpedia.org/ontology/> .
#prefix ex: <http://ex.org/a#> .
#prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
#prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#'> .
#prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
#prefix dbf: <http://fr.dbpedia.org/resource/> .
#prefix foaf: <http://xmlns.com/foaf/0.1/> .
#prefix owl: <http://www.w3.org/2002/07/owl#> .
dbo:Event rdf:type owl:Class .
dbo:Place rdf:type owl:ObjectProperty ;
rdfs:domain dbo:Event ;
rdfs:range dbo:Event .
dbo:institution rdf:type owl:Class .
dbo:EducationalInstitution rdf:type owl:Class ;
rdfs:subClassOf dbo:institution .
dbo:Place rdf:type dbo:EducationalInstitution .
I'm not getting the expected results:
nForce *:
Your whole ontology is strange:
You're reusing the DBpedia ontology in which dbo:Place is already defined as a class, but you're using it as an object property.
Next, you declared the range of this object property to be an dbo:Event. Semantically, this also sound strange.
You have a triple which assigns dbo:Place to be an instance of class
dbo:EducationalInstitution. That means, in the end dbo:Place is a class, a property and an individual. This is really bad modelling.
Other conventions:
reusing ontologies is good, but you should not reuse the namespace but use your own namespace
naming convention:
for classes CamelCase style
for properties lowerCamelCase style
Why don't you use a more appropriate modelling like this:
#prefix dbo: <http://dbpedia.org/ontology/> .
#prefix ex: <http://ex.org/a#> .
#prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
#prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#'> .
#prefix owl: <http://www.w3.org/2002/07/owl#> .
dbo:Event rdf:type owl:Class .
ex:takesPlaceAt rdf:type owl:ObjectProperty ;
rdfs:domain dbo:Event ;
rdfs:range dbo:Place .
ex:Institution rdf:type owl:Class .
ex:EducationalInstitution rdf:type owl:Class ;
rdfs:subClassOf ex:Institution .
dbo:EducationalInstitution rdfs:subClassOf dbo:Place .
Regarding the data:
_:b0 dbf:Ville "Rouen" ;
rdf:type ex:EducationalInstitution ;
foaf:name "Université du Havre" .
DBpedia ontology has propertiesThat's for sure wrong.
dbf:Ville is not a property but an individual. Why do you use it as a property? The Dbpedia ontology has a property dbo:city. And the resource for Rouen exists already.
Maybe you could do it like this:
#prefix dbo: <http://dbpedia.org/ontology/> .
#prefix ex: <http://ex.org/a#> .
#prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
#prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
#prefix dbr: <http://dbpedia.org/resource/> .
#prefix foaf: <http://xmlns.com/foaf/0.1/> .
<0762762P> rdf:type dbo:Event ;
dbo:status "En cours" ;
dbo:Place _:b0 .
_:b0 dbo:city http://dbpedia.org/resource/Rouen ;
rdf:type ex:EducationalInstitution ;
foaf:name "Université du Havre" .
Related
I have the following RDF data in my Fuseki triplestore.
#prefix owl: <http://www.w3.org/2002/07/owl#> .
#prefix schema: <http://schema.org/> .
#prefix ex: <http://localhost:3030/eb/> .
#prefix wgs84: <http://www.w3.org/2003/01/geo/wgs84_pos#> .
#prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
ex:School rdf:type owl:Class .
<http://localhost:3030/eb/School/1> rdf:type ex:School ;
schema:name "Escola 1" .
ex:NewSchool rdf:type owl:Class .
<http://localhost:3030/eb/NewSchool/1> rdf:type ex:NewSchool ;
wgs84:lat "23.085980" ;
wgs84:long "-5.692" .
<http://localhost:3030/eb/School/1> owl:sameAs <http://localhost:3030/eb/NewSchool/1> .
I query like this:
SELECT ?predicate ?object
WHERE {
<http://localhost:3030/eb/School/1> ?predicate ?object
}
with the following result:
predicate object
<http://www.w3.org/2002/07/owl#sameAs> <http://localhost:3030/eb/NewSchool/1>
<http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://localhost:3030/eb/Escola>
<http://schema.org/name> "Escola 1"
I would like to know what should I do to make the query return the wgs84:lat / wgs84:long values from the owl:sameAs instance? Is it possible using a SPARQL query?
What it is needed here is to edit the configuration files (inside the folder /run/configuration/datasetname.ttl), add and restart the Fuseki server.
:service1 a fuseki:Service ;
fuseki:dataset :inferred_dataset ;
:inferred_dataset a ja:RDFDataset ;
ja:defaultGraph :inference_model .
:inference_model a ja:InfModel ;
ja:baseModel :tdb_graph ;
ja:reasoner [
ja:reasonerURL <http://jena.hpl.hp.com/2003/OWLFBRuleReasoner>
] .
:tdb_graph a tdb:GraphTDB ;
tdb:dataset :tdb_dataset_readwrite .
:tdb_dataset_readwrite
a tdb:DatasetTDB ;
tdb:location "[MyDatasetLocationOnDisk]" .
Some links on how to do that:
https://christinemdraper.wordpress.com/2017/04/09/getting-started-with-rdf-sparql-jena-fuseki/
https://github.com/jfmunozf/Jena-Fuseki-Reasoner-Inference/wiki/Configuring-Apache-Jena-Fuseki-2.4.1-inference-and-reasoning-support-using-SPARQL-1.1:-Jena-inference-rules,-RDFS-Entailment-Regimes-and-OWL-reasoning
https://gist.github.com/ruebot/fb7b1da82042860138d2d609756e07dc
configure fuseki with TDB2 and OWL Reasoner
Then it behaves just like intended in the question.
Just to remember, if one wants to link to a third party's vocabulary, one must download the file and load it into Fuseki, to make the infereces work.
May I know if Apahe JENA supports OWL 2 syntax in Java? It does mentioned that in the documentation (https://jena.apache.org/documentation/ontology/) it only provide limited cardinality restrictions. I would like to confirm this from the experts.
Apache Jena does not support OWL2, only OWL11 through org.apache.jena.ontology.OntModel interface. See also documentation.
But you still can work with OWL2 in Jena using some external jena-based APIs and tools, e.g. ONT-API, that is OWL-API-api(v5) impl over Jena.
In ONT-API there are two main OWL2 view of data, which encapsulate the same RDF Graph: com.github.owlcs.ontapi.jena.model.OntModel and com.github.owlcs.ontapi.Ontology (in older versions (ONT-API:v1.x.x) these classes have names ru.avicomp.ontapi.jena.model.OntGraphModel and ru.avicomp.ontapi.OntologyModel respectively).
The com.github.owlcs.ontapi.jena.model.OntModel view is a full analogue of Jena org.apache.jena.ontology.OntModel, it is the facility to work with triples.
And the com.github.owlcs.ontapi.Ontology view is an extended org.semanticweb.owlapi.model.OWLOntology, the facility to work with axiomatic data, that is backed by the com.github.owlcs.ontapi.jena.model.OntModel view and vice-versa.
For example, the following snippet:
String uri = "https://stackoverflow.com/questions/54049750";
String ns = uri + "#";
OntModel m = OntModelFactory.createModel()
.setNsPrefixes(OntModelFactory.STANDARD).setNsPrefix("q", ns);
m.setID(uri);
OntClass c = m.createOntClass(ns + "c");
OntObjectProperty p = m.createObjectProperty(ns + "p");
OntIndividual i = c.createIndividual(ns + "i");
m.createObjectComplementOf(m.createObjectUnionOf(c, m.getOWLThing(),
m.createObjectSomeValuesFrom(p, m.createObjectOneOf(i))));
m.write(System.out, "ttl");
will produce the following ontology:
#prefix q: <https://stackoverflow.com/questions/54049750#> .
#prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
#prefix owl: <http://www.w3.org/2002/07/owl#> .
#prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
#prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
<https://stackoverflow.com/questions/54049750>
a owl:Ontology .
q:c a owl:Class .
q:p a owl:ObjectProperty .
q:i a owl:NamedIndividual , q:c .
[ a owl:Class ;
owl:complementOf [ a owl:Class ;
owl:unionOf ( q:c owl:Thing
[ a owl:Restriction ;
owl:onProperty q:p ;
owl:someValuesFrom [ a owl:Class ;
owl:oneOf ( q:i )
]
]
)
]
] .
I'm creating an ontology using Apache Jena. However, I can't find a way of creating custom datatypes as in the following example:
'has value' some xsd:float[>= 0.0f , <= 15.0f].
Do you have any ideas?
It seems what you need is DatatypeRestriction with two facet restrictions: xsd:minInclusive and xsd:maxInclusive.
It is OWL2 constructions.
org.apache.jena.ontology.OntModel does not support OWL2, only OWL1.1 partially (see documentation), and, therefore, there are no builtin methods for creating such data-ranges (there is only DataOneOf data range expression, see OntModel#createDataRange(RDFList)).
So you have to create a desired datatype manually, triple by triple, using the general org.apache.jena.rdf.model.Model interface.
In RDF, it would look like this:
_:x rdf:type rdfs:Datatype.
_:x owl:onDatatype DN.
_:x owl:withRestrictions (_:x1 ... _:xn).
See also owl2-quick-guide.
Or, to build such an ontology, you can use some external utilities or APIs.
For example, in ONT-API (v. 2.x.x) the following snippet
String ns = "https://stackoverflow.com/questions/54131709#";
OntModel m = OntModelFactory.createModel()
.setNsPrefixes(OntModelFactory.STANDARD).setNsPrefix("q", ns);
OntDataRange.Named floatDT = m.getDatatype(XSD.xfloat);
OntFacetRestriction min = m.createFacetRestriction(OntFacetRestriction.MinInclusive.class,
floatDT.createLiteral("0.0"));
OntFacetRestriction max = m.createFacetRestriction(OntFacetRestriction.MaxInclusive.class,
floatDT.createLiteral("15.0"));
OntDataRange.Named myDT = m.createDatatype(ns + "MyDatatype");
myDT.addEquivalentClass(m.createDataRestriction(floatDT, min, max));
m.createResource().addProperty(m.createDataProperty(ns + "someProperty"),
myDT.createLiteral("2.2"));
m.write(System.out, "ttl");
will produce the following ontology:
#prefix q: <https://stackoverflow.com/questions/54131709#> .
#prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
#prefix owl: <http://www.w3.org/2002/07/owl#> .
#prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
#prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
[ q:someProperty "2.2"^^q:MyDatatype ] .
q:MyDatatype a rdfs:Datatype ;
owl:equivalentClass [ a rdfs:Datatype ;
owl:onDatatype xsd:float ;
owl:withRestrictions ( [ xsd:minInclusive "0.0"^^xsd:float ]
[ xsd:maxInclusive "15.0"^^xsd:float ]
)
] .
q:someProperty a owl:DatatypeProperty .
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.
How to load TDB storage with inference via tdbloader.bat (windows, Jena 2.7.3)?
I used this assembler file:
#prefix tdb: <http://jena.hpl.hp.com/2008/tdb#> .
#prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
#prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
#prefix ja: <http://jena.hpl.hp.com/2005/11/Assembler#> .
#prefix tdb: <http://jena.hpl.hp.com/2008/tdb#> .
[] ja:loadClass "com.hp.hpl.jena.tdb.TDB" .
tdb:DatasetTDB rdfs:subClassOf ja:RDFDataset .
tdb:GraphTDB rdfs:subClassOf ja:Model .
<#dataset> rdf:type ja:RDFDataset ;
ja:defaultGraph <#infModel> .
<#infModel> a ja:InfModel ;
ja:baseModel <#tdbGraph>;
ja:reasoner
[ ja:reasonerURL <http://jena.hpl.hp.com/2003/OWLFBRuleReasoner> ].
<#tdbGraph> rdf:type tdb:GraphTDB ;
tdb:location "DB";
.
My command:
c:\apache-jena-2.7.3\bat>tdbloader --tdb=test.ttl C:\apache-jena-2.7.3\Lubm10\*
I got an exception:
java.lang.ClassCastException: com.hp.hpl.jena.reasoner.rulesys.FBRuleInfGraph cannot be cast to com.hp.hpl.jena.tdb.store.GraphTDB
What is wrong?
(removing semicolon after "DB" - does not help)
It's not clear what you are trying to achieve. tdbloader is a tool for loading triples into a TDB store, prior to processing those triples via your app or SPARQL end-point. Separately, from your app code, you can construct a Jena model which uses the inference engine over a base model from a TDB graph. But I can't see why you are using an inference model at load time. If you look at the exception you are getting:
FBRuleInfGraph cannot be cast to com.hp.hpl.jena.tdb.store.GraphTDB
it confirms that you can't use an inference graph at that stage of the process, and I'm not sure why you would. Unless, of course, you are trying to statically compute the inference closure over the base model and store that in TDB, saving the need for inference computation at runtime. However, if you are trying to do that, I don't believe that can currently be done via the Jena assembler. You'll have to write custom code to do that at the moment.
Bottom line: separate the concerns. Use a plain graph description for tdbloader, use the inference graph at run time.