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 java.util.HashSet; 022import java.util.Map; 023import java.util.Set; 024import java.util.SortedSet; 025import java.util.TreeMap; 026import java.util.TreeSet; 027 028import org.dllearner.core.AbstractReasonerComponent; 029import org.dllearner.core.owl.ClassHierarchy; 030import org.semanticweb.owlapi.model.OWLClass; 031import org.semanticweb.owlapi.model.OWLClassExpression; 032import org.semanticweb.owlapi.model.OWLDataFactory; 033import org.semanticweb.owlapi.model.OWLDataProperty; 034import org.semanticweb.owlapi.model.OWLIndividual; 035import org.semanticweb.owlapi.model.OWLObjectProperty; 036 037import uk.ac.manchester.cs.owl.owlapi.OWLDataFactoryImpl; 038 039import com.google.common.collect.Sets; 040import com.jamonapi.Monitor; 041import com.jamonapi.MonitorFactory; 042 043/** 044 * Utility methods for constructing refinement operators. 045 * 046 * @author Jens Lehmann 047 * 048 */ 049public final class Utility { 050 051 private AbstractReasonerComponent reasoner; 052 ClassHierarchy sh; 053 054 // specifies whether to do real disjoint tests or check that 055 // two named classes do not have common instances 056 private boolean instanceBasedDisjoints = true; 057 058 // cache for reasoner queries 059 private Map<OWLClassExpression,Map<OWLClassExpression,Boolean>> cachedDisjoints = new TreeMap<>(); 060 061 // cache for applicaple object properties 062 private Map<OWLClassExpression, SortedSet<OWLObjectProperty>> appOPCache = new TreeMap<>(); 063 private Map<OWLClassExpression, SortedSet<OWLDataProperty>> appDPCache = new TreeMap<>(); 064 private Map<OWLObjectProperty,OWLClassExpression> opDomains; 065 private Map<OWLDataProperty, OWLClassExpression> dpDomains; 066 067 private OWLDataFactory df = new OWLDataFactoryImpl(); 068 069 public Utility(AbstractReasonerComponent rs) { 070 throw new Error("not implemented yet"); 071 } 072 073 public Utility(AbstractReasonerComponent rs, Map<OWLObjectProperty,OWLClassExpression> opDomains, boolean instanceBasedDisjoints) { 074 this.reasoner = rs; 075 sh = rs.getClassHierarchy(); 076 // we cache object property domains 077 this.opDomains = opDomains; 078 this.instanceBasedDisjoints = instanceBasedDisjoints; 079 } 080 081 public Utility(AbstractReasonerComponent rs, Map<OWLObjectProperty,OWLClassExpression> opDomains, Map<OWLDataProperty,OWLClassExpression> dpDomains, boolean instanceBasedDisjoints) { 082 this.reasoner = rs; 083 this.dpDomains = dpDomains; 084 sh = rs.getClassHierarchy(); 085 // we cache object property domains 086 this.opDomains = opDomains; 087 this.instanceBasedDisjoints = instanceBasedDisjoints; 088 } 089 090 /** 091 * Compute the set of applicable object properties for a 092 * given description. 093 * 094 * @param index The index is a class expression which determines 095 * which of the properties are applicable. Exactly those which 096 * where the index and property domain are not disjoint are 097 * applicable, where disjoint is defined by {@link #isDisjoint(OWLClassExpression, OWLClassExpression)}. 098 * 099 */ 100 public SortedSet<OWLObjectProperty> computeApplicableObjectProperties(OWLClassExpression index) { 101 // use a cache, because large ontologies can have many object properties 102 SortedSet<OWLObjectProperty> applicableObjectProperties = appOPCache.get(index); 103 if(applicableObjectProperties == null) { 104 Set<OWLObjectProperty> objectProperties = reasoner.getObjectProperties(); 105 applicableObjectProperties = new TreeSet<>(); 106 for(OWLObjectProperty op : objectProperties) { 107 OWLClassExpression domain = opDomains.get(op); 108 if(!isDisjoint(index,domain)) { 109 applicableObjectProperties.add(op); 110 } 111 } 112 appOPCache.put(index, applicableObjectProperties); 113 } 114 return applicableObjectProperties; 115 } 116 117 /** 118 * Compute the set of applicable data properties for a 119 * given description. 120 * 121 * @param index The index is a OWLClassExpression which determines 122 * which of the properties are applicable. Exactly those which 123 * where the index and property domain are not disjoint are 124 * applicable, where disjoint is defined by {@link #isDisjoint(OWLClassExpression, OWLClassExpression)}. 125 * 126 */ 127 public SortedSet<OWLDataProperty> computeApplicableDatatypeProperties(OWLClassExpression index) { 128 // use a cache, because large ontologies can have many data properties 129 SortedSet<OWLDataProperty> applicableDatatypeProperties = appDPCache.get(index); 130 if(applicableDatatypeProperties == null) { 131 Set<OWLDataProperty> datatypeProperties = reasoner.getDatatypeProperties(); 132 applicableDatatypeProperties = new TreeSet<>(); 133 for(OWLDataProperty op : datatypeProperties) { 134 OWLClassExpression domain = dpDomains.get(op); 135 if(!isDisjoint(index,domain)) { 136 applicableDatatypeProperties.add(op); 137 } 138 } 139 appDPCache.put(index, applicableDatatypeProperties); 140 } 141 return applicableDatatypeProperties; 142 } 143 144 /** 145 * Given a set of applicable object properties, this method returns 146 * the most general ones, i.e. those where more general ones do not 147 * exist in the set of applicable properties. Due to the definition 148 * of "applicable", the returned set is just the intersection of the most 149 * general object properties and the applicable properties. (A non-applicable 150 * property cannot have applicable subproperties, because subproperties 151 * can only restrict, but not broaden their domain.) 152 * 153 * @param applicableObjectProperties The set of applicable properties. 154 * @return The most general applicable properties. 155 */ 156 public Set<OWLObjectProperty> computeMgr(Set<OWLObjectProperty> applicableObjectProperties) { 157 return new HashSet<>(Sets.intersection(reasoner.getMostGeneralProperties(), applicableObjectProperties)); 158 } 159 160 /** 161 * Given a set of applicable data properties, this method returns 162 * the most general ones, i.e. those where more general ones do not 163 * exist in the set of applicable properties. Due to the definition 164 * of "applicable", the returned set is just the intersection of the most 165 * general object properties and the applicable properties. (A non-applicable 166 * property cannot have applicable subproperties, because subproperties 167 * can only restrict, but not broaden their domain.) 168 * 169 * @param applicableDatatypeProperties The set of applicable properties. 170 * @return The most general applicable properties. 171 */ 172 public Set<OWLDataProperty> computeMgrDP(Set<OWLDataProperty> applicableDatatypeProperties) { 173 return new HashSet<>(Sets.intersection(reasoner.getMostGeneralDatatypeProperties(), applicableDatatypeProperties)); 174 } 175 176 public Set<OWLClass> getClassCandidates(OWLClassExpression index, Set<OWLClass> existingClasses) { 177 return getClassCandidatesRecursive(index, existingClasses, df.getOWLThing()); 178 } 179 180 private Set<OWLClass> getClassCandidatesRecursive(OWLClassExpression index, Set<OWLClass> existingClasses, OWLClassExpression upperClass) { 181 Set<OWLClass> candidates = new TreeSet<>(); 182 183 // we descend the subsumption hierarchy to ensure that we get 184 // the most general concepts satisfying the criteria 185 // there are 4 checks a class has to satisfy to get into the set; 186 // for 2 of them we can stop further traversal in the subsumption 187 // hierarchy 188 for(OWLClassExpression d : sh.getSubClasses(upperClass, true)) { 189// System.out.println("d: " + d); 190 // owl:Nothing is never a candidate (not in EL) 191 if(!d.isOWLNothing()) { 192 OWLClass candidate = d.asOWLClass(); 193 // we first do those checks where we know that we do not 194 // need to traverse the subsumption hierarchy if they are 195 // not satisfied 196 // check1: disjointness with index 197 // check3: no superclass exists already 198 // check5: disjointness 199 if(!isDisjoint(candidate,index) && checkSubClasses(existingClasses,candidate) && checkDisjoints(existingClasses,candidate)) { 200 // check whether the class is meaningful, i.e. adds something to the index 201 // to do this, we need to make sure that the class is not a superclass of the 202 // index (otherwise we get nothing new) 203 if(!isDisjoint(df.getOWLObjectComplementOf(candidate),index) && checkSuperClasses(existingClasses,candidate)) { 204 // candidate went successfully through all checks 205 candidates.add(candidate); 206 } else { 207// System.out.println("k32: " + candidate + " index " + index + " cond1 " + isDisjoint(new Negation(candidate),index) + " cond2 " + checkSuperClasses(existingClasses,candidate)); 208 // descend subsumption hierarchy to find candidates 209 candidates.addAll(getClassCandidatesRecursive(index, existingClasses, candidate)); 210 } 211 } 212 } 213 } 214 return candidates; 215 } 216 217 // returns true if the candidate is not subclass of an existing class, 218 // false otherwise (check 3) 219 private boolean checkSubClasses(Set<OWLClass> existingClasses, OWLClass candidate) { 220 for(OWLClass nc : existingClasses) { 221// System.out.println("csc: " + nc + candidate); 222 if(sh.isSubclassOf(candidate, nc)) { 223 return false; 224 } 225 } 226 return true; 227 } 228 229 // returns true if the candidate is not superclass of an existing class, 230 // false otherwise (check 4) 231 private boolean checkSuperClasses(Set<OWLClass> existingClasses, OWLClass candidate) { 232 for(OWLClass nc : existingClasses) { 233 if(sh.isSubclassOf(nc, candidate)) 234 return false; 235 } 236 return true; 237 } 238 239 // returns false if any of the classes is disjoint with the new one; true otherwise 240 private boolean checkDisjoints(Set<OWLClass> existingClasses, OWLClass candidate) { 241 for(OWLClass nc : existingClasses) { 242 if(isDisjoint(nc, candidate)) 243 return false; 244 } 245 return true; 246 } 247 248 249 public boolean isDisjoint(OWLClassExpression d1, OWLClassExpression d2) { 250// System.out.println("d1: " + d1); 251// System.out.println("d2: " + d2); 252// System.out.println("cache: " + cachedDisjoints); 253 254 // check whether we have cached this query 255 Map<OWLClassExpression,Boolean> tmp = cachedDisjoints.get(d1); 256 Boolean tmp2 = null; 257 if(tmp != null) 258 tmp2 = tmp.get(d2); 259 260 if(tmp2==null) { 261 Boolean result; 262 if(instanceBasedDisjoints) { 263 result = isDisjointInstanceBased(d1,d2); 264 } else { 265 OWLClassExpression d = df.getOWLObjectIntersectionOf(d1, d2); 266 Monitor mon = MonitorFactory.start("disjointness reasoning"); 267 result = reasoner.isSuperClassOf(df.getOWLNothing(), d); 268 mon.stop(); 269 } 270 // add the result to the cache (we add it twice such that 271 // the order of access does not matter) 272 273 // create new entries if necessary 274 if(tmp == null) 275 cachedDisjoints.put(d1, new TreeMap<>()); 276 if(!cachedDisjoints.containsKey(d2)) 277 cachedDisjoints.put(d2, new TreeMap<>()); 278 279 // add result symmetrically in the OWLClassExpression matrix 280 cachedDisjoints.get(d1).put(d2, result); 281 cachedDisjoints.get(d2).put(d1, result); 282 return result; 283 } else { 284 return tmp2; 285 } 286 } 287 288 private boolean isDisjointInstanceBased(OWLClassExpression d1, OWLClassExpression d2) { 289 SortedSet<OWLIndividual> d1Instances = reasoner.getIndividuals(d1); 290 SortedSet<OWLIndividual> d2Instances = reasoner.getIndividuals(d2); 291 for(OWLIndividual d1Instance : d1Instances) { 292 if(d2Instances.contains(d1Instance)) 293 return false; 294 } 295 return true; 296 } 297 298}