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.core.owl;
020
021import com.google.common.collect.Sets;
022import org.semanticweb.owlapi.model.OWLObject;
023import org.slf4j.Logger;
024import org.slf4j.LoggerFactory;
025
026import java.util.Map.Entry;
027import java.util.*;
028
029/**
030 * @author Lorenz Buehmann
031 *
032 */
033public abstract class AbstractHierarchy<T extends OWLObject> implements Hierarchy<T> {
034        
035        private static final Logger logger = LoggerFactory.getLogger(AbstractHierarchy.class);
036        
037        private SortedMap<T, SortedSet<T>> hierarchyUp;
038        private SortedMap<T, SortedSet<T>> hierarchyDown;
039        
040        private SortedSet<T> rootEntities = new TreeSet<>();
041        private SortedSet<T> leafEntities = new TreeSet<>();
042        
043
044        public AbstractHierarchy(SortedMap<T, SortedSet<T>> hierarchyUp, SortedMap<T, SortedSet<T>> hierarchyDown) {
045                this.hierarchyUp = hierarchyUp;
046                this.hierarchyDown = hierarchyDown;
047                
048                // find most general and most special entities
049                for (T entity : Sets.union(hierarchyUp.keySet(), hierarchyDown.keySet())) {
050                        SortedSet<T> moreGen = getParents(entity);
051                        SortedSet<T> moreSpec = getChildren(entity);
052
053                        if (moreGen.size() == 0 || (moreGen.size() == 1 && moreGen.first().isTopEntity()))
054                                rootEntities.add(entity);
055
056                        if (moreSpec.size() == 0 || (moreSpec.size() == 1 && moreSpec.first().isBottomEntity()))
057                                leafEntities.add(entity);
058                }
059        }
060        
061        /* (non-Javadoc)
062         * @see org.dllearner.core.owl.Hierarchy#getChildren(org.semanticweb.owlapi.model.OWLEntity)
063         */
064        @Override
065        public SortedSet<T> getChildren(T entity) {
066                return getChildren(entity, true);
067        }
068
069        /**
070         * @return all entities in this hierarchy
071         */
072        public Set<T> getEntities() {
073                return Sets.union(hierarchyDown.keySet(), hierarchyUp.keySet());
074        }
075
076        /* (non-Javadoc)
077         * @see org.dllearner.core.owl.Hierarchy#getChildren(org.semanticweb.owlapi.model.OWLEntity, boolean)
078         */
079        @Override
080        public SortedSet<T> getChildren(T entity, boolean direct) {
081                SortedSet<T> result =  hierarchyDown.get(entity);
082                
083                if(result == null) {
084                        logger.debug("Query for " + entity + " in hierarchy, but the entity is not contained in the (downward) hierarchy, e.g. because the entity does not exist or is ignored. Returning empty result instead.");
085                        return new TreeSet<>();
086                }
087                
088                result.remove(entity);
089                if(!direct) { // get transitive children
090                        SortedSet<T> tmp = new TreeSet<>();
091                        for(T child : result){
092                                tmp.addAll(getChildren(child, direct));
093                        }
094                        result.addAll(tmp);
095                }
096                return new TreeSet<>(result);
097        }
098
099        /* (non-Javadoc)
100         * @see org.dllearner.core.owl.Hierarchy#getParents(org.semanticweb.owlapi.model.OWLEntity)
101         */
102        @Override
103        public SortedSet<T> getParents(T entity) {
104                return getParents(entity, true);
105        }
106
107        /* (non-Javadoc)
108         * @see org.dllearner.core.owl.Hierarchy#getParents(org.semanticweb.owlapi.model.OWLEntity, boolean)
109         */
110        @Override
111        public SortedSet<T> getParents(T entity, boolean direct) {
112                SortedSet<T> result =  hierarchyUp.get(entity);
113                
114                if(result == null) {
115                        logger.debug("Query for " + entity + " in hierarchy, but the entity is not contained in the (upward) hierarchy, e.g. because the entity does not exist or is ignored. Returning empty result instead.");
116                        return new TreeSet<>();
117                }
118                
119                result.remove(entity);
120                if(!direct) {
121                        SortedSet<T> tmp = new TreeSet<>();
122                        for(T parent : result){
123                                tmp.addAll(getParents(parent, direct));
124                        }
125                        result.addAll(tmp);
126                }
127                
128                return new TreeSet<>(result);
129        }
130
131        /* (non-Javadoc)
132         * @see org.dllearner.core.owl.Hierarchy#getSiblings(org.semanticweb.owlapi.model.OWLEntity)
133         */
134        @Override
135        public SortedSet<T> getSiblings(T entity) {
136                SortedSet<T> siblings = new TreeSet<>();
137                
138                Set<T> parents = getParents(entity);
139                for(T parent : parents) {
140                        siblings.addAll(getChildren(parent));
141                }
142                
143                siblings.remove(entity);
144                return siblings;
145        }
146
147        /* (non-Javadoc)
148         * @see org.dllearner.core.owl.Hierarchy#isChildOf(org.semanticweb.owlapi.model.OWLEntity, org.semanticweb.owlapi.model.OWLEntity)
149         */
150        @Override
151        public boolean isChildOf(T entity1, T entity2) {
152                if (entity1.equals(entity2)) {
153                        return true;
154                } else {
155                        SortedSet<T> parents = getParents(entity1);
156                        
157                        if(parents != null){
158                                // search the upper classes of the subclass
159                                for (T parent : parents) {
160                                        if (isChildOf(parent, entity2)) {
161                                                return true;
162                                        }
163                                }
164                        }
165                        // we cannot reach the class via any of the upper classes,
166                        // so it is not a super class
167                        return false;
168                }
169        }
170
171        /* (non-Javadoc)
172         * @see org.dllearner.core.owl.Hierarchy#isParentOf(org.semanticweb.owlapi.model.OWLClassExpression, org.semanticweb.owlapi.model.OWLClassExpression)
173         */
174        @Override
175        public boolean isParentOf(T entity1, T entity2) {
176                return isChildOf(entity2, entity1);
177        }
178
179        /* (non-Javadoc)
180         * @see org.dllearner.core.owl.Hierarchy#getRoots()
181         */
182        @Override
183        public SortedSet<T> getRoots() {
184                SortedSet<T> roots = new TreeSet<>();
185                
186                for(T child : getChildren(getTopConcept())){
187                        SortedSet<T> parents = getParents(child);
188                        parents.remove(getTopConcept());
189                        
190                        if(parents.isEmpty()){
191                                roots.add(child);
192                        }
193                }
194                return roots;
195        }
196        
197        /**
198         * @return The most general entites.
199         */
200        public SortedSet<T> getMostGeneralEntities() {
201                return rootEntities;
202        }
203
204        /**
205         * @return The most special roles.
206         */
207        public SortedSet<T> getMostSpecialEntities() {
208                return leafEntities;
209        }
210        
211        /* (non-Javadoc)
212         * @see org.dllearner.core.owl.Hierarchy#contains(org.semanticweb.owlapi.model.OWLObject)
213         */
214        @Override
215        public boolean contains(T entity) {
216                return hierarchyUp.containsKey(entity);
217        }
218        
219        /**
220         * This method modifies the subsumption hierarchy such that for each class,
221         * there is only a single path to reach it via upward and downward
222         * refinement respectively.
223         */
224        public void thinOutSubsumptionHierarchy() {
225                SortedMap<T, SortedSet<T>> hierarchyDownNew = new TreeMap<>();
226                SortedMap<T, SortedSet<T>> hierarchyUpNew = new TreeMap<>();
227
228                Set<T> conceptsInSubsumptionHierarchy = new TreeSet<>();
229                conceptsInSubsumptionHierarchy.addAll(hierarchyUp.keySet());
230                conceptsInSubsumptionHierarchy.addAll(hierarchyDown.keySet());
231                
232                // add empty sets for each concept
233                for (T c : conceptsInSubsumptionHierarchy) {
234                        hierarchyDownNew.put(c, new TreeSet<>());
235                        hierarchyUpNew.put(c, new TreeSet<>());
236                }
237
238                for (T c : conceptsInSubsumptionHierarchy) {
239                        // look whether there are more general concepts
240                        // (if yes, pick the first one)
241                        SortedSet<T> moreGeneral = getParents(c);
242                        if (moreGeneral != null && !moreGeneral.isEmpty()) {
243                                T chosenParent = moreGeneral.first();
244                                hierarchyDownNew.get(chosenParent).add(c);
245                        }
246                }
247
248                for (T c : conceptsInSubsumptionHierarchy) {
249                        SortedSet<T> moreSpecial = getChildren(c);
250                        if (moreSpecial != null && !moreSpecial.isEmpty()) {
251                                T chosenChild = moreSpecial.first();
252                                hierarchyUpNew.get(chosenChild).add(c);
253                        }
254                }
255                
256                // top node
257                hierarchyDownNew.put(getTopConcept(), getChildren(getTopConcept()));
258                
259                // bottom node
260                hierarchyUpNew.put(getBottomConcept(), getParents(getBottomConcept()));
261                
262                setHierarchyDown(hierarchyDownNew);
263                setHierarchyUp(hierarchyUpNew);
264        }
265        
266        /**
267         * The method computes a new subsumption hierarchy, which is a copy of this
268         * one, but only the specified entities are allowed to occur. For instance,
269         * if we have subclass relationships between 1sYearStudent, Student, and
270         * Person, but Student is not allowed, then there a is a subclass relationship
271         * between 1stYearStudent and Person.
272         * Currently, owl:Thing and owl:Nothing are always allowed for technical
273         * reasons.
274         * @param allowedEntities The entities, which are allowed to occur in the new
275         * subsumption hierarchy.
276         * @return A copy of this hierarchy, which is restricted to a certain set
277         * of entities.
278         */
279        public AbstractHierarchy<T> cloneAndRestrict(Set<? extends T> allowedEntities) {
280                // currently TOP and BOTTOM are always allowed
281                // (TODO would be easier if Thing/Nothing were declared as named classes)
282                Set<T> allowed = new TreeSet<>();
283                allowed.addAll(allowedEntities);
284                allowed.add(getTopConcept());
285                allowed.add(getBottomConcept());
286                
287                // create new maps
288                SortedMap<T, SortedSet<T>> subsumptionHierarchyUpNew = new TreeMap<>();
289                SortedMap<T, SortedSet<T>> subsumptionHierarchyDownNew = new TreeMap<>();
290                
291                for(Entry<T, SortedSet<T>> entry : hierarchyUp.entrySet()) {
292                        T key = entry.getKey();
293                        // we only store mappings for allowed entities
294                        if(allowed.contains(key)) {
295                                // copy the set of all parents (we consume them until
296                                // they are empty)
297                                TreeSet<T> parents = new TreeSet<>(entry.getValue());
298                                // storage for new parents
299                                TreeSet<T> newParents = new TreeSet<>();
300                                
301                                while(!parents.isEmpty()) {
302                                        // pick and remove the first element
303                                        T d = parents.pollFirst();
304                                        // case 1: it is allowed, so we add it
305                                        if(allowed.contains(d)) {
306                                                newParents.add(d);
307                                        // case 2: it is not allowed, so we try its super classes
308                                        } else {
309                                                Set<T> tmp = hierarchyUp.get(d);
310                                                if(tmp != null){
311                                                        parents.addAll(tmp);
312                                                }
313                                        }
314                                }
315                                
316                                subsumptionHierarchyUpNew.put(key, newParents);
317                        }
318                }
319                
320                // downward case is analogous
321                for(Entry<T, SortedSet<T>> entry : hierarchyDown.entrySet()) {
322                        T key = entry.getKey();
323                        if(allowed.contains(key)) {
324                                TreeSet<T> children = new TreeSet<>(entry.getValue());
325                                TreeSet<T> newChildren = new TreeSet<>();
326                                
327                                while(!children.isEmpty()) {
328                                        T d = children.pollFirst();
329                                        if(allowed.contains(d)) {
330                                                newChildren.add(d);
331                                        } else {
332                                                SortedSet<T> tmp = hierarchyDown.get(d);
333                                                if(tmp != null) {
334                                                        children.addAll(tmp);
335                                                }
336                                        }
337                                }
338                                
339                                subsumptionHierarchyDownNew.put(key, newChildren);
340                        }
341                }               
342
343                try {
344                        return this.getClass().getConstructor(
345                                        SortedMap.class, SortedMap.class).newInstance(
346                                                        subsumptionHierarchyUpNew, subsumptionHierarchyDownNew);
347                } catch (Exception e) {
348                        e.printStackTrace();
349                }
350                return null;
351        }
352        
353        /**
354         * @param hierarchyUp the hierarchyUp to set
355         */
356        public void setHierarchyUp(SortedMap<T, SortedSet<T>> hierarchyUp) {
357                this.hierarchyUp = hierarchyUp;
358        }
359        
360        /**
361         * @return the hierarchyUp
362         */
363        public SortedMap<T, SortedSet<T>> getHierarchyUp() {
364                return hierarchyUp;
365        }
366        
367        /**
368         * @param hierarchyDown the hierarchyDown to set
369         */
370        public void setHierarchyDown(SortedMap<T, SortedSet<T>> hierarchyDown) {
371                this.hierarchyDown = hierarchyDown;
372        }
373        
374        /**
375         * @return the hierarchyDown
376         */
377        public SortedMap<T, SortedSet<T>> getHierarchyDown() {
378                return hierarchyDown;
379        }
380        
381        public void precompute() {
382                
383        }
384        
385        @Override
386        public String toString() {
387                return toString(false);
388        }
389        
390        public String toString(boolean showUpwardHierarchy) {
391                if (showUpwardHierarchy) {
392                        String str = "downward subsumption:\n";
393                        str += toString(hierarchyDown, getTopConcept(), 0);
394                        str += "upward subsumption:\n";
395                        str += toString(hierarchyUp, getBottomConcept(), 0);
396                        return str;
397                } else {
398                        return toString(hierarchyDown, getTopConcept(), 0);
399                }
400        }
401        
402        protected String toString(SortedMap<T, SortedSet<T>> hierarchy, T concept, int depth) {
403                String str = "";
404                for (int i = 0; i < depth; i++)
405                        str += "  ";
406                str += concept.toString() + "\n";
407                Set<T> tmp = hierarchy.get(concept);
408                if (tmp != null) {
409                        for (T c : tmp)
410                                str += toString(hierarchy, c, depth + 1);
411                }
412                return str;
413        }
414        
415        public abstract T getTopConcept();
416        public abstract T getBottomConcept();
417
418}