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 com.google.common.collect.Lists;
022import org.dllearner.core.AbstractReasonerComponent;
023import org.dllearner.core.ComponentInitException;
024import org.dllearner.core.owl.OWLObjectIntersectionOfImplExt;
025import org.dllearner.core.owl.OWLObjectUnionOfImplExt;
026import org.dllearner.learningproblems.PosNegLP;
027import org.semanticweb.owlapi.model.*;
028
029import java.util.*;
030
031/**
032 * Operatoren Psi-Down und Psi-Up müssen noch so umgeschrieben werden, dass sie
033 * nur konsistente Konzepte (mit korrekten parent-Links) enthalten. Dazu müssen
034 * alle verwendeten atomaren Konzepte geklont werden. 
035 * 
036 * Außerdem erscheint es ratsam weitere konzeptverkürzende Maßnahmen einzuführen,
037 * z.B. EXISTS r.A => BOTTOM für down bzw. TOP für up
038 * => Konzepte erreichen etwa eine Länge von 20
039 * 
040 * @author jl
041 *
042 */
043public class PsiDown extends RefinementOperatorAdapter {
044
045        PosNegLP learningProblem;
046        AbstractReasonerComponent reasoningService;
047        
048        private TreeSet<OWLClassExpression> topSet;
049        
050        public PsiDown(PosNegLP learningProblem, AbstractReasonerComponent reasoningService) {
051                this.learningProblem = learningProblem;
052                this.reasoningService = reasoningService;
053                
054                // Top-Menge erstellen
055                createTopSet();
056        }
057        
058        private void createTopSet() {
059                topSet = new TreeSet<>();
060                
061                // TOP OR TOP => Was soll mit Refinements passieren, die immer improper sind?
062                List<OWLClassExpression> operands = Lists.<OWLClassExpression>newArrayList(df.getOWLThing(), df.getOWLThing());
063                OWLObjectUnionOf md = new OWLObjectUnionOfImplExt(operands);
064                topSet.add(md);
065                
066                // allgemeinste Konzepte
067                topSet.addAll(reasoningService.getSubClasses(df.getOWLThing()));
068                
069                // negierte speziellste Konzepte
070                Set<OWLClassExpression> tmp = reasoningService.getSuperClasses(df.getOWLNothing());
071                for(OWLClassExpression c : tmp) 
072                        topSet.add(df.getOWLObjectComplementOf(c));
073        
074                // EXISTS r.TOP und ALL r.TOP für alle r
075                for(OWLObjectProperty r : reasoningService.getObjectProperties()) {
076                        topSet.add(df.getOWLObjectAllValuesFrom(r, df.getOWLThing()));
077                        topSet.add(df.getOWLObjectSomeValuesFrom(r, df.getOWLThing()));
078                }               
079        }
080        
081        @Override
082        @SuppressWarnings("unchecked")
083        public Set<OWLClassExpression> refine(OWLClassExpression concept) {
084                
085                Set<OWLClassExpression> refinements = new HashSet<>();
086                Set<OWLClassExpression> tmp;
087                
088                if (concept.isOWLThing()) {
089                        return (Set<OWLClassExpression>) topSet.clone();
090                } else if (concept.isOWLNothing()) {
091                        return new HashSet<>();
092                } else if (!concept.isAnonymous()) {
093                        // beachte: die Funktion gibt bereits nur nicht-äquivalente Konzepte zurück
094                        // beachte weiter: die zurückgegebenen Instanzen dürfen nicht verändert werden,
095                        // da beim Caching der Subsumptionhierarchie (momentan) keine Kopien gemacht werden
096                        // Bottom wird hier ggf. automatisch mit zurückgegeben
097                        refinements.addAll(reasoningService.getSubClasses(concept));
098                // negiertes atomares Konzept
099                } else if (concept instanceof OWLObjectComplementOf) {
100                        OWLClassExpression operand = ((OWLObjectComplementOf) concept).getOperand();
101                        if(!operand.isAnonymous()){
102                                tmp = reasoningService.getSuperClasses(operand);
103                                
104                                for(OWLClassExpression c : tmp) {
105                                        if(!c.isOWLThing()){
106                                                refinements.add(df.getOWLObjectComplementOf(c));
107                                        }
108                                }
109                        }
110                } else if (concept instanceof OWLObjectIntersectionOf) {
111                        List<OWLClassExpression> operands = ((OWLObjectIntersectionOf) concept).getOperandsAsList();
112                        // refine one of the elements
113                        for(OWLClassExpression child : operands) {
114                                
115                                // Refinement für das Kind ausführen
116                                tmp = refine(child);
117                                
118                                // neue MultiConjunction konstruieren
119                                for(OWLClassExpression c : tmp) {
120                                        // TODO: müssen auch alle Konzepte geklont werden??
121                                        // hier wird nur eine neue Liste erstellt
122                                        // => eigentlich muss nicht geklont werden (d.h. deep copy) da
123                                        // die Konzepte nicht verändert werden während des Algorithmus
124                                        List<OWLClassExpression> newChildren = new LinkedList<>(operands);
125                                        // es muss genau die vorherige Reihenfolge erhalten bleiben
126                                        // (zumindest bis die Normalform definiert ist)
127                                        int index = newChildren.indexOf(child);
128                                        newChildren.add(index, c);                                      
129                                        newChildren.remove(child);
130                                        OWLClassExpression mc = new OWLObjectIntersectionOfImplExt(newChildren);
131                                        refinements.add(mc);    
132                                }
133                        }
134                } else if (concept instanceof OWLObjectUnionOf) {
135                        // refine one of the elements
136                        List<OWLClassExpression> operands = ((OWLObjectUnionOf) concept).getOperandsAsList();
137                        for(OWLClassExpression child : operands) {
138                                
139                                // Refinement für das Kind ausführen
140                                // tmp = refine(child);
141                                tmp = refine(child);
142                                // neue MultiConjunction konstruieren
143                                for(OWLClassExpression c : tmp) {
144                                        List<OWLClassExpression> newChildren = new LinkedList<>(operands);
145                                        // es muss genau die vorherige Reihenfolge erhalten bleiben
146                                        // (zumindest bis die Normalform definiert ist)
147                                        int index = newChildren.indexOf(child);
148                                        newChildren.add(index, c);                                      
149                                        newChildren.remove(child);                                      
150                                        OWLObjectUnionOf md = new OWLObjectUnionOfImplExt(newChildren);
151                                        refinements.add(md);    
152                                }
153                        }
154                        
155                        // ein Element der Disjunktion kann weggelassen werden
156                        for(OWLClassExpression child : ((OWLObjectUnionOf) concept).getOperandsAsList()) {
157                                List<OWLClassExpression> newChildren = new LinkedList<>(operands);
158                                newChildren.remove(child);
159                                // wenn nur ein Kind da ist, dann wird Disjunktion gleich weggelassen
160                                if(newChildren.size()==1)
161                                        refinements.add(newChildren.get(0));
162                                else {
163                                        OWLObjectUnionOf md = new OWLObjectUnionOfImplExt(newChildren);
164                                        refinements.add(md);
165                                }
166                        }
167                        
168                } else if (concept instanceof OWLObjectSomeValuesFrom) {
169                        OWLObjectPropertyExpression role = ((OWLObjectSomeValuesFrom) concept).getProperty();
170                        OWLClassExpression filler = ((OWLObjectSomeValuesFrom) concept).getFiller();
171                        
172                        tmp = refine(filler);
173                        for(OWLClassExpression c : tmp) {
174                                refinements.add(df.getOWLObjectSomeValuesFrom(role, c));
175                        }               
176                        
177                        // falls Kind Bottom ist, dann kann exists weggelassen werden
178                        if(filler.isOWLNothing())
179                                refinements.add(df.getOWLNothing());
180                        
181                } else if (concept instanceof OWLObjectAllValuesFrom) {
182                        OWLObjectPropertyExpression role = ((OWLObjectAllValuesFrom) concept).getProperty();
183                        OWLClassExpression filler = ((OWLObjectAllValuesFrom) concept).getFiller();
184
185                        tmp = refine(filler);
186                        for(OWLClassExpression c : tmp) {
187                                refinements.add(df.getOWLObjectAllValuesFrom(role, c));
188                        }               
189                        
190                        if(filler.isOWLNothing())
191                                refinements.add(df.getOWLNothing());
192                        
193                        // falls es keine spezielleren atomaren Konzepte gibt, dann wird 
194                        // bottom angehangen => nur wenn es ein atomares Konzept (insbesondere != bottom)
195                        // ist
196                        // if(tmp.size()==0) {
197                        // if(concept.getChild(0) instanceof AtomicConcept && tmp.size()==0) {
198                        //      refinements.add(new All(((Quantification)concept).getRole(),new Bottom()));
199                        //}
200                } else
201                        throw new RuntimeException(concept.toString());
202                
203                // falls Konzept ungleich Bottom oder Top, dann kann ein Refinement von Top
204                // angehangen werden
205                if(concept instanceof OWLObjectUnionOf || 
206                                !concept.isAnonymous() ||
207                                concept instanceof OWLObjectComplementOf || 
208                                concept instanceof OWLObjectSomeValuesFrom || 
209                                concept instanceof OWLObjectAllValuesFrom) {
210                        
211                        // es wird AND TOP angehangen
212                        List<OWLClassExpression> operands = Lists.newArrayList(concept, df.getOWLThing());
213                        OWLClassExpression mc = new OWLObjectIntersectionOfImplExt(operands);
214                        refinements.add(mc);
215                }
216
217                // Refinements werden jetzt noch bereinigt, d.h. Verschachtelungen von Konjunktionen
218                // werden entfernt; es wird eine neue Menge erzeugt, da die Transformationen die
219                // Ordnung des Konzepts ändern könnten
220                // TODO: eventuell geht das noch effizienter, da die meisten Refinement-Regeln Refinements
221                // von Child-Konzepten sind, die bereits geordnet sind, d.h. man könnte dort eventuell
222                // gleich absichern, dass alle neu hinzugefügten Refinements in geordneter Negationsnormalform
223                // sind
224                // SortedSet<Concept> returnSet = new TreeSet<Concept>(conceptComparator);
225                /*
226                
227                Set<Concept> returnSet = new HashSet<Concept>();
228                for(Concept c : refinements) {
229                        ConceptTransformation.cleanConcept(c);
230                        // ConceptTransformation.transformToOrderedNegationNormalForm(c, conceptComparator);
231                        returnSet.add(c);
232                }
233                
234                return returnSet;
235                */
236                
237                // Zwischenschritt wird weggelassen - man muss nicht alle Konzepte cleanen,
238                // um dann nur eins davon auszuwählen
239                
240                return refinements;
241        }
242
243        @Override
244        public Set<OWLClassExpression> refine(OWLClassExpression concept, int maxLength,
245                        List<OWLClassExpression> knownRefinements) {
246                throw new RuntimeException();
247        }
248
249        @Override
250        public void init() throws ComponentInitException {
251                initialized = true;
252        }
253
254}