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;
020
021import java.util.ArrayList;
022import java.util.Arrays;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026
027import org.dllearner.core.AbstractCELA;
028import org.dllearner.core.AbstractClassExpressionLearningProblem;
029import org.dllearner.core.AbstractReasonerComponent;
030import org.dllearner.core.ComponentAnn;
031import org.dllearner.core.ComponentInitException;
032import org.dllearner.core.EvaluatedDescription;
033import org.dllearner.core.config.ConfigOption;
034import org.dllearner.core.owl.OWLObjectIntersectionOfImplExt;
035import org.dllearner.learningproblems.EvaluatedDescriptionPosNeg;
036import org.dllearner.learningproblems.ScorePosNeg;
037import org.semanticweb.owlapi.model.OWLClassExpression;
038import org.semanticweb.owlapi.model.OWLObjectProperty;
039
040/**
041 * Simple example learning algorithm exhaustively creating complex class
042 * expressions of the AL description logic.
043 */
044@ComponentAnn(name = "Naive AL Learner", shortName = "naiveALLearner", version = 0.1)
045public class NaiveALLearner extends AbstractCELA{
046
047    private Map<Integer, List<OWLClassExpression>> generatedDescriptions;
048    
049    private boolean running = false;
050    @ConfigOption(defaultValue = "4", description = "maximum length of class expression")
051    private int maxLength = 4;
052    
053    private OWLClassExpression bestDescription;
054    private ScorePosNeg bestScore;
055
056    public NaiveALLearner() {
057    }
058
059    public NaiveALLearner(AbstractClassExpressionLearningProblem lp, AbstractReasonerComponent reasoner) {
060        super(lp, reasoner);
061    }
062    @Override
063    public void start() {
064        running = true;
065
066        // first generate all possible complex class expressions up to a length
067        // of maxLength
068        for (int i=1; i<=maxLength; i++) {
069            generateDescriptions(i);
070        }
071
072        // now evaluate these expressions to find a description that best
073        // describes and distinguishes positive and negative examples
074        evaluateGeneratedDefinition();
075
076        System.out.println("Best description: " + bestDescription);
077        System.out.println("Best score: " + bestScore);
078        stop();
079    }
080
081    @Override
082    public void init() throws ComponentInitException {
083        generatedDescriptions = new HashMap<>();
084        
085        // start with owl:Thing
086        bestDescription = OWL_THING;
087        bestScore = null;
088        
089        initialized = true;
090    }
091
092    @Override
093    public void stop() {
094        running = false;
095    }
096
097    @Override
098    public boolean isRunning() {
099        return running;
100    }
101
102    @Override
103    public OWLClassExpression getCurrentlyBestDescription() {
104        return bestDescription;
105    }
106
107    @Override
108    public EvaluatedDescription getCurrentlyBestEvaluatedDescription() {
109        return new EvaluatedDescriptionPosNeg(bestDescription,bestScore);
110    }
111
112    /**
113     * Generates new descriptions (i.e. concepts) of the length `length`.
114     * - `length` == 1:
115     *      - all atomic classes are added to the set `generatedDescriptions`
116     *      - the concepts owl:Thing and owl:Nothing are added
117     * - `length` == 2:
118     *      - negations of all atomic classes are added
119     * - `length` == 3:
120     *      - all atomic roles are added as limited existential quantification/
121     *        value restriction
122     *      - all combinations of intersections of atomic classes are added
123     * - `length` > 3:
124     *      - all concepts of length `length`-1 are extended by intersection
125     *        with an atomic class
126     *      - all concepts of length `length`-2 are extended by limited
127     *        existential quantification/value restriction
128     *
129     * @param length the length
130     */
131    private void generateDescriptions(int length) {
132        generatedDescriptions.put(length, new ArrayList<>());
133        List<OWLClassExpression> thisLenDescriptions = generatedDescriptions.get(length);
134
135        if (length == 1) {
136            // add atomic classes
137            thisLenDescriptions.add(OWL_THING);
138            thisLenDescriptions.add(OWL_NOTHING);
139
140            for (OWLClassExpression atomicClass : reasoner.getAtomicConceptsList()) {
141                thisLenDescriptions.add(atomicClass);
142            }
143        }
144
145        if (length == 2) {
146            // add negation of atomic classes
147            for (OWLClassExpression atomicClass : reasoner.getAtomicConceptsList()) {
148                thisLenDescriptions.add(dataFactory.getOWLObjectComplementOf(atomicClass));
149            }
150        }
151
152        if (length == 3) {
153            // add limited existential quantification/value restriction
154            for (OWLObjectProperty prop : reasoner.getObjectProperties()) {
155                thisLenDescriptions.add(dataFactory.getOWLObjectSomeValuesFrom(prop, OWL_THING));
156                thisLenDescriptions.add(dataFactory.getOWLObjectAllValuesFrom(prop, OWL_THING));
157
158                for (OWLClassExpression atomicClass : reasoner.getAtomicConceptsList()) {
159                    thisLenDescriptions.add(dataFactory.getOWLObjectAllValuesFrom(prop, atomicClass));
160                }
161            }
162
163            // add intersections of atomic concepts
164            for (OWLClassExpression leftAtomicConcept : reasoner.getAtomicConceptsList()) {
165                for(OWLClassExpression rightAtomicConcept : reasoner.getAtomicConceptsList()) {
166                    thisLenDescriptions.add(new OWLObjectIntersectionOfImplExt(
167                            Arrays.asList(leftAtomicConcept, rightAtomicConcept)));
168                }
169            }
170        }
171
172        if (length > 3) {
173            // add ALL <objectProperty>.<generatedConcept> for all concepts of length
174            // `length`-2
175            for (OWLObjectProperty objProp : reasoner.getObjectProperties()) {
176                for (OWLClassExpression description : generatedDescriptions.get(length-2)) {
177                    thisLenDescriptions.add(dataFactory.getOWLObjectAllValuesFrom(objProp, description));
178                }
179            }
180            // add <generatedConcept> INTERSECT <atomicClass> for all concepts
181            // of length `length`-1
182            for (OWLClassExpression atomicConcept : reasoner.getAtomicConceptsList()) {
183                for (OWLClassExpression concept : generatedDescriptions.get(length-1)) {
184                    thisLenDescriptions.add(new OWLObjectIntersectionOfImplExt(
185                            Arrays.asList(concept, atomicConcept)));
186                }
187            }
188        }
189    }
190
191    public void setMaxLength(int length) {
192        maxLength = length;
193    }
194
195    public int getMaxLength() {
196        return maxLength;
197    }
198
199    private void evaluateGeneratedDefinition() {
200        double bestScoreVal = Double.NEGATIVE_INFINITY;
201        double tmpScoreVal;
202        ScorePosNeg tmpScore;
203        for (int i=1; i<=maxLength; i++) {
204            for (OWLClassExpression description : generatedDescriptions.get(i)) {
205                tmpScore = (ScorePosNeg) learningProblem.computeScore(description);
206                tmpScoreVal = tmpScore.getScoreValue();
207                if (tmpScoreVal > bestScoreVal) {
208                    bestDescription = description;
209                    bestScore = tmpScore;
210                    bestScoreVal = tmpScoreVal;
211                }
212            }
213        }
214    }
215}