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}