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.algorithms.hybridgp;
020
021import java.util.Random;
022import java.util.Set;
023import java.util.SortedMap;
024import java.util.TreeMap;
025
026import org.dllearner.algorithms.gp.Program;
027import org.dllearner.core.AbstractReasonerComponent;
028import org.dllearner.learningproblems.PosNegLP;
029import org.dllearner.learningproblems.ScorePosNeg;
030import org.dllearner.refinementoperators.PsiDown;
031import org.dllearner.refinementoperators.PsiUp;
032import org.dllearner.utilities.owl.ConceptTransformation;
033import org.dllearner.utilities.owl.OWLClassExpressionUtils;
034import org.semanticweb.owlapi.model.OWLClassExpression;
035
036public class Psi implements GeneticRefinementOperator {
037
038        PsiUp pu;
039        PsiDown pd;
040        PosNegLP learningProblem;
041        int nrOfPositiveExamples;
042        int nrOfNegativeExamples;
043        Random random;
044        
045        // Cache, damit keine Konzepte doppelt ausgewertet werden
046        public SortedMap<OWLClassExpression,ScorePosNeg> evalCache = new TreeMap<>();
047        
048        // Cache, damit PsiDown bzw. PsiUp nicht mehrfach für gleiches Konzept
049        // aufgerufen werden
050        public SortedMap<OWLClassExpression,Set<OWLClassExpression>> pdCache = new TreeMap<>();
051        public SortedMap<OWLClassExpression,Set<OWLClassExpression>> puCache = new TreeMap<>();
052        
053        // Statistiken
054        int conceptCacheHits = 0;
055        int nrOfRequests = 0;
056        int pdCacheHits = 0;
057        private long pdRequests = 0;
058        int puCacheHits = 0;
059        private long puRequests = 0;
060        private long psiApplicationStartTime = 0;
061        private long psiApplicationTimeNs = 0;
062        private long psiReasoningStartTime = 0;
063        private long psiReasoningTimeNs = 0;
064        
065        @SuppressWarnings("unused")
066        private long someTimeStart = 0;
067        public long someTime = 0;
068        
069        public Psi(PosNegLP learningProblem, AbstractReasonerComponent reasoningService) { //, PsiUp pu, PsiDown pd) {
070                // this.pu = pu;
071                // this.pd = pd;
072                this.learningProblem = learningProblem;
073                pu = new PsiUp(learningProblem, reasoningService);
074                pd = new PsiDown(learningProblem, reasoningService);
075                nrOfPositiveExamples = learningProblem.getPositiveExamples().size();
076                nrOfNegativeExamples = learningProblem.getNegativeExamples().size();
077                random = new Random();
078        }
079        
080        public OWLClassExpression applyPsi(OWLClassExpression concept, int coveredPositives, int coveredNegatives) {
081                // Wahrscheinlichkeit für upward refinement berechnen
082                double tmp = coveredNegatives/(double)nrOfNegativeExamples;
083                double tmp2 = coveredPositives/(double)nrOfPositiveExamples;
084                
085                double downwardProbability = tmp/(1+tmp-tmp2); 
086                
087                boolean debug = false;
088                if(debug) {
089                        System.out.println("negative percentage covered: " + tmp);
090                        System.out.println("positive percentage covered: " + tmp2);
091                        // System.out.println("upward probability: upward")
092                }
093                
094                if(downwardProbability<0 || downwardProbability>1) {
095                        
096                        // bei Division gibt es Ungenauigkeit, so dass man einen kleinen
097                        // Toleranzbereich lassen muss
098                        if(downwardProbability < -0.0001)
099                                throw new RuntimeException();
100                        else if(downwardProbability > 1.0001)
101                                throw new RuntimeException();
102                        // Rundung auf korrekten Wert
103                        else if(downwardProbability<0)
104                                downwardProbability = 0d;
105                        else
106                                downwardProbability = 1d;
107                }
108                        
109                // System.out.println(upwardProbability);
110                
111                boolean downward = (Math.random()<=downwardProbability);
112                
113                // someTimeStart = System.nanoTime();
114                // downward oder upward refinement operator anwenden
115                Set<OWLClassExpression> refinements;
116                if(downward) {
117                        pdRequests++;
118                        // Cache aufrufen
119                        refinements = pdCache.get(concept);
120                        if(refinements == null) {
121                                refinements = pd.refine(concept);
122                                pdCache.put(concept, refinements);
123                        } else
124                                pdCacheHits++;
125                } else {
126                        puRequests++;
127                        refinements = puCache.get(concept);
128                        if(refinements == null) {
129                                refinements = pu.refine(concept);
130                                puCache.put(concept, refinements);
131                        } else
132                                puCacheHits++;
133                }
134                
135                /*
136                if(puRequests % 500 == 0 && !downward) {
137                        System.out.println(concept);
138                        for(Concept refinement : refinements)
139                                System.out.println("  " + refinement);
140                }*/
141                
142                
143                // someTime += System.nanoTime() - someTimeStart;
144                // System.out.println(refinements.size());
145                
146                String dir = "";
147                int prob = -1;
148                if(debug) {
149                        if(downward) {
150                                dir = "downward";
151                                prob = (int) (100 * downwardProbability);
152                        } else {
153                                dir = "upward";
154                                prob = (int) (100 * (1-downwardProbability));                   
155                        }
156                }
157                        
158                
159                // ein refinement zufällig auswählen
160                OWLClassExpression[] array = refinements.toArray(new OWLClassExpression[refinements.size()]);
161                // kein refinement gefunden
162                if(array.length==0) {
163                        if(debug) {
164                                System.out.println("message: no " + dir + " refinement found for " + concept);
165                                System.out.println();                           
166                        }
167                        // Konzept selbst wird zurückgegeben (also reproduction)
168                        return concept;
169                }
170                        
171                int position = random.nextInt(array.length);
172                OWLClassExpression returnConcept = array[position];
173                ConceptTransformation.cleanConcept(returnConcept);
174                
175                // das Rückgabekonzept wird geklont, damit alle parent-Links repariert
176                // werden (damit andere GP-Operatoren korrekt funktionieren)
177                OWLClassExpression returnConceptClone = OWLClassExpressionUtils.clone(returnConcept);
178                
179                if(debug) {
180                        System.out.println(concept + " " + dir + "("+prob+"%) to " + returnConcept);
181                        System.out.println();
182                }
183                
184                return returnConceptClone;
185        }
186        
187        @Override
188        public Program applyOperator(Program program) {
189                psiApplicationStartTime = System.nanoTime();
190                nrOfRequests++;
191                
192                OWLClassExpression concept = program.getTree();
193                // es muss sichergestellt sein, dass Konjunktionen nur als MultConjunctions
194                // vorhanden sind (analog bei Disjunktion) => effizienter wäre eine Auslagerung
195                // dieses Codes in die Konzepterzeugung, da die Transformation häufig nichts
196                // bringt; allerdings sollte GP noch kompatibel mit anderen Operatoren bleiben
197                // Concept conceptMod = ConceptTransformation.transformToMulti(concept);                
198                // sicherstellen, dass Konstrukte wie NOT TOP, die momentan nicht vom
199                // Operator behandelt werden können, herausfallen (TODO: anschauen, ob es
200                // sich lohnt Operatoren zu definieren, die keine Negationsnormalform
201                // erfordern)
202                
203                OWLClassExpression conceptMod = concept.getNNF();
204                // um mehr Cache Hits zu bekommen, wird noch vereinfach und geordnet
205                
206                
207                OWLClassExpression conceptModForCache = ConceptTransformation.applyEquivalenceRules(conceptMod);
208                
209                ScorePosNeg score = program.getScore();
210                // Eval-Cache füllen
211                evalCache.put(conceptModForCache, score);
212                
213                // System.out.println("covered positives: " + score.getCoveredPositives());
214                // System.out.println("covered negatives: " + score.getCoveredNegatives());             
215                int coveredPositives = score.getCoveredPositives().size();
216                int coveredNegatives = score.getCoveredNegatives().size();
217                // someTimeStart = System.nanoTime();
218                OWLClassExpression newConcept = applyPsi(conceptMod, coveredPositives, coveredNegatives);
219                ConceptTransformation.cleanConcept(newConcept);
220                // someTime += System.nanoTime() - someTimeStart;
221                // newConcept.setParent(null);
222                
223                /////////// TESTCODE: umwandeln des erhaltenen Konzepts
224                // someTimeStart = System.nanoTime();
225                OWLClassExpression newConceptMod = ConceptTransformation.applyEquivalenceRules(newConcept);
226                // someTime += System.nanoTime() - someTimeStart;
227                ///////////
228                
229                // versuchen Reasoner-Cache zu treffen
230                // Problem: Score hängt von Konzeptlänge ab!! => muss hier explizit
231                // reingerechnet werden
232                ScorePosNeg newScore = evalCache.get(newConceptMod);
233                
234                if(newScore==null) {
235                        psiReasoningStartTime = System.nanoTime();
236                        newScore = (ScorePosNeg) learningProblem.computeScore(newConcept);
237                        psiReasoningTimeNs += System.nanoTime() - psiReasoningStartTime;
238                        
239                        evalCache.put(newConceptMod, newScore);
240                } else {
241                        conceptCacheHits++;
242                        
243                        // ToDo: es muss jetzt explizit ein neues Score-Objekt
244                        // erzeugt werden, welches die geänderte Konzeptlänge
245                        // berücksichtigt
246                        newScore = newScore.getModifiedLengthScore(OWLClassExpressionUtils.getLength(newConcept));
247                }
248                
249                /*
250                if(nrOfRequests % 1000 == 0) {
251                        System.out.println(concept);
252                        System.out.println(newConcept);
253                }
254                */
255                
256                Program newProgram = new Program(newScore, newConcept);
257                psiApplicationTimeNs += System.nanoTime() - psiApplicationStartTime;
258                return newProgram;
259        }
260        
261        // gibt die Größe des Caches zurück (gutes Maß um zu sehen, ob überhaupt
262        // neue Konzepte erforscht werden)
263        public int getCacheSize() {
264                return evalCache.size();
265        }
266
267        public int getConceptCacheHits() {
268                return conceptCacheHits;
269        }
270
271        public int getNrOfRequests() {
272                return nrOfRequests;
273        }
274
275        public long getPsiApplicationTimeNs() {
276                return psiApplicationTimeNs;
277        }
278
279        public long getPsiReasoningTimeNs() {
280                return psiReasoningTimeNs;
281        }
282
283        public int getPdCacheHits() {
284                return pdCacheHits;
285        }
286
287        public int getPuCacheHits() {
288                return puCacheHits;
289        }
290
291        public long getPdRequests() {
292                return pdRequests;
293        }
294
295        public long getPuRequests() {
296                return puRequests;
297        }
298
299        public SortedMap<OWLClassExpression, Set<OWLClassExpression>> getPdCache() {
300                return pdCache;
301        }
302
303        public SortedMap<OWLClassExpression, Set<OWLClassExpression>> getPuCache() {
304                return puCache;
305        }
306        
307}