001package org.dllearner.utilities;
002
003import com.google.common.collect.Sets;
004import org.apache.jena.graph.Node;
005import org.dllearner.core.AbstractReasonerComponent;
006import org.semanticweb.owlapi.model.*;
007
008import java.util.SortedSet;
009import java.util.TreeSet;
010import java.util.function.Function;
011import java.util.stream.Collectors;
012
013/**
014 * A collection of services that are known as non-standard reasoning.
015 *
016 * @author Lorenz Buehmann
017 */
018public class NonStandardReasoningServices {
019
020        /**
021         * Computes the least common subsumer (LCS) of the given entities <code>e1</code> and <code>e2</code> if exists,
022         * otherwise <code>null</code>
023         *
024         * @param reasoner the reasoner used to compute the direct parents
025         * @param n1 first entity
026         * @param n2 second entity
027         * @param entityType the entity type
028         * @param <E> the entity type
029         * @return the LCS if exists
030         */
031        public static <E extends OWLEntity> Node getLeastCommonSubsumer(AbstractReasonerComponent reasoner, Node n1, Node n2,
032                                                                                                                                        EntityType<E> entityType) {
033                // trivial case
034                if(n1.equals(n2)) {
035                        return n1;
036                }
037
038                E lcs = getLeastCommonSubsumer(reasoner,
039                                                                           OwlApiJenaUtils.asOWLEntity(n1, entityType),
040                                                                           OwlApiJenaUtils.asOWLEntity(n2, entityType));
041
042                if(lcs != null) {
043                        return OwlApiJenaUtils.asNode(lcs);
044                }
045
046                return null;
047        }
048
049        /**
050         * Computes the least common subsumer (LCS) of the given entities <code>e1</code> and <code>e2</code> if exists,
051         * otherwise <code>null</code>
052         *
053         * @param reasoner the reasoner used to compute the direct parents
054         * @param e1 first entity
055         * @param e2 second entity
056         * @param <E> the entity type
057         * @return the LCS if exists
058         */
059        @SuppressWarnings("unchecked")
060        public static <E extends OWLEntity> E getLeastCommonSubsumer(AbstractReasonerComponent reasoner, E e1, E e2) {
061                // check if both entities are of the same type
062                if(e1.getEntityType() != e2.getEntityType()) {
063                        throw new IllegalArgumentException("LCS operation only defined for entities of the same type!");
064                }
065
066                // trivial case
067                if(e1.equals(e2)) {
068                        return e1;
069                }
070
071                // depending on the entity type define the method to get the direct parents
072                EntityType<?> entityType = e1.getEntityType();
073                Function<E, SortedSet<E>> f;
074                if(entityType == EntityType.CLASS) {
075                        f = e -> (SortedSet<E>) reasoner.getSuperClasses((OWLClass) e).stream()
076                                                .filter(ce -> !ce.isAnonymous())
077                                                .map(OWLClassExpression::asOWLClass)
078                                                .collect(Collectors.toCollection(TreeSet::new));
079                } else if(entityType == EntityType.OBJECT_PROPERTY) {
080                        f = e -> (SortedSet<E>) reasoner.getSuperProperties((OWLObjectProperty) e);
081                } else if(entityType == EntityType.DATA_PROPERTY) {
082                        f = e -> (SortedSet<E>) reasoner.getSuperProperties((OWLDataProperty) e);
083                } else {
084                        throw new RuntimeException("LCS for " + entityType.getPluralPrintName() + " not implemented!");
085                }
086
087                // compute the LCS
088                return getLeastCommonSubsumer(e1, e2, f);
089        }
090
091        private static <E extends OWLEntity> E getLeastCommonSubsumer(E e1, E e2, Function<E, SortedSet<E>> f) {
092                // e1 = e2 -> e1
093                if(e1.equals(e2)) {
094                        return e1;
095                }
096
097                // e2 contained in direct parents(e1) -> e2
098                SortedSet<E> superEntities1 = f.apply(e1);
099                if(superEntities1.contains(e2)) {
100                        return e2;
101                }
102
103                // e1 contained in direct parents(e2) -> e1
104                SortedSet<E> superEntities2 = f.apply(e2);
105                if(superEntities2.contains(e1)) {
106                        return e1;
107                }
108
109                // parents(e1) ∩ parents(e2) != empty -> an element of the intersection
110                Sets.SetView<E> intersection = Sets.intersection(superEntities1, superEntities2);
111                if(!intersection.isEmpty()) {
112                        return intersection.iterator().next();
113                }
114
115                // recursive call using the parents
116                for (E sup1 : superEntities1) {
117                        for (E sup2 : superEntities2) {
118                                E lcs = getLeastCommonSubsumer(sup1, sup2, f);
119
120                                if(lcs != null) {
121                                        return lcs;
122                                }
123                        }
124                }
125
126                // no LCS found
127                return null;
128        }
129}