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.core.owl; 020 021import com.google.common.collect.Sets; 022import org.semanticweb.owlapi.model.OWLObject; 023import org.slf4j.Logger; 024import org.slf4j.LoggerFactory; 025 026import java.util.Map.Entry; 027import java.util.*; 028 029/** 030 * @author Lorenz Buehmann 031 * 032 */ 033public abstract class AbstractHierarchy<T extends OWLObject> implements Hierarchy<T> { 034 035 private static final Logger logger = LoggerFactory.getLogger(AbstractHierarchy.class); 036 037 private SortedMap<T, SortedSet<T>> hierarchyUp; 038 private SortedMap<T, SortedSet<T>> hierarchyDown; 039 040 private SortedSet<T> rootEntities = new TreeSet<>(); 041 private SortedSet<T> leafEntities = new TreeSet<>(); 042 043 044 public AbstractHierarchy(SortedMap<T, SortedSet<T>> hierarchyUp, SortedMap<T, SortedSet<T>> hierarchyDown) { 045 this.hierarchyUp = hierarchyUp; 046 this.hierarchyDown = hierarchyDown; 047 048 // find most general and most special entities 049 for (T entity : Sets.union(hierarchyUp.keySet(), hierarchyDown.keySet())) { 050 SortedSet<T> moreGen = getParents(entity); 051 SortedSet<T> moreSpec = getChildren(entity); 052 053 if (moreGen.size() == 0 || (moreGen.size() == 1 && moreGen.first().isTopEntity())) 054 rootEntities.add(entity); 055 056 if (moreSpec.size() == 0 || (moreSpec.size() == 1 && moreSpec.first().isBottomEntity())) 057 leafEntities.add(entity); 058 } 059 } 060 061 /* (non-Javadoc) 062 * @see org.dllearner.core.owl.Hierarchy#getChildren(org.semanticweb.owlapi.model.OWLEntity) 063 */ 064 @Override 065 public SortedSet<T> getChildren(T entity) { 066 return getChildren(entity, true); 067 } 068 069 /** 070 * @return all entities in this hierarchy 071 */ 072 public Set<T> getEntities() { 073 return Sets.union(hierarchyDown.keySet(), hierarchyUp.keySet()); 074 } 075 076 /* (non-Javadoc) 077 * @see org.dllearner.core.owl.Hierarchy#getChildren(org.semanticweb.owlapi.model.OWLEntity, boolean) 078 */ 079 @Override 080 public SortedSet<T> getChildren(T entity, boolean direct) { 081 SortedSet<T> result = hierarchyDown.get(entity); 082 083 if(result == null) { 084 logger.debug("Query for " + entity + " in hierarchy, but the entity is not contained in the (downward) hierarchy, e.g. because the entity does not exist or is ignored. Returning empty result instead."); 085 return new TreeSet<>(); 086 } 087 088 result.remove(entity); 089 if(!direct) { // get transitive children 090 SortedSet<T> tmp = new TreeSet<>(); 091 for(T child : result){ 092 tmp.addAll(getChildren(child, direct)); 093 } 094 result.addAll(tmp); 095 } 096 return new TreeSet<>(result); 097 } 098 099 /* (non-Javadoc) 100 * @see org.dllearner.core.owl.Hierarchy#getParents(org.semanticweb.owlapi.model.OWLEntity) 101 */ 102 @Override 103 public SortedSet<T> getParents(T entity) { 104 return getParents(entity, true); 105 } 106 107 /* (non-Javadoc) 108 * @see org.dllearner.core.owl.Hierarchy#getParents(org.semanticweb.owlapi.model.OWLEntity, boolean) 109 */ 110 @Override 111 public SortedSet<T> getParents(T entity, boolean direct) { 112 SortedSet<T> result = hierarchyUp.get(entity); 113 114 if(result == null) { 115 logger.debug("Query for " + entity + " in hierarchy, but the entity is not contained in the (upward) hierarchy, e.g. because the entity does not exist or is ignored. Returning empty result instead."); 116 return new TreeSet<>(); 117 } 118 119 result.remove(entity); 120 if(!direct) { 121 SortedSet<T> tmp = new TreeSet<>(); 122 for(T parent : result){ 123 tmp.addAll(getParents(parent, direct)); 124 } 125 result.addAll(tmp); 126 } 127 128 return new TreeSet<>(result); 129 } 130 131 /* (non-Javadoc) 132 * @see org.dllearner.core.owl.Hierarchy#getSiblings(org.semanticweb.owlapi.model.OWLEntity) 133 */ 134 @Override 135 public SortedSet<T> getSiblings(T entity) { 136 SortedSet<T> siblings = new TreeSet<>(); 137 138 Set<T> parents = getParents(entity); 139 for(T parent : parents) { 140 siblings.addAll(getChildren(parent)); 141 } 142 143 siblings.remove(entity); 144 return siblings; 145 } 146 147 /* (non-Javadoc) 148 * @see org.dllearner.core.owl.Hierarchy#isChildOf(org.semanticweb.owlapi.model.OWLEntity, org.semanticweb.owlapi.model.OWLEntity) 149 */ 150 @Override 151 public boolean isChildOf(T entity1, T entity2) { 152 if (entity1.equals(entity2)) { 153 return true; 154 } else { 155 SortedSet<T> parents = getParents(entity1); 156 157 if(parents != null){ 158 // search the upper classes of the subclass 159 for (T parent : parents) { 160 if (isChildOf(parent, entity2)) { 161 return true; 162 } 163 } 164 } 165 // we cannot reach the class via any of the upper classes, 166 // so it is not a super class 167 return false; 168 } 169 } 170 171 /* (non-Javadoc) 172 * @see org.dllearner.core.owl.Hierarchy#isParentOf(org.semanticweb.owlapi.model.OWLClassExpression, org.semanticweb.owlapi.model.OWLClassExpression) 173 */ 174 @Override 175 public boolean isParentOf(T entity1, T entity2) { 176 return isChildOf(entity2, entity1); 177 } 178 179 /* (non-Javadoc) 180 * @see org.dllearner.core.owl.Hierarchy#getRoots() 181 */ 182 @Override 183 public SortedSet<T> getRoots() { 184 SortedSet<T> roots = new TreeSet<>(); 185 186 for(T child : getChildren(getTopConcept())){ 187 SortedSet<T> parents = getParents(child); 188 parents.remove(getTopConcept()); 189 190 if(parents.isEmpty()){ 191 roots.add(child); 192 } 193 } 194 return roots; 195 } 196 197 /** 198 * @return The most general entites. 199 */ 200 public SortedSet<T> getMostGeneralEntities() { 201 return rootEntities; 202 } 203 204 /** 205 * @return The most special roles. 206 */ 207 public SortedSet<T> getMostSpecialEntities() { 208 return leafEntities; 209 } 210 211 /* (non-Javadoc) 212 * @see org.dllearner.core.owl.Hierarchy#contains(org.semanticweb.owlapi.model.OWLObject) 213 */ 214 @Override 215 public boolean contains(T entity) { 216 return hierarchyUp.containsKey(entity); 217 } 218 219 /** 220 * This method modifies the subsumption hierarchy such that for each class, 221 * there is only a single path to reach it via upward and downward 222 * refinement respectively. 223 */ 224 public void thinOutSubsumptionHierarchy() { 225 SortedMap<T, SortedSet<T>> hierarchyDownNew = new TreeMap<>(); 226 SortedMap<T, SortedSet<T>> hierarchyUpNew = new TreeMap<>(); 227 228 Set<T> conceptsInSubsumptionHierarchy = new TreeSet<>(); 229 conceptsInSubsumptionHierarchy.addAll(hierarchyUp.keySet()); 230 conceptsInSubsumptionHierarchy.addAll(hierarchyDown.keySet()); 231 232 // add empty sets for each concept 233 for (T c : conceptsInSubsumptionHierarchy) { 234 hierarchyDownNew.put(c, new TreeSet<>()); 235 hierarchyUpNew.put(c, new TreeSet<>()); 236 } 237 238 for (T c : conceptsInSubsumptionHierarchy) { 239 // look whether there are more general concepts 240 // (if yes, pick the first one) 241 SortedSet<T> moreGeneral = getParents(c); 242 if (moreGeneral != null && !moreGeneral.isEmpty()) { 243 T chosenParent = moreGeneral.first(); 244 hierarchyDownNew.get(chosenParent).add(c); 245 } 246 } 247 248 for (T c : conceptsInSubsumptionHierarchy) { 249 SortedSet<T> moreSpecial = getChildren(c); 250 if (moreSpecial != null && !moreSpecial.isEmpty()) { 251 T chosenChild = moreSpecial.first(); 252 hierarchyUpNew.get(chosenChild).add(c); 253 } 254 } 255 256 // top node 257 hierarchyDownNew.put(getTopConcept(), getChildren(getTopConcept())); 258 259 // bottom node 260 hierarchyUpNew.put(getBottomConcept(), getParents(getBottomConcept())); 261 262 setHierarchyDown(hierarchyDownNew); 263 setHierarchyUp(hierarchyUpNew); 264 } 265 266 /** 267 * The method computes a new subsumption hierarchy, which is a copy of this 268 * one, but only the specified entities are allowed to occur. For instance, 269 * if we have subclass relationships between 1sYearStudent, Student, and 270 * Person, but Student is not allowed, then there a is a subclass relationship 271 * between 1stYearStudent and Person. 272 * Currently, owl:Thing and owl:Nothing are always allowed for technical 273 * reasons. 274 * @param allowedEntities The entities, which are allowed to occur in the new 275 * subsumption hierarchy. 276 * @return A copy of this hierarchy, which is restricted to a certain set 277 * of entities. 278 */ 279 public AbstractHierarchy<T> cloneAndRestrict(Set<? extends T> allowedEntities) { 280 // currently TOP and BOTTOM are always allowed 281 // (TODO would be easier if Thing/Nothing were declared as named classes) 282 Set<T> allowed = new TreeSet<>(); 283 allowed.addAll(allowedEntities); 284 allowed.add(getTopConcept()); 285 allowed.add(getBottomConcept()); 286 287 // create new maps 288 SortedMap<T, SortedSet<T>> subsumptionHierarchyUpNew = new TreeMap<>(); 289 SortedMap<T, SortedSet<T>> subsumptionHierarchyDownNew = new TreeMap<>(); 290 291 for(Entry<T, SortedSet<T>> entry : hierarchyUp.entrySet()) { 292 T key = entry.getKey(); 293 // we only store mappings for allowed entities 294 if(allowed.contains(key)) { 295 // copy the set of all parents (we consume them until 296 // they are empty) 297 TreeSet<T> parents = new TreeSet<>(entry.getValue()); 298 // storage for new parents 299 TreeSet<T> newParents = new TreeSet<>(); 300 301 while(!parents.isEmpty()) { 302 // pick and remove the first element 303 T d = parents.pollFirst(); 304 // case 1: it is allowed, so we add it 305 if(allowed.contains(d)) { 306 newParents.add(d); 307 // case 2: it is not allowed, so we try its super classes 308 } else { 309 Set<T> tmp = hierarchyUp.get(d); 310 if(tmp != null){ 311 parents.addAll(tmp); 312 } 313 } 314 } 315 316 subsumptionHierarchyUpNew.put(key, newParents); 317 } 318 } 319 320 // downward case is analogous 321 for(Entry<T, SortedSet<T>> entry : hierarchyDown.entrySet()) { 322 T key = entry.getKey(); 323 if(allowed.contains(key)) { 324 TreeSet<T> children = new TreeSet<>(entry.getValue()); 325 TreeSet<T> newChildren = new TreeSet<>(); 326 327 while(!children.isEmpty()) { 328 T d = children.pollFirst(); 329 if(allowed.contains(d)) { 330 newChildren.add(d); 331 } else { 332 SortedSet<T> tmp = hierarchyDown.get(d); 333 if(tmp != null) { 334 children.addAll(tmp); 335 } 336 } 337 } 338 339 subsumptionHierarchyDownNew.put(key, newChildren); 340 } 341 } 342 343 try { 344 return this.getClass().getConstructor( 345 SortedMap.class, SortedMap.class).newInstance( 346 subsumptionHierarchyUpNew, subsumptionHierarchyDownNew); 347 } catch (Exception e) { 348 e.printStackTrace(); 349 } 350 return null; 351 } 352 353 /** 354 * @param hierarchyUp the hierarchyUp to set 355 */ 356 public void setHierarchyUp(SortedMap<T, SortedSet<T>> hierarchyUp) { 357 this.hierarchyUp = hierarchyUp; 358 } 359 360 /** 361 * @return the hierarchyUp 362 */ 363 public SortedMap<T, SortedSet<T>> getHierarchyUp() { 364 return hierarchyUp; 365 } 366 367 /** 368 * @param hierarchyDown the hierarchyDown to set 369 */ 370 public void setHierarchyDown(SortedMap<T, SortedSet<T>> hierarchyDown) { 371 this.hierarchyDown = hierarchyDown; 372 } 373 374 /** 375 * @return the hierarchyDown 376 */ 377 public SortedMap<T, SortedSet<T>> getHierarchyDown() { 378 return hierarchyDown; 379 } 380 381 public void precompute() { 382 383 } 384 385 @Override 386 public String toString() { 387 return toString(false); 388 } 389 390 public String toString(boolean showUpwardHierarchy) { 391 if (showUpwardHierarchy) { 392 String str = "downward subsumption:\n"; 393 str += toString(hierarchyDown, getTopConcept(), 0); 394 str += "upward subsumption:\n"; 395 str += toString(hierarchyUp, getBottomConcept(), 0); 396 return str; 397 } else { 398 return toString(hierarchyDown, getTopConcept(), 0); 399 } 400 } 401 402 protected String toString(SortedMap<T, SortedSet<T>> hierarchy, T concept, int depth) { 403 String str = ""; 404 for (int i = 0; i < depth; i++) 405 str += " "; 406 str += concept.toString() + "\n"; 407 Set<T> tmp = hierarchy.get(concept); 408 if (tmp != null) { 409 for (T c : tmp) 410 str += toString(hierarchy, c, depth + 1); 411 } 412 return str; 413 } 414 415 public abstract T getTopConcept(); 416 public abstract T getBottomConcept(); 417 418}