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