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}