001    /**
002     * Copyright (C) 2007-2011, 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     */
019    
020    package org.dllearner.refinementoperators;
021    
022    import java.util.ArrayList;
023    import java.util.Collection;
024    import java.util.HashMap;
025    import java.util.HashSet;
026    import java.util.Iterator;
027    import java.util.LinkedList;
028    import java.util.List;
029    import java.util.Map;
030    import java.util.Set;
031    import java.util.SortedSet;
032    import java.util.Stack;
033    import java.util.TreeMap;
034    import java.util.TreeSet;
035    
036    import org.apache.log4j.Logger;
037    import org.dllearner.algorithms.el.ELDescriptionEdge;
038    import org.dllearner.algorithms.el.ELDescriptionEdgeComparator;
039    import org.dllearner.algorithms.el.ELDescriptionNode;
040    import org.dllearner.algorithms.el.ELDescriptionTree;
041    import org.dllearner.algorithms.el.ELDescriptionTreeComparator;
042    import org.dllearner.algorithms.el.TreeAndRoleSet;
043    import org.dllearner.algorithms.el.TreeAndRoleSetComparator;
044    import org.dllearner.core.AbstractReasonerComponent;
045    import org.dllearner.core.owl.Description;
046    import org.dllearner.core.owl.Intersection;
047    import org.dllearner.core.owl.NamedClass;
048    import org.dllearner.core.owl.ObjectProperty;
049    import org.dllearner.core.owl.ObjectPropertyHierarchy;
050    import org.dllearner.core.owl.ClassHierarchy;
051    import org.dllearner.core.owl.Thing;
052    import org.dllearner.utilities.Helper;
053    
054    //import com.jamonapi.Monitor;
055    //import com.jamonapi.MonitorFactory;
056    
057    /**
058     * EL downward refinement operator constructed by Jens Lehmann
059     * and Christoph Haase. It takes an EL description tree as input
060     * and outputs a set of EL description trees.
061     * 
062     * <p>Properties:
063     * <ul>
064     *   <li>weakly complete (can be extended to guarantee completeness if desired)</li>
065     *   <li>proper</li>
066     *   <li>finite</li>
067     *   <li>uses class/property hierarchy</li>
068     *   <li>takes domain/range into account</li>
069     *   <li>uses disjoint classes/classes without common instances</li>
070     *   <li>all refinements are minimal (i.e. cannot be shortened without changing semantics)</li>
071     * </ul>
072     * 
073     * @author Jens Lehmann
074     *
075     */
076    @SuppressWarnings("unused")
077    public class ELDown2 extends RefinementOperatorAdapter {
078    
079            private static Logger logger = Logger.getLogger(ELDown2.class); 
080            
081            private AbstractReasonerComponent rs;
082            
083            // hierarchies
084            private ClassHierarchy subsumptionHierarchy;
085            private ObjectPropertyHierarchy opHierarchy;
086            
087            // domains and ranges
088            private Map<ObjectProperty,Description> opDomains = new TreeMap<ObjectProperty,Description>();
089            private Map<ObjectProperty,Description> opRanges = new TreeMap<ObjectProperty,Description>();
090            
091            // app_A set of applicable properties for a given class
092            private Map<Description, Set<ObjectProperty>> app = new TreeMap<Description, Set<ObjectProperty>>();
093    
094            // most general applicable properties
095            private Map<Description,Set<ObjectProperty>> mgr = new TreeMap<Description,Set<ObjectProperty>>();
096    
097            // utility class
098            private Utility utility;
099            
100            // comparators
101            private ELDescriptionTreeComparator treeComp = new ELDescriptionTreeComparator();
102            private ELDescriptionEdgeComparator edgeComp = new ELDescriptionEdgeComparator();
103            private TreeAndRoleSetComparator mComp = new TreeAndRoleSetComparator();
104            
105            public ELDown2(AbstractReasonerComponent rs) {
106                    this(rs, true);
107            }
108            
109            public ELDown2(AbstractReasonerComponent rs, boolean instanceBasedDisjoints) {
110                    this.rs = rs;
111                    subsumptionHierarchy = rs.getClassHierarchy();
112                    opHierarchy = rs.getObjectPropertyHierarchy();
113                    
114                    // query reasoner for domains and ranges
115                    // (because they are used often in the operator)
116                    for(ObjectProperty op : rs.getObjectProperties()) {
117                            opDomains.put(op, rs.getDomain(op));
118                            opRanges.put(op, rs.getRange(op));
119                    }       
120                    
121                    utility = new Utility(rs, opDomains, instanceBasedDisjoints);
122            }
123            
124            /* (non-Javadoc)
125             * @see org.dllearner.refinementoperators.RefinementOperator#refine(org.dllearner.core.owl.Description)
126             */
127            @Override
128            public Set<Description> refine(Description concept) {
129                    logger.trace("refining " + concept);
130                    ELDescriptionTree tree = new ELDescriptionTree(rs, concept);
131                    List<ELDescriptionTree> refinementTrees = refine(tree);
132                    Set<Description> refinements = new HashSet<Description>();
133                    for(ELDescriptionTree refinementTree : refinementTrees) {
134                            refinements.add(refinementTree.transformToDescription());
135                    }
136                    return refinements;
137            }
138            
139            /**
140             * Performs downward refinement for the given tree. The operator
141             * works directly on EL description trees (which differ from the
142             * the tree structures build by descriptions).
143             * 
144             * @param tree Input EL description tree.
145             * @return Set of refined EL description trees.
146             */
147            public List<ELDescriptionTree> refine(ELDescriptionTree tree) {
148                    logger.trace("applying \\rho on " + tree.toDescriptionString());
149                    
150                    List<ELDescriptionTree> refinements = new LinkedList<ELDescriptionTree>();
151                    // loop over all nodes of the tree and perform one of the 
152                    // transformations on it (we make a copy of all nodes, because
153                    // the transformations can, of course, add new nodes)
154                    List<ELDescriptionNode> nodes = new LinkedList<ELDescriptionNode>(tree.getNodes());
155                    for(ELDescriptionNode v : nodes) {
156                            logger.trace("picked node v: " + v);
157                            
158                            // the position of the node within the tree (needed for getting
159                            // the corresponding node in a cloned tree) 
160                            int[] position = v.getCurrentPosition();        
161    //                      logger.trace("  at position " + Helper.arrayContent(position));
162                            
163                            // perform operations
164                            refinements.addAll(extendLabel(tree, v, position));
165                            refinements.addAll(refineLabel(tree, v, position));
166                            refinements.addAll(refineEdge(tree, v, position));
167                            refinements.addAll(attachSubtree2(tree, v, position));
168                    }
169                    
170                    return refinements;
171            }
172    
173            // operation 1: label extension
174            private List<ELDescriptionTree> extendLabel(ELDescriptionTree tree, ELDescriptionNode v, int[] position) {
175    //              Monitor mon = MonitorFactory.start("extend label");
176                    List<ELDescriptionTree> refinements = new LinkedList<ELDescriptionTree>();
177                                    
178                    // the index is the range of role in the edge pointing to the parent of this node
179                    Description index;
180                    if(v.isRoot()) {
181                            index = Thing.instance;
182                    } else {
183                            index = opRanges.get(v.getParentEdge().getLabel());
184                    }
185                    
186                    // call ncc (see paper)
187                    Set<NamedClass> candidates = utility.getClassCandidates(index, v.getLabel());
188                    
189    //              System.out.println("index: " + index + " label: " + v.getLabel());
190    //              System.out.println("candidates: " + candidates);
191                    
192                    for(NamedClass nc : candidates) {
193                            // clone operation
194                            ELDescriptionTree clonedTree = tree.clone();
195                            ELDescriptionNode clonedNode = clonedTree.getNode(position);
196                            // extend label
197                            clonedNode.extendLabel(nc);
198                            if(clonedTree.isMinimal()) {
199                                    refinements.add(clonedTree);    
200                            }
201                    }
202                                    
203    //              mon.stop();
204                    return refinements;
205            }       
206            
207            // operation 2: label refinement
208            private List<ELDescriptionTree> refineLabel(ELDescriptionTree tree, ELDescriptionNode v, int[] position) {
209    //              Monitor mon = MonitorFactory.start("refine label");
210                    List<ELDescriptionTree> refinements = new LinkedList<ELDescriptionTree>();
211                    
212                    // loop through all classes in label
213                    for(NamedClass nc : v.getLabel()) {
214                            // find all more special classes for the given label
215                            for(Description moreSpecial : rs.getSubClasses(nc)) {
216                                    if(moreSpecial instanceof NamedClass) {
217                                            // clone operation
218                                            ELDescriptionTree clonedTree = tree.clone();
219                                            ELDescriptionNode clonedNode = clonedTree.getNode(position);
220                                            
221                                            // create refinements by replacing class                                        
222                                            clonedNode.replaceInLabel(nc, (NamedClass) moreSpecial);
223                                            
224                                            if(clonedTree.isMinimal()) {
225                                                    refinements.add(clonedTree);    
226                                            }
227                                    }
228                            }
229                    }
230    //              mon.stop();
231                    return refinements;
232            }       
233            
234            // operation 3: refine edge
235            private List<ELDescriptionTree> refineEdge(ELDescriptionTree tree, ELDescriptionNode v, int[] position) {
236    //              Monitor mon = MonitorFactory.start("refine edge");
237                    List<ELDescriptionTree> refinements = new LinkedList<ELDescriptionTree>();
238    
239                    for(int edgeNumber = 0; edgeNumber < v.getEdges().size(); edgeNumber++) {
240                            ELDescriptionEdge edge = v.getEdges().get(edgeNumber);
241                            ObjectProperty op = edge.getLabel();
242                            // find all more special properties
243                            for(ObjectProperty op2 : rs.getSubProperties(op)) {
244                                    // we check whether the range of this property is not disjoint
245                                    // with the existing child node (we do not perform a full disjointness
246                                    // check, but only compare with the flattened concept to keep the number
247                                    // of possible disjointness checks finite)
248                                    if(!utility.isDisjoint(getFlattenedConcept(edge.getNode()), opRanges.get(op2))) {
249                                            // clone operation
250                                            ELDescriptionTree clonedTree = tree.clone();
251                                            // find cloned edge and replace its label
252                                            clonedTree.getNode(position).refineEdge(edgeNumber, op2);
253    //                                      ELDescriptionEdge clonedEdge = clonedTree.getNode(position).getEdges().get(edgeNumber);
254    //                                      clonedEdge.setLabel(op2);
255                                            if(clonedTree.isMinimal()) {
256                                                    refinements.add(clonedTree);    
257                                            }
258                                    }
259                            }       
260                    }               
261    //              mon.stop();
262                    return refinements;
263            }
264            
265            // operation 4: attach tree
266            private Collection<ELDescriptionTree> attachSubtree(ELDescriptionTree tree, ELDescriptionNode v, int[] position) {
267    //              Monitor mon = MonitorFactory.start("attach tree");
268                    List<ELDescriptionTree> refinements = new LinkedList<ELDescriptionTree>();
269                    
270                    // compute the set of most general roles such that the domain of each role is not disjoint
271                    // with the range of the role pointing to this node
272                    Description index;
273                    if(v.isRoot()) {
274                            index = Thing.instance;
275                    } else {
276                            index = opRanges.get(v.getParentEdge().getLabel());
277                    }
278                    
279                    SortedSet<ObjectProperty> appOPs = utility.computeApplicableObjectProperties(index);
280                    
281                    Set<ObjectProperty> mgr = utility.computeMgr(appOPs);
282                    
283                    // loop through most general roles
284                    for(ObjectProperty op : mgr) {
285                            logger.trace("pick most general role: " + op);
286                            
287                            // a list of subtrees (stored as edges i.e. role + root node which points to tree)
288    //                      LinkedList<ELDescriptionEdge> m = new LinkedList<ELDescriptionEdge>();
289                            // we must store m as set, otherwise we get duplicates
290                            TreeSet<ELDescriptionEdge> m = new TreeSet<ELDescriptionEdge>(edgeComp);
291                            
292                            // create tree corresponding to top node
293                            ELDescriptionTree topTree = new ELDescriptionTree(rs, Thing.instance);
294                            
295                            // init list with picked role and top node i.e. its root
296                            m.add(new ELDescriptionEdge(op, topTree.getRootNode()));
297                            
298                            // iterate until m is empty
299                            while(!m.isEmpty()) {
300                                    // pick and remove first element
301                                    ELDescriptionEdge edge = m.pollFirst();
302                                    logger.trace("picked first element of M: " + edge);
303                                    ObjectProperty r = edge.getLabel();
304                                    // tp = t' in algorithm description (p stands for prime)
305                                    ELDescriptionTree tp = edge.getNode().getTree();
306                                    
307                                    // merge tree into main tree
308                                    ELDescriptionTree mergedTree = mergeTrees(tree, v, position, r, tp);
309                                    
310                                    // the position of w is the position of v + #edges outgoing from v
311                                    int[] wPosition = new int[position.length+1];
312                                    System.arraycopy(position, 0, wPosition, 0, position.length);
313                                    wPosition[position.length] = v.getEdges().size();       
314                                    
315                                    ELDescriptionNode wClone = mergedTree.getNode(wPosition);
316                                    
317                                    logger.trace("merged to t_{C'}: \n" + mergedTree);
318                                    
319                                    // we check equivalence by a minimality test (TODO: can we still do this?)
320                                    boolean minimal = mergedTree.isMinimal();
321    //                              MonitorFactory.add("as.minimal", "boolean", minimal ? 1 : 0);
322                                    if(minimal) {
323                                            logger.trace("Merged tree is minimal, i.e. not equivalent.");
324                                            // it is not equivalent, i.e. we found a refinement
325                                            refinements.add(mergedTree);
326                                    } else {                                        
327                                            logger.trace("Merged tree is not minimal, i.e. equivalent.");
328                                            // perform complex check in merged tree
329                                            boolean check = asCheck(wClone);
330                                            logger.trace("Result of complex check: " + check);
331    //                                      MonitorFactory.add("as.check", "boolean", check ? 1 : 0);
332                                            
333                                            if(check) {
334                                                    // refine property
335                                                    for(ObjectProperty subRole : rs.getSubProperties(r)) {
336                                                            m.add(new ELDescriptionEdge(subRole, tp.getRootNode()));
337                                                    }
338                                                    // refine tree using recursive operator call
339                                                    logger.trace("Recursive Call");
340                                                    // do not monitor recursive calls (counts time twice or more)
341    //                                              mon.stop();
342                                                    List<ELDescriptionTree> recRefs = refine(tp);
343    //                                              mon.start();
344                                                    logger.trace("Recursive Call Done");
345                                                    for(ELDescriptionTree tpp : recRefs) {
346                                                            m.add(new ELDescriptionEdge(r, tpp.getRootNode()));
347                                                    }
348                                            }
349                                    }
350                                    
351                                    logger.trace("M: " + m);
352                            }
353                    }
354    //              mon.stop();
355                    return refinements;
356            }       
357            
358            // new version of as
359            private Collection<ELDescriptionTree> attachSubtree2(ELDescriptionTree tree, ELDescriptionNode v, int[] position) {
360    //              Monitor mon = MonitorFactory.start("attach tree");
361                    Set<ELDescriptionTree> refinements = new TreeSet<ELDescriptionTree>(treeComp);
362                    
363                    // create and initialise M
364                    TreeSet<TreeAndRoleSet> m = new TreeSet<TreeAndRoleSet>(mComp);
365                    ELDescriptionTree topTree = new ELDescriptionTree(rs, Thing.instance);
366                    Description index = getIndex(v);
367                    SortedSet<ObjectProperty> appOPs = utility.computeApplicableObjectProperties(index);
368                    m.add(new TreeAndRoleSet(topTree, appOPs));
369                    
370    //              logger.trace("M initialised: " + m);
371                    
372                    while(!m.isEmpty()) {
373                            
374                            // pick first element of M
375                            TreeAndRoleSet tars = m.pollFirst();
376                            ELDescriptionTree tp = tars.getTree();
377                            Set<ObjectProperty> rSet = tars.getRoles();
378    //                      logger.trace("selected first element of M: " + tars);
379                            
380                            
381                            // init sets R' and R''
382                            // more efficient
383                            SortedSet<ObjectProperty> rpSet = utility.computeMgr(appOPs);
384                            rpSet.retainAll(rSet);
385    //                      SortedSet<ObjectProperty> rpSet = new TreeSet<ObjectProperty>();
386    //                      for(ObjectProperty rEl : rSet) {
387    //                              if(!containsSuperProperty(rEl, rSet)) {
388    //                                      rpSet.add(rEl);
389    //                              }
390    //                      }
391                            
392    //                      logger.trace("R': " + rpSet);
393                            Set<ObjectProperty> rppSet = new TreeSet<ObjectProperty>();
394                            
395                            while(!rpSet.isEmpty()) {
396                                    // pick an element r from R'
397                                    Iterator<ObjectProperty> it = rpSet.iterator();
398                                    ObjectProperty r = it.next();
399                                    it.remove();
400    //                              logger.trace("picked role r: " + r);
401                                    
402                                    ELDescriptionTree tpp = mergeTrees(tree, v, position, r, tp);
403    //                              logger.trace("merged tree:\n" + tpp);
404                                    // the position of w is the position of v + #edges outgoing from v
405                                    int[] wPosition = new int[position.length+1];
406                                    System.arraycopy(position, 0, wPosition, 0, position.length);
407                                    wPosition[position.length] = v.getEdges().size();
408                                    ELDescriptionNode w = tpp.getNode(wPosition);                           
409                                    
410                                    boolean minimal = tpp.isMinimal();
411    //                              MonitorFactory.add("as.minimal", "boolean", minimal ? 1 : 0);
412                                    if(minimal) {
413                                            refinements.add(tpp);
414    //                                      logger.trace("tree is minimal; added to T");
415                                    } else {
416                                            boolean check = asCheck(w);
417    //                                      MonitorFactory.add("as.check", "boolean", check ? 1 : 0);                                       
418    //                                      logger.trace("tree is not minimal; result of complex check: " + check);
419                                            
420                                            if(check) {
421                                                    
422    //                                              Monitor mon2 = MonitorFactory.start("as.tmp");
423                                                    // add role to R' if it is in R (allowed)
424                                                    for(ObjectProperty subRole : rs.getSubProperties(r)) {
425                                                            if(rSet.contains(subRole)) {
426                                                                    rpSet.add(subRole);
427                                                            }
428                                                    }
429                                                    rppSet.add(r);
430    //                                              logger.trace("updated R' to: " + rpSet);
431    //                                              logger.trace("updated R'' to: " + rppSet);
432    //                                              mon2.stop();
433                                            }
434                                    }
435                            }
436                            
437                            if(rppSet.size() != 0) {
438                                    // recursive call
439    //                              mon.stop();
440    //                              logger.trace("recursive call start");
441                                    List<ELDescriptionTree> recRefs = refine(tp);
442    //                              logger.trace("recursive call end");
443    //                              mon.start();                            
444                                    
445                                    for(ELDescriptionTree tStar : recRefs) {
446                                            m.add(new TreeAndRoleSet(tStar, rppSet));
447                                    }       
448    //                              logger.trace("M after recursion: " + m);
449                            }
450                                    
451                    }
452                    
453    //              mon.stop();
454                    return refinements;             
455            }
456                            
457            
458            // create a new tree which is obtained by attaching the new tree at the given node in the tree via role r
459            private ELDescriptionTree mergeTrees(ELDescriptionTree tree, ELDescriptionNode node, int[] position, ObjectProperty r, ELDescriptionTree newTree) {
460    //              Monitor mon = MonitorFactory.start("as.merge trees");
461    //              System.out.println("merge start");
462    //              System.out.println(tree);
463    //              System.out.println(newTree);
464                    // merged tree = tree + new node with role pointing to a new node
465                    ELDescriptionTree mergedTree = tree.clone();
466                    ELDescriptionNode clonedNode = mergedTree.getNode(position);
467    //              ELDescriptionNode nodeNew = new ELDescriptionNode(clonedNode, r);
468    //              logger.trace("node: " + node);
469    //              logger.trace("cloned node: " + clonedNode);
470    //              logger.trace("node position: " + arrayContent(position));
471    //              logger.trace("merge start: " + mergedTree);
472                    
473                    // create a list of nodes we still need to process
474                    LinkedList<ELDescriptionNode> toProcess = new LinkedList<ELDescriptionNode>();
475                    toProcess.add(newTree.getRootNode());
476                    
477                    // map from nodes to cloned nodes
478                    Map<ELDescriptionNode,ELDescriptionNode> cloneMap = new HashMap<ELDescriptionNode,ELDescriptionNode>();
479                    
480    //              Monitor mon2 = MonitorFactory.start("as.tmp");
481                    
482                    // loop until the process list is empty
483                    while(!toProcess.isEmpty()) {
484                            // process a node
485                            ELDescriptionNode v = toProcess.pollFirst();
486                            // find parent
487                            ELDescriptionNode vp;
488                            if(v.isRoot()) {
489                                    // root is connected to main tree via role r
490                                    vp = new ELDescriptionNode(clonedNode, r, newTree.getRootNode().getLabel());
491                            } else {
492                                    ELDescriptionNode parent = cloneMap.get(v.getParent());
493                                    ObjectProperty role = v.getParentEdge().getLabel();
494                                    Set<NamedClass> label = v.getLabel();
495                                    // create new node
496                                    vp = new ELDescriptionNode(parent, role, label);                                
497                            }
498                            cloneMap.put(v, vp);
499                            // attach children of node to process list
500                            for(ELDescriptionEdge edge : v.getEdges()) {
501                                    toProcess.add(edge.getNode());
502                            }
503                    }
504                    
505    //              mon2.stop();
506                    
507    //              mon.stop();
508                    return mergedTree;
509            }
510            
511            // TODO: variables have been renamed in article
512            public boolean asCheck(ELDescriptionNode v) {
513    //              Monitor mon = MonitorFactory.start("as.complex check");
514    //              System.out.println("asCheck: " + v.getTree().toSimulationString());
515                    
516                    // find all edges up to the root node
517                    List<ELDescriptionEdge> piVEdges = new LinkedList<ELDescriptionEdge>();
518                    ELDescriptionNode tmp = v;
519                    while(!tmp.isRoot()) {
520                            piVEdges.add(tmp.getParentEdge());
521                            tmp = tmp.getParent();
522                    }
523                    
524    //              System.out.println(piVEdges);
525                    
526                    // go through all edges
527                    for(ELDescriptionEdge piVEdge : piVEdges) {
528                            // collect (w,r',w')
529                            ELDescriptionNode wp = piVEdge.getNode();
530                            ObjectProperty rp = piVEdge.getLabel();
531                            ELDescriptionNode w = wp.getParent();
532                            
533    //                      System.out.println("w: " + w);
534    //                      System.out.println("rp: " + rp);
535    //                      System.out.println("wp: " + wp);
536                            
537                            // go through all (w,s,w'')
538                            for(ELDescriptionEdge wEdge : w.getEdges()) {
539                                    ObjectProperty rpp = wEdge.getLabel();
540                                    ELDescriptionNode wpp = wEdge.getNode();
541                                    if(wp != wpp && opHierarchy.isSubpropertyOf(rp, rpp)) {
542    //                                      System.out.println("wp: " + wp);
543    //                                      System.out.println("wpp: " + wpp);
544                                            if(wp.getIn().contains(wpp)) {
545                                                    return false;
546                                            }
547                                    }
548                            }
549                    }
550                    
551    //              mon.stop();
552                    return true;
553            }
554            
555            // simplifies a potentially nested tree in a flat conjunction by taking
556            // the domain of involved roles, e.g. for
557            // C = Professor \sqcap \exists hasChild.Student
558            // the result would be Professor \sqcap Human (assuming Human is the domain
559            // of hasChild)
560            // TODO: used in both EL operators => move to utility class
561            private Description getFlattenedConcept(ELDescriptionNode node) {
562                    Intersection i = new Intersection();
563                    
564                    // add all named classes to intersection
565                    for(NamedClass nc : node.getLabel()) {
566                            i.addChild(nc);
567                    }
568                    // add domain of all roles to intersection
569                    for(ELDescriptionEdge edge : node.getEdges()) {
570                            i.addChild(opDomains.get(edge.getLabel()));
571                    }
572                    
573                    int size = i.getChildren().size();
574                    // size = 0 means we have the top concept
575                    if(size == 0) {
576                            return Thing.instance;
577                    }
578                    // if the intersection has just one element, we return
579                    // the element itself instead
580                    else if(size == 1) {
581                            return i.getChild(0);
582                    }
583                    
584                    return i;
585            }       
586            
587            private Description getIndex(ELDescriptionNode v) {
588                    if(v.isRoot()) {
589                            return Thing.instance;
590                    } else {
591                            return opRanges.get(v.getParentEdge().getLabel());
592                    }               
593            }
594            
595            private boolean containsSuperProperty(ObjectProperty prop, Set<ObjectProperty> props) {
596                    for(ObjectProperty p : props) {
597                            if(!p.equals(prop)) {
598                                    if(opHierarchy.isSubpropertyOf(prop, p)) {
599                                            return true;
600                                    }                                               
601                            }
602                    }
603                    return false;
604            }
605            
606    }