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 com.jamonapi.Monitor; 023import com.jamonapi.MonitorFactory; 024import org.dllearner.core.ComponentAnn; 025import org.dllearner.core.ConsoleAxiomLearningProgressMonitor; 026import org.dllearner.core.EvaluatedAxiom; 027import org.dllearner.core.config.ConfigOption; 028import org.dllearner.kb.SparqlEndpointKS; 029import org.dllearner.kb.sparql.SparqlEndpoint; 030import org.dllearner.learningproblems.AxiomScore; 031import org.dllearner.learningproblems.Heuristics; 032import org.dllearner.utilities.owl.OWLClassExpressionToSPARQLConverter; 033import org.semanticweb.owlapi.dlsyntax.renderer.DLSyntaxObjectRenderer; 034import org.semanticweb.owlapi.io.ToStringRenderer; 035import org.semanticweb.owlapi.model.*; 036import uk.ac.manchester.cs.owl.owlapi.OWLObjectPropertyImpl; 037 038import java.util.Set; 039import java.util.TreeSet; 040 041@ComponentAnn(name="object property domain axiom learner", shortName="opldomain", version=0.1, description="A learning algorithm for object property domain axioms.") 042public class ObjectPropertyDomainAxiomLearner extends ObjectPropertyAxiomLearner<OWLObjectPropertyDomainAxiom> { 043 044 private static final ParameterizedSparqlString POPULARITY_COUNT_QUERY = new ParameterizedSparqlString( 045 "SELECT (COUNT(DISTINCT(?s)) AS ?cnt) WHERE {?s ?p ?o .}"); 046 047 private static final ParameterizedSparqlString SUBJECTS_OF_TYPE_COUNT_QUERY = new ParameterizedSparqlString( 048 "SELECT (COUNT(DISTINCT(?s)) AS ?cnt) WHERE {?s ?p ?o; a ?type .}"); 049 050 private static final ParameterizedSparqlString SUBJECTS_OF_TYPE_WITH_INFERENCE_COUNT_QUERY = new ParameterizedSparqlString( 051 "SELECT (COUNT(DISTINCT(?s)) AS ?cnt) WHERE {?s ?p ?o; rdf:type/rdfs:subClassOf* ?type .}"); 052 053 private static final ParameterizedSparqlString SUBJECTS_OF_TYPE_COUNT_BATCHED_QUERY = new ParameterizedSparqlString( 054 "PREFIX owl:<http://www.w3.org/2002/07/owl#> " + 055 "SELECT ?type (COUNT(DISTINCT(?s)) AS ?cnt) WHERE {" + 056 "?s ?p ?o; a ?type . ?type a owl:Class .} GROUP BY ?type"); 057 058 private static final ParameterizedSparqlString SUBJECTS_OF_TYPE_WITH_INFERENCE_COUNT_BATCHED_QUERY = new ParameterizedSparqlString( 059 "PREFIX owl:<http://www.w3.org/2002/07/owl#> " + 060 "SELECT ?type (COUNT(DISTINCT(?s)) AS ?cnt) WHERE {" + 061 "?s ?p ?o; rdf:type/rdfs:subClassOf* ?type . ?type a owl:Class .} GROUP BY ?type"); 062 063 @ConfigOption(defaultValue = "false", description = "compute everything in a single SPARQL query") 064 protected boolean batchMode = false; 065 066 public ObjectPropertyDomainAxiomLearner(){ 067 super.posExamplesQueryTemplate = new ParameterizedSparqlString("SELECT ?s ?o WHERE {?s ?p ?o. ?s a ?type}"); 068 super.negExamplesQueryTemplate = new ParameterizedSparqlString("SELECT ?s ?o WHERE {?s ?p ?o. FILTER NOT EXISTS{?s a ?type}}"); 069 070 COUNT_QUERY = DISTINCT_SUBJECTS_COUNT_QUERY; 071 072 axiomType = AxiomType.OBJECT_PROPERTY_DOMAIN; 073 } 074 075 public ObjectPropertyDomainAxiomLearner(SparqlEndpointKS ks){ 076 this(); 077 078 this.ks = ks; 079 } 080 081 @Override 082 public void setEntityToDescribe(OWLObjectProperty entityToDescribe) { 083 super.setEntityToDescribe(entityToDescribe); 084 085 DISTINCT_SUBJECTS_COUNT_QUERY.setIri("p", entityToDescribe.toStringID()); 086 SUBJECTS_OF_TYPE_COUNT_QUERY.setIri("p", entityToDescribe.toStringID()); 087 SUBJECTS_OF_TYPE_WITH_INFERENCE_COUNT_QUERY.setIri("p", entityToDescribe.toStringID()); 088 SUBJECTS_OF_TYPE_COUNT_BATCHED_QUERY.setIri("p", entityToDescribe.toStringID()); 089 SUBJECTS_OF_TYPE_WITH_INFERENCE_COUNT_BATCHED_QUERY.setIri("p", entityToDescribe.toStringID()); 090 } 091 092 /* (non-Javadoc) 093 * @see org.dllearner.algorithms.properties.PropertyAxiomLearner#getSampleQuery() 094 */ 095 @Override 096 protected ParameterizedSparqlString getSampleQuery() { 097 return new ParameterizedSparqlString( 098 "PREFIX owl:<http://www.w3.org/2002/07/owl#> " 099 + "CONSTRUCT " 100 + "{?s ?p ?o; a ?cls1 . " 101 + (strictOWLMode ? "?cls1 a owl:Class. " : "") 102 + "} " 103 + "WHERE " 104 + "{?s ?p ?o; a ?cls1 . " 105 + (strictOWLMode ? "?cls1 a owl:Class. " : "") 106 + "}"); 107 } 108 109 /* (non-Javadoc) 110 * @see org.dllearner.core.AbstractAxiomLearningAlgorithm#getExistingAxioms() 111 */ 112 @Override 113 protected void getExistingAxioms() { 114 OWLClassExpression existingDomain = reasoner.getDomain(entityToDescribe); 115 logger.info("Existing domain: " + existingDomain); 116 if(existingDomain != null){ 117 existingAxioms.add(df.getOWLObjectPropertyDomainAxiom(entityToDescribe, existingDomain)); 118 if(reasoner.isPrepared()){ 119 if(reasoner.getClassHierarchy().contains(existingDomain)){ 120 for(OWLClassExpression sup : reasoner.getClassHierarchy().getSuperClasses(existingDomain)){ 121 existingAxioms.add(df.getOWLObjectPropertyDomainAxiom(entityToDescribe, existingDomain)); 122 logger.info("Existing domain(inferred): " + sup); 123 } 124 } 125 } 126 } 127 } 128 129 protected int getPopularity() { 130 POPULARITY_COUNT_QUERY.setIri("p", entityToDescribe.toStringID()); 131 String query = POPULARITY_COUNT_QUERY.toString(); 132 ResultSet rs = executeSelectQuery(query); 133 int popularity = rs.next().getLiteral("cnt").getInt(); 134 return popularity; 135 } 136 137 /** 138 * We can handle the domain axiom Domain(r, C) as a subclass of axiom \exists r.\top \sqsubseteq C 139 * 140 * A = \exists r.\top 141 * B = C 142 */ 143 @Override 144 protected void run(){ 145 if(batchMode) { 146 runBatched(); 147 } else { 148 runIterative(); 149 } 150 } 151 152 private void runIterative() { 153 // get the candidates 154 Set<OWLClass> candidates = reasoner.getNonEmptyOWLClasses(); 155 156 // check for each candidate how often the subject belongs to it 157 int i = 1; 158 Monitor mon = MonitorFactory.getTimeMonitor("dom-class-time"); 159 for (OWLClass candidate : candidates) { 160 mon.start(); 161 logger.debug("Candidate:" + candidate); 162 progressMonitor.learningProgressChanged(axiomType, i++, candidates.size()); 163 164 // get total number of instances of B 165 int cntB = reasoner.getPopularity(candidate); 166 167 if(cntB == 0){// skip empty classes 168 logger.debug("Cannot compute domain statements for empty candidate class " + candidate); 169 continue; 170 } 171 172 // get number of instances of (A AND B) 173 SUBJECTS_OF_TYPE_COUNT_QUERY.setIri("type", candidate.toStringID()); 174 int cntAB = executeSelectQuery(SUBJECTS_OF_TYPE_COUNT_QUERY.toString()).next().getLiteral("cnt").getInt(); 175 logger.debug("Candidate:" + candidate + "\npopularity:" + cntB + "\noverlap:" + cntAB); 176 177 // compute score 178 AxiomScore score = computeScore(popularity, cntB, cntAB); 179 180 currentlyBestAxioms.add( 181 new EvaluatedAxiom<>( 182 df.getOWLObjectPropertyDomainAxiom(entityToDescribe, candidate), 183 score)); 184 185 mon.stop(); 186 logger.debug(candidate + " analyzed in " + mon.getLastValue()); 187 } 188 } 189 190 /** 191 * We can handle the domain axiom Domain(r, C) as a subclass of axiom \exists r.\top \sqsubseteq C 192 */ 193 private void runBatched(){ 194 195 reasoner.precomputeClassPopularity(); 196 197 // get for each subject type the frequency 198 ResultSet rs = executeSelectQuery(SUBJECTS_OF_TYPE_COUNT_BATCHED_QUERY.toString()); 199 ResultSetRewindable rsrw = ResultSetFactory.copyResults(rs); 200 int size = rsrw.size(); 201 rsrw.reset(); 202 int i = 1; 203 while(rsrw.hasNext()){ 204 QuerySolution qs = rsrw.next(); 205 if(qs.getResource("type").isURIResource()){ 206 progressMonitor.learningProgressChanged(axiomType, i++, size); 207 208 OWLClass candidate = df.getOWLClass(IRI.create(qs.getResource("type").getURI())); 209 210 //get total number of instances of B 211 int cntB = reasoner.getPopularity(candidate); 212 213 //get number of instances of (A AND B) 214 int cntAB = qs.getLiteral("cnt").getInt(); 215 216 //precision (A AND B)/B 217 double precision = Heuristics.getConfidenceInterval95WaldAverage(cntB, cntAB); 218 219 //recall (A AND B)/A 220 double recall = Heuristics.getConfidenceInterval95WaldAverage(popularity, cntAB); 221 222 //F score 223 double score = Heuristics.getFScore(recall, precision, beta); 224 225 currentlyBestAxioms.add( 226 new EvaluatedAxiom<>( 227 df.getOWLObjectPropertyDomainAxiom(entityToDescribe, candidate), 228 new AxiomScore(score, useSampling))); 229 230 } 231 } 232 } 233 234 @Override 235 protected Set<OWLObjectPropertyAssertionAxiom> getExamples(ParameterizedSparqlString queryTemplate, 236 EvaluatedAxiom<OWLObjectPropertyDomainAxiom> evAxiom) { 237 OWLObjectPropertyDomainAxiom axiom = evAxiom.getAxiom(); 238 queryTemplate.setIri("p", entityToDescribe.toStringID()); 239 queryTemplate.setIri("type", axiom.getDomain().asOWLClass().toStringID()); 240 241 Set<OWLObjectPropertyAssertionAxiom> 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 OWLIndividual object = df.getOWLNamedIndividual(IRI.create(qs.getResource("o").getURI())); 249 examples.add(df.getOWLObjectPropertyAssertionAxiom(entityToDescribe, subject, object)); 250 } 251 252 return examples; 253 } 254 255 public void setBatchMode(boolean batchMode) { 256 this.batchMode = batchMode; 257 } 258 259 public boolean isBatchMode() { 260 return batchMode; 261 } 262 263 public static void main(String[] args) throws Exception { 264 ToStringRenderer.getInstance().setRenderer(new DLSyntaxObjectRenderer()); 265 SparqlEndpointKS ks = new SparqlEndpointKS(SparqlEndpoint.getEndpointDBpedia()); 266 ks.init(); 267 268 ObjectPropertyDomainAxiomLearner la = new ObjectPropertyDomainAxiomLearner(ks); 269 la.setPropertyToDescribe(new OWLObjectPropertyImpl(IRI.create("http://dbpedia.org/ontology/author"))); 270 la.setUseSampling(false); 271 la.setBatchMode(true); 272 la.setProgressMonitor(new ConsoleAxiomLearningProgressMonitor()); 273 la.init(); 274 275 la.start(); 276 277 la.getCurrentlyBestEvaluatedAxioms().forEach(ax -> { 278 System.out.println("---------------\n" + ax); 279 la.getPositiveExamples(ax).stream().limit(5).forEach(System.out::println); 280 }); 281 } 282 283}