001/** 002 * Copyright (C) 2007 - 2016, Jens Lehmann 003 * 004 * This file is part of DL-Learner. 005 * 006 * DL-Learner is free software; you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation; either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * DL-Learner is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with this program. If not, see <http://www.gnu.org/licenses/>. 018 */ 019package org.dllearner.algorithms.properties; 020 021import org.apache.jena.query.*; 022import org.dllearner.core.ComponentAnn; 023import org.dllearner.core.ConsoleAxiomLearningProgressMonitor; 024import org.dllearner.core.EvaluatedAxiom; 025import org.dllearner.core.config.ConfigOption; 026import org.dllearner.kb.SparqlEndpointKS; 027import org.dllearner.kb.sparql.SparqlEndpoint; 028import org.dllearner.learningproblems.AxiomScore; 029import org.dllearner.learningproblems.Heuristics; 030import org.semanticweb.owlapi.dlsyntax.renderer.DLSyntaxObjectRenderer; 031import org.semanticweb.owlapi.io.ToStringRenderer; 032import org.semanticweb.owlapi.model.*; 033import uk.ac.manchester.cs.owl.owlapi.OWLObjectPropertyImpl; 034 035import java.util.Set; 036import java.util.TreeSet; 037 038@ComponentAnn(name="object property range learner", shortName="oplrange", version=0.1, description="A learning algorithm for object property range axioms.") 039public class ObjectPropertyRangeAxiomLearner extends ObjectPropertyAxiomLearner<OWLObjectPropertyRangeAxiom> { 040 041 private static final ParameterizedSparqlString DISTINCT_OBJECTS_COUNT_QUERY = new ParameterizedSparqlString( 042 "SELECT (COUNT(DISTINCT(?o)) as ?cnt) WHERE {?s ?p ?o .}"); 043 044 private static final ParameterizedSparqlString OBJECTS_OF_TYPE_COUNT_QUERY = new ParameterizedSparqlString( 045 "SELECT (COUNT(DISTINCT(?o)) AS ?cnt) WHERE {?s ?p ?o . ?o a ?type .}"); 046 private static final ParameterizedSparqlString OBJECTS_OF_TYPE_WITH_INFERENCE_COUNT_QUERY = new ParameterizedSparqlString( 047 "SELECT (COUNT(DISTINCT(?o)) AS ?cnt) WHERE {?s ?p ?o . ?o rdf:type/rdfs:subClassOf* ?type .}"); 048 049 private static final ParameterizedSparqlString OBJECTS_OF_TYPE_COUNT_BATCHED_QUERY = new ParameterizedSparqlString( 050 "PREFIX owl:<http://www.w3.org/2002/07/owl#> SELECT ?type (COUNT(DISTINCT(?o)) AS ?cnt) WHERE {?s ?p ?o . ?o a ?type . ?type a owl:Class .} GROUP BY ?type"); 051 private static final ParameterizedSparqlString OBJECTS_OF_TYPE_WITH_INFERENCE_COUNT_BATCHED_QUERY = new ParameterizedSparqlString( 052 "PREFIX owl:<http://www.w3.org/2002/07/owl#> SELECT ?type (COUNT(DISTINCT(?o)) AS ?cnt) WHERE {?s ?p ?o . ?o rdf:type/rdfs:subClassOf* ?type . ?type a owl:Class .} GROUP BY ?type"); 053 054 @ConfigOption(defaultValue = "false", description = "compute everything in a single SPARQL query") 055 protected boolean batchMode = false; 056 057 public ObjectPropertyRangeAxiomLearner() { 058 super.posExamplesQueryTemplate = new ParameterizedSparqlString("SELECT ?s ?o WHERE {?o ?p ?s. ?s a ?type .}"); 059 super.negExamplesQueryTemplate = new ParameterizedSparqlString("SELECT ?s ?o WHERE {?o ?p ?s. FILTER NOT EXISTS {?s a ?type}}"); 060 061 COUNT_QUERY = DISTINCT_OBJECTS_COUNT_QUERY; 062 063 axiomType = AxiomType.OBJECT_PROPERTY_RANGE; 064 } 065 066 public ObjectPropertyRangeAxiomLearner(SparqlEndpointKS ks){ 067 this(); 068 this.ks = ks; 069 } 070 071 /* (non-Javadoc) 072 * @see org.dllearner.algorithms.properties.PropertyAxiomLearner#setEntityToDescribe(org.semanticweb.owlapi.model.OWLProperty) 073 */ 074 @Override 075 public void setEntityToDescribe(OWLObjectProperty entityToDescribe) { 076 super.setEntityToDescribe(entityToDescribe); 077 078 DISTINCT_OBJECTS_COUNT_QUERY.setIri("p", entityToDescribe.toStringID()); 079 OBJECTS_OF_TYPE_COUNT_QUERY.setIri("p", entityToDescribe.toStringID()); 080 OBJECTS_OF_TYPE_WITH_INFERENCE_COUNT_QUERY.setIri("p", entityToDescribe.toStringID()); 081 OBJECTS_OF_TYPE_COUNT_BATCHED_QUERY.setIri("p", entityToDescribe.toStringID()); 082 OBJECTS_OF_TYPE_WITH_INFERENCE_COUNT_BATCHED_QUERY.setIri("p", entityToDescribe.toStringID()); 083 } 084 085 /* (non-Javadoc) 086 * @see org.dllearner.core.AbstractAxiomLearningAlgorithm#getExistingAxioms() 087 */ 088 @Override 089 protected void getExistingAxioms() { 090 OWLClassExpression existingRange = reasoner.getRange(entityToDescribe); 091 if (existingRange != null) { 092 existingAxioms.add(df.getOWLObjectPropertyRangeAxiom(entityToDescribe, existingRange)); 093 logger.info("Existing range: " + existingRange); 094 if (reasoner.isPrepared()) { 095 if (reasoner.getClassHierarchy().contains(existingRange)) { 096 for (OWLClassExpression sup : reasoner.getClassHierarchy().getSuperClasses(existingRange)) { 097 existingAxioms.add(df.getOWLObjectPropertyRangeAxiom(entityToDescribe, existingRange)); 098 logger.info("Existing range(inferred): " + sup); 099 } 100 } 101 } 102 } 103 } 104 105 /* (non-Javadoc) 106 * @see org.dllearner.algorithms.properties.PropertyAxiomLearner#getSampleQuery() 107 */ 108 @Override 109 protected ParameterizedSparqlString getSampleQuery() { 110 return new ParameterizedSparqlString( 111 "PREFIX owl:<http://www.w3.org/2002/07/owl#> " 112 + "CONSTRUCT " 113 + "{?s ?p ?o . ?o a ?cls . " 114 + (strictOWLMode ? "?cls a owl:Class . " : "") 115 + "} " 116 + "WHERE " 117 + "{?s ?p ?o . ?o a ?cls . " 118 + (strictOWLMode ? "?cls a owl:Class . " : "") 119 + "}"); 120 } 121 122 @Override 123 protected void run(){ 124 if(batchMode) { 125 runBatched(); 126 } else { 127 runIterative(); 128 } 129 } 130 131 private void runIterative(){ 132 // get the candidates 133 Set<OWLClass> candidates = reasoner.getNonEmptyOWLClasses(); 134 135 // check for each candidate how often the subject belongs to it 136 int i = 1; 137 for (OWLClass candidate : candidates) { 138 logger.debug("Candidate:" + candidate); 139 progressMonitor.learningProgressChanged(axiomType, i++, candidates.size()); 140 141 // get total number of instances of B 142 int cntB = reasoner.getPopularity(candidate); 143 logger.debug("Popularity:" + cntB); 144 145 if(cntB == 0){// skip empty properties 146 logger.debug("Cannot compute range statements for empty candidate class " + candidate); 147 continue; 148 } 149 150 // get number of instances of (A AND B) 151 OBJECTS_OF_TYPE_COUNT_QUERY.setIri("type", candidate.toStringID()); 152 int cntAB = executeSelectQuery(OBJECTS_OF_TYPE_COUNT_QUERY.toString()).next().getLiteral("cnt").getInt(); 153 logger.debug("Candidate:" + candidate + "\npopularity:" + cntB + "\noverlap:" + cntAB); 154 155 // compute score 156 AxiomScore score = computeScore(popularity, cntB, cntAB); 157 158 currentlyBestAxioms.add( 159 new EvaluatedAxiom<>(df.getOWLObjectPropertyRangeAxiom(entityToDescribe, candidate), score)); 160 } 161 } 162 163 /** 164 * We can handle the domain axiom Domain(r, C) as a subclass of axiom \exists r.\top \sqsubseteq C 165 */ 166 private void runBatched(){ 167 168 // we can compute the popularity of the properties once which can avoid sending several single 169 // query later on 170 reasoner.precomputeClassPopularity(); 171 172 // get for each object type the frequency 173 ResultSet rs = executeSelectQuery(OBJECTS_OF_TYPE_COUNT_BATCHED_QUERY.toString()); 174 ResultSetRewindable rsrw = ResultSetFactory.copyResults(rs); 175 int size = rsrw.size(); 176 rsrw.reset(); 177 int i = 1; 178 while(rsrw.hasNext()){ 179 QuerySolution qs = rsrw.next(); 180 if(qs.getResource("type").isURIResource()){ 181 progressMonitor.learningProgressChanged(axiomType, i++, size); 182 183 OWLClass candidate = df.getOWLClass(IRI.create(qs.getResource("type").getURI())); 184 185 //get total number of instances of B 186 int cntB = reasoner.getPopularity(candidate); 187 188 //get number of instances of (A AND B) 189 int cntAB = qs.getLiteral("cnt").getInt(); 190 191 //precision (A AND B)/B 192 double precision = Heuristics.getConfidenceInterval95WaldAverage(cntB, cntAB); 193 194 //recall (A AND B)/A 195 double recall = Heuristics.getConfidenceInterval95WaldAverage(popularity, cntAB); 196 197 //F score 198 double score = Heuristics.getFScore(recall, precision, beta); 199 200 currentlyBestAxioms.add( 201 new EvaluatedAxiom<>( 202 df.getOWLObjectPropertyRangeAxiom(entityToDescribe, candidate), 203 new AxiomScore(score, useSampling))); 204 205 } 206 } 207 } 208 209 @Override 210 protected Set<OWLObjectPropertyAssertionAxiom> getExamples(ParameterizedSparqlString queryTemplate, 211 EvaluatedAxiom<OWLObjectPropertyRangeAxiom> evAxiom) { 212 OWLObjectPropertyRangeAxiom axiom = evAxiom.getAxiom(); 213 queryTemplate.setIri("p", entityToDescribe.toStringID()); 214 queryTemplate.setIri("type", axiom.getRange().asOWLClass().toStringID()); 215 216 Set<OWLObjectPropertyAssertionAxiom> examples = new TreeSet<>(); 217 218 ResultSet rs = executeSelectQuery(queryTemplate.toString()); 219 220 while (rs.hasNext()) { 221 QuerySolution qs = rs.next(); 222 OWLIndividual subject = df.getOWLNamedIndividual(IRI.create(qs.getResource("s").getURI())); 223 OWLIndividual object = df.getOWLNamedIndividual(IRI.create(qs.getResource("o").getURI())); 224 examples.add(df.getOWLObjectPropertyAssertionAxiom(entityToDescribe, subject, object)); 225 } 226 227 return examples; 228 } 229 230 public void setBatchMode(boolean batchMode) { 231 this.batchMode = batchMode; 232 } 233 234 public boolean isBatchMode() { 235 return batchMode; 236 } 237 238 239 public static void main(String[] args) throws Exception { 240 ToStringRenderer.getInstance().setRenderer(new DLSyntaxObjectRenderer()); 241 SparqlEndpointKS ks = new SparqlEndpointKS(SparqlEndpoint.getEndpointDBpedia()); 242 ks.init(); 243 244 ObjectPropertyRangeAxiomLearner la = new ObjectPropertyRangeAxiomLearner(ks); 245 la.setPropertyToDescribe(new OWLObjectPropertyImpl(IRI.create("http://dbpedia.org/ontology/author"))); 246 la.setUseSampling(false); 247 la.setBatchMode(true); 248 la.setProgressMonitor(new ConsoleAxiomLearningProgressMonitor()); 249 la.init(); 250 251 la.start(); 252 253 la.getCurrentlyBestEvaluatedAxioms().forEach(ax -> { 254 System.out.println("---------------\n" + ax); 255 la.getPositiveExamples(ax).stream().limit(5).forEach(System.out::println); 256 }); 257 } 258}