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.EvaluatedAxiom; 023import org.dllearner.core.config.ConfigOption; 024import org.dllearner.kb.SparqlEndpointKS; 025import org.dllearner.learningproblems.AxiomScore; 026import org.semanticweb.owlapi.model.*; 027 028import java.util.Set; 029import java.util.SortedSet; 030import java.util.TreeSet; 031 032/** 033 * A learning algorithm for object property hierarchy axioms. 034 * @author Lorenz Buehmann 035 * 036 */ 037public abstract class ObjectPropertyHierarchyAxiomLearner<T extends OWLObjectPropertyAxiom> extends ObjectPropertyAxiomLearner<T> { 038 039 protected static final ParameterizedSparqlString PROPERTY_OVERLAP_QUERY = new ParameterizedSparqlString( 040 "SELECT ?p_other (COUNT(*) AS ?overlap) WHERE {" 041 + "?s ?p ?o; ?p_other ?o . " 042 + "?p_other a <http://www.w3.org/2002/07/owl#ObjectProperty> . FILTER(?p != ?p_other)}" 043 + " GROUP BY ?p_other"); 044 045 protected static final ParameterizedSparqlString PROPERTY_OVERLAP_WITH_RANGE_QUERY = new ParameterizedSparqlString( 046 "SELECT ?p_other (COUNT(*) AS ?overlap) WHERE {" 047 + "?s ?p ?o; ?p_other ?o . " 048 + "?p_other a <http://www.w3.org/2002/07/owl#ObjectProperty> ; rdfs:range ?range . FILTER(?p != ?p_other)}" 049 + " GROUP BY ?p_other"); 050 051 protected static final ParameterizedSparqlString GIVEN_PROPERTY_OVERLAP_QUERY = new ParameterizedSparqlString( 052 "SELECT (COUNT(*) AS ?overlap) WHERE {?s ?p ?o; ?p_other ?o . }"); 053 054 private static final ParameterizedSparqlString SAMPLE_QUERY = new ParameterizedSparqlString( 055 "CONSTRUCT {?s ?p ?o . ?s ?p1 ?o . ?p1 a <http://www.w3.org/2002/07/owl#ObjectProperty> .} " 056 + "WHERE {?s ?p ?o . OPTIONAL{?s ?p1 ?o . FILTER(?p != ?p1)} }"); 057 058 protected static final ParameterizedSparqlString PROPERTY_OVERLAP_WITH_POPULARITY_BATCH_QUERY = new ParameterizedSparqlString( 059 "SELECT ?p_other (COUNT(*) AS ?overlap) WHERE {" 060 + "?s ?p ?o; ?p_other ?o . " 061 + "?p_other a <http://www.w3.org/2002/07/owl#ObjectProperty> ; rdfs:range ?range . FILTER(?p != ?p_other)}" 062 + " GROUP BY ?p_other"); 063 064 065 // set strict mode, i.e. if for the property explicit domain and range is given 066 // we only consider properties with same range and domain 067 @ConfigOption(defaultValue = "false") 068 protected boolean strictMode = false; 069 070 @ConfigOption(defaultValue = "1.0", description = "the beta value for the F-score calculation") 071 protected double beta = 1.0; 072 073 @ConfigOption(defaultValue = "false", description = "compute everything in a single SPARQL query") 074 protected boolean batchMode = false; 075 076 077 public ObjectPropertyHierarchyAxiomLearner(SparqlEndpointKS ks) { 078 this.ks = ks; 079 080 super.posExamplesQueryTemplate = new ParameterizedSparqlString( 081 "SELECT DISTINCT ?s ?o WHERE {?s ?p ?o ; ?p_other ?o}"); 082 super.negExamplesQueryTemplate = new ParameterizedSparqlString( 083 "SELECT DISTINCT ?s ?o WHERE {?s ?p ?o. FILTER NOT EXISTS{?s ?p_other ?o}}"); 084 } 085 086 @Override 087 public void setEntityToDescribe(OWLObjectProperty entityToDescribe) { 088 super.setEntityToDescribe(entityToDescribe); 089 090 GIVEN_PROPERTY_OVERLAP_QUERY.setIri("p", entityToDescribe.toStringID()); 091 PROPERTY_OVERLAP_QUERY.setIri("p", entityToDescribe.toStringID()); 092 PROPERTY_OVERLAP_WITH_RANGE_QUERY.setIri("p", entityToDescribe.toStringID()); 093 } 094 095 /* (non-Javadoc) 096 * @see org.dllearner.algorithms.properties.PropertyAxiomLearner#getSampleQuery() 097 */ 098 @Override 099 protected ParameterizedSparqlString getSampleQuery() { 100 return SAMPLE_QUERY; 101 } 102 103 @Override 104 protected void run() { 105 if(batchMode) { 106 runBatched(); 107 } else { 108 runIterative(); 109 } 110 } 111 112 protected void runIterative() { 113 // get the candidates 114 SortedSet<OWLObjectProperty> candidates = getCandidates(); 115 116 // check for each candidate if an overlap exist 117 int i = 1; 118 for (OWLObjectProperty p : candidates) { 119 logger.debug("processing candidate property {}...", p); 120 progressMonitor.learningProgressChanged(axiomType, i++, candidates.size()); 121 122 // get the popularity of the candidate 123 int candidatePopularity = reasoner.getPopularity(p); 124 125 if(candidatePopularity == 0){// skip empty properties 126 logger.debug("Cannot compute equivalence statements for empty candidate property " + p); 127 continue; 128 } 129 130 // get the number of overlapping triples, i.e. triples with the same subject and object 131 GIVEN_PROPERTY_OVERLAP_QUERY.setIri("p_other", p.toStringID()); 132 ResultSet rs = executeSelectQuery(GIVEN_PROPERTY_OVERLAP_QUERY.toString()); 133 int overlap = rs.next().getLiteral("overlap").getInt(); 134 135 // compute the score 136 AxiomScore score = computeScore(candidatePopularity, popularity, overlap); 137 138 currentlyBestAxioms.add(new EvaluatedAxiom<>(getAxiom(entityToDescribe, p), score)); 139 } 140 } 141 142 /** 143 * In this method we try to compute the overlap with each property in one single SPARQL query. 144 * This method might be much slower as the query is much more complex. 145 * 146 * There are two options: 147 * 1) compute the overlap in a single query, but the popularity for each overlapping property separately 148 * 2) compute overlap and popularity in a single query 149 */ 150 protected void runBatched() { 151 152 String query; 153 if(strictMode){ 154 // get rdfs:range of the property 155 OWLClassExpression range = reasoner.getRange(entityToDescribe); 156 157 if(range != null && !range.isAnonymous() && !range.isOWLThing()){ 158 PROPERTY_OVERLAP_WITH_RANGE_QUERY.setIri("range", range.asOWLClass().toStringID()); 159 query = PROPERTY_OVERLAP_WITH_RANGE_QUERY.toString(); 160 } else { 161 query = PROPERTY_OVERLAP_QUERY.toString(); 162 } 163 } else { 164 query = PROPERTY_OVERLAP_QUERY.toString(); 165 } 166 167 // compute the property candidates p_i that have at least one (s,o) in common with the target property p 168 ResultSet rs = executeSelectQuery(query); 169 ResultSetRewindable rsrw = ResultSetFactory.copyResults(rs); 170 int size = rsrw.size(); 171 rs = rsrw; 172 while (rs.hasNext()) { 173 QuerySolution qs = rsrw.next(); 174 175 progressMonitor.learningProgressChanged(axiomType, rs.getRowNumber(), size); 176 177 OWLObjectProperty candidate = df.getOWLObjectProperty(IRI.create(qs.getResource("p_other").getURI())); 178 179 // get the popularity of the candidate 180 int candidatePopularity = reasoner.getPopularity(candidate); 181 182 // get the number of overlapping triples, i.e. triples with the same subject and object 183 int overlap = qs.getLiteral("overlap").getInt(); 184 185 // compute the score 186 AxiomScore score = computeScore(candidatePopularity, popularity, overlap); 187 188 currentlyBestAxioms.add(new EvaluatedAxiom<>(getAxiom(entityToDescribe, candidate), score)); 189 } 190 } 191 192 public abstract T getAxiom(OWLObjectProperty property, OWLObjectProperty otherProperty); 193 194 /** 195 * Returns the candidate properties for comparison. 196 * @return the candidate properties 197 */ 198 protected SortedSet<OWLObjectProperty> getCandidates(){ 199 // get the candidates 200 SortedSet<OWLObjectProperty> candidates = new TreeSet<>(); 201 202 if (strictMode) { // that have the same domain and range 203 // get rdfs:domain of the property 204 OWLClassExpression domain = reasoner.getDomain(entityToDescribe); 205 206 // get rdfs:range of the property 207 OWLClassExpression range = reasoner.getRange(entityToDescribe); 208 209 String query = "SELECT ?p WHERE {?p a owl:ObjectProperty ."; 210 if (domain != null && !domain.isAnonymous() && !domain.isOWLThing()) { 211 query += "?p rdfs:domain <" + domain.asOWLClass().toStringID() + "> ."; 212 } 213 214 if (range != null && !range.isAnonymous() && !range.isOWLThing()) { 215 query += "?p rdfs:range <" + range.asOWLClass().toStringID() + "> ."; 216 } 217 query += "}"; 218 219 ResultSet rs = executeSelectQuery(query); 220 while (rs.hasNext()) { 221 OWLObjectProperty p = df.getOWLObjectProperty(IRI.create(rs.next().getResource("p").getURI())); 222 candidates.add(p); 223 } 224 225 } else {// we have to check all other properties 226 candidates = reasoner.getOWLObjectProperties(); 227 } 228 candidates.remove(entityToDescribe); 229 230 return candidates; 231 } 232 233 @Override 234 protected Set<OWLObjectPropertyAssertionAxiom> getExamples(ParameterizedSparqlString queryTemplate, 235 EvaluatedAxiom<T> evAxiom) { 236 T axiom = evAxiom.getAxiom(); 237 queryTemplate.setIri("p", entityToDescribe.toStringID()); 238 239 OWLObjectProperty otherProperty; 240 if(axiom instanceof OWLNaryPropertyAxiom){// we assume a single atomic property 241 otherProperty = ((OWLNaryPropertyAxiom<OWLObjectPropertyExpression>) axiom).getPropertiesMinus(entityToDescribe).iterator().next() 242 .asOWLObjectProperty(); 243 } else { 244 otherProperty = ((OWLSubObjectPropertyOfAxiom) axiom).getSuperProperty().asOWLObjectProperty(); 245 } 246 queryTemplate.setIri("p_other", otherProperty.toStringID()); 247 248 Set<OWLObjectPropertyAssertionAxiom> examples = new TreeSet<>(); 249 250 ResultSet rs = executeSelectQuery(queryTemplate.toString()); 251 252 while (rs.hasNext()) { 253 QuerySolution qs = rs.next(); 254 OWLIndividual subject = df.getOWLNamedIndividual(IRI.create(qs.getResource("s").getURI())); 255 OWLIndividual object = df.getOWLNamedIndividual(IRI.create(qs.getResource("o").getURI())); 256 examples.add(df.getOWLObjectPropertyAssertionAxiom(entityToDescribe, subject, object)); 257 } 258 259 return examples; 260 } 261 262 /** 263 * @param beta the beta to set 264 */ 265 public void setBeta(double beta) { 266 this.beta = beta; 267 } 268 269 /** 270 * @param strictMode the strictMode to set 271 */ 272 public void setStrictMode(boolean strictMode) { 273 this.strictMode = strictMode; 274 } 275 276 public boolean isStrictMode() { 277 return strictMode; 278 } 279 280 public double getBeta() { 281 return beta; 282 } 283 284 /** 285 * If <code>true</code>, batch mode is used and only a single query will be used to compute the result. Otherwise, 286 * iteration over all properties in the ontology is done, i.e. at lots of queries - but simpler ones - will 287 * be executed. 288 * 289 * @param batchMode 290 */ 291 public void setBatchMode(boolean batchMode) { 292 this.batchMode = batchMode; 293 } 294 295 public boolean isBatchMode() { 296 return batchMode; 297 } 298}