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