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.*; 022import org.dllearner.core.*; 023import org.dllearner.core.annotations.NoConfigOption; 024import org.dllearner.core.config.ConfigOption; 025import org.dllearner.core.options.CommonConfigOptions; 026import org.dllearner.core.owl.*; 027import org.dllearner.reasoning.SPARQLReasoner; 028import org.dllearner.utilities.OWLAPIUtils; 029import org.dllearner.utilities.owl.ConceptTransformation; 030import org.dllearner.utilities.owl.OWLClassExpressionLengthMetric; 031import org.dllearner.utilities.owl.OWLClassExpressionUtils; 032import org.dllearner.utilities.split.DefaultDateTimeValuesSplitter; 033import org.dllearner.utilities.split.DefaultNumericValuesSplitter; 034import org.dllearner.utilities.split.ValuesSplitter; 035import org.semanticweb.owlapi.model.*; 036import org.semanticweb.owlapi.vocab.OWLFacet; 037import org.semanticweb.owlapi.vocab.OWLRDFVocabulary; 038import org.slf4j.Logger; 039import org.slf4j.LoggerFactory; 040import org.slf4j.Marker; 041import org.slf4j.helpers.BasicMarkerFactory; 042import org.springframework.beans.factory.annotation.Autowired; 043import uk.ac.manchester.cs.owl.owlapi.OWLClassImpl; 044import uk.ac.manchester.cs.owl.owlapi.OWLDataFactoryImpl; 045 046import java.util.*; 047import java.util.Map.Entry; 048import java.util.function.Function; 049import java.util.stream.Collectors; 050 051import static com.google.common.primitives.Ints.max; 052import static java.util.stream.Collectors.summingInt; 053 054/** 055 * A downward refinement operator, which makes use of domains 056 * and ranges of properties. The operator is currently under 057 * development. Its aim is to span a much "cleaner" and smaller search 058 * tree compared to RhoDown by omitting many class descriptions, 059 * which are obviously too weak, because they violate 060 * domain/range restrictions. Furthermore, it makes use of disjoint 061 * classes in the knowledge base. 062 * 063 * Note: Some of the code has moved to {@link Utility} in a modified 064 * form to make it accessible for implementations of other refinement 065 * operators. These utility methods may be completed and carefully 066 * integrated back later. 067 * 068 * @author Jens Lehmann 069 * 070 */ 071@ComponentAnn(name = "rho refinement operator", shortName = "rho", version = 0.8) 072public class RhoDRDown extends RefinementOperatorAdapter implements Component, CustomHierarchyRefinementOperator, CustomStartRefinementOperator, ReasoningBasedRefinementOperator { 073 074 private static Logger logger = LoggerFactory.getLogger(RhoDRDown.class); 075 private final static Marker sparql_debug = new BasicMarkerFactory().getMarker("SD"); 076 077 private static final OWLClass OWL_THING = new OWLClassImpl( 078 OWLRDFVocabulary.OWL_THING.getIRI()); 079 080 @ConfigOption(description = "the reasoner to use") 081 private AbstractReasonerComponent reasoner; 082 083 // hierarchies 084 @NoConfigOption 085 private ClassHierarchy classHierarchy; 086 @NoConfigOption 087 private ObjectPropertyHierarchy objectPropertyHierarchy; 088 @NoConfigOption 089 private DatatypePropertyHierarchy dataPropertyHierarchy; 090 091 // domains and ranges 092 private Map<OWLObjectProperty,OWLClassExpression> opDomains = new TreeMap<>(); 093 private Map<OWLDataProperty,OWLClassExpression> dpDomains = new TreeMap<>(); 094 private Map<OWLObjectProperty,OWLClassExpression> opRanges = new TreeMap<>(); 095 096 // maximum number of fillers for each role 097 private Map<OWLObjectPropertyExpression, Integer> maxNrOfFillers = new TreeMap<>(); 098 // limit for cardinality restrictions (this makes sense if we e.g. have compounds with up to 099 // more than 200 atoms but we are only interested in atoms with certain characteristics and do 100 // not want something like e.g. >= 204 hasAtom.NOT Carbon-87; which blows up the search space 101 @ConfigOption(defaultValue = "5", description = "limit for cardinality restrictions (this makes sense if we e.g. have compounds with too many atoms)") 102 private int cardinalityLimit = 5; 103 104 // start concept (can be used to start from an arbitrary concept, needs 105 // to be Thing or NamedClass), note that when you use e.g. Compound as 106 // start class, then the algorithm should start the search with class 107 // Compound (and not with Thing), because otherwise concepts like 108 // NOT Carbon-87 will be returned which itself is not a subclass of Compound 109 @ConfigOption( 110 defaultValue = "owl:Thing", 111 description = "You can specify a start class for the algorithm") 112 private OWLClassExpression startClass = OWL_THING; 113 114 // the length of concepts of top refinements, the first values is 115 // for refinements of \rho_\top(\top), the second one for \rho_A(\top) 116 private int topRefinementsLength = 0; 117 private Map<OWLClassExpression, Integer> topARefinementsLength = new TreeMap<>(); 118 // M is finite and this value is the maximum length of any value in M 119 private int mMaxLength = 4; 120 121 // the sets M_\top and M_A 122 private Map<Integer,SortedSet<OWLClassExpression>> m = new TreeMap<>(); 123 private Map<OWLClassExpression,Map<Integer,SortedSet<OWLClassExpression>>> mA = new TreeMap<>(); 124 125 // @see MathOperations.getCombos 126 private Map<Integer, List<List<Integer>>> combos = new HashMap<>(); 127 128 // refinements of the top concept ordered by length 129 private Map<Integer, SortedSet<OWLClassExpression>> topRefinements = new TreeMap<>(); 130 private Map<OWLClassExpression,Map<Integer, SortedSet<OWLClassExpression>>> topARefinements = new TreeMap<>(); 131 132 // cumulated refinements of top (all from length one to the specified length) 133 private Map<Integer, TreeSet<OWLClassExpression>> topRefinementsCumulative = new HashMap<>(); 134 private Map<OWLClassExpression,Map<Integer, TreeSet<OWLClassExpression>>> topARefinementsCumulative = new TreeMap<>(); 135 136 // app_A set of applicable properties for a given class (separate for 137 // object properties, boolean datatypes, and double datatypes) 138 private Map<OWLClassExpression, Set<OWLObjectProperty>> appOP = new TreeMap<>(); 139 private Map<OWLClassExpression, Set<OWLDataProperty>> appBD = new TreeMap<>(); 140 private Map<OWLClassExpression, Set<OWLDataProperty>> appNumeric = new TreeMap<>(); 141 private Map<OWLClassExpression, Set<OWLDataProperty>> appSD = new TreeMap<>(); 142 143 // most general applicable properties 144 private Map<OWLClassExpression,Set<OWLObjectProperty>> mgr = new TreeMap<>(); 145 private Map<OWLClassExpression,Set<OWLDataProperty>> mgbd = new TreeMap<>(); 146 private Map<OWLClassExpression,Set<OWLDataProperty>> mgNumeric = new TreeMap<>(); 147 private Map<OWLClassExpression,Set<OWLDataProperty>> mgDT = new TreeMap<>(); 148 private Map<OWLClassExpression,Set<OWLDataProperty>> mgsd = new TreeMap<>(); 149 150 // numeric values splitter 151 private ValuesSplitter numericValuesSplitter; 152 153 // splits for double datatype properties in ascending order 154 private Map<OWLDataProperty,List<OWLLiteral>> splits = new TreeMap<>(); 155 156 @ConfigOption(description = "the number of generated split intervals for numeric types", defaultValue = "12") 157 private int maxNrOfSplits = 12; 158 159 // data structure for a simple frequent pattern matching preprocessing phase 160 @ConfigOption(defaultValue = "3", description = "minimum number an individual or literal has to be seen in the " + 161 "knowledge base before considering it for inclusion in concepts") 162 private int frequencyThreshold = CommonConfigOptions.valueFrequencyThresholdDefault; 163 // data structure with identified frequent values 164 private Map<OWLObjectPropertyExpression, Set<OWLIndividual>> frequentValues = new HashMap<>(); 165 // frequent data values 166 private Map<OWLDataProperty, Set<OWLLiteral>> frequentDataValues = new HashMap<>(); 167 168 // statistics 169 public long mComputationTimeNs = 0; 170 public long topComputationTimeNs = 0; 171 172 @ConfigOption(defaultValue="true") 173 private boolean applyAllFilter = true; 174 175 @ConfigOption(defaultValue="true", description = "throwing out all refinements with " + 176 "duplicate \u2203 r for any r") 177 private boolean applyExistsFilter = true; 178 179 @ConfigOption(description="support of universal restrictions (owl:allValuesFrom), e.g. \u2200 r.C ", defaultValue="true") 180 private boolean useAllConstructor = true; 181 182 @ConfigOption(description="support of existential restrictions (owl:someValuesFrom), e.g. \u2203 r.C ", defaultValue="true") 183 private boolean useExistsConstructor = true; 184 185 @ConfigOption(description="support of has value constructor (owl:hasValue), e.g. \u2203 r.{a} ", defaultValue="false") 186 private boolean useHasValueConstructor = false; 187 188 @ConfigOption(description = "support of has value constructor (owl:hasValue), e.g. \u2203 r.{20} ", defaultValue = "false") 189 private boolean useDataHasValueConstructor = false; 190 191 @ConfigOption(description="support of qualified cardinality restrictions (owl:minCardinality, owl:maxCardinality, owl:exactCardinality), e.g. \u2265 3 r.C ", defaultValue="true") 192 private boolean useCardinalityRestrictions = true; 193 194 @ConfigOption(description="support of local reflexivity of an object property expression (owl:hasSelf), e.g. \u2203 loves.Self for a narcissistic", defaultValue="false") 195 private boolean useHasSelf = false; 196 197 @ConfigOption(description="support of negation (owl:complementOf), e.g. \u00AC C ", defaultValue="true") 198 private boolean useNegation = true; 199 200 @ConfigOption(description="support of inverse object properties (owl:inverseOf), e.g. r\u207B.C ", defaultValue="false") 201 private boolean useInverse = false; 202 203 @ConfigOption(description="support of boolean datatypes (xsd:boolean), e.g. \u2203 r.{true} ", defaultValue="true") 204 private boolean useBooleanDatatypes = true; 205 206 @ConfigOption(description="support of numeric datatypes (xsd:int, xsd:double, ...), e.g. \u2203 r.{true} ", defaultValue="true") 207 private boolean useNumericDatatypes = true; 208 209 @ConfigOption(defaultValue="true") 210 private boolean useTimeDatatypes = true; 211 212 @ConfigOption(description="support of string datatypes (xsd:string), e.g. \u2203 r.{\"SOME_STRING\"} ",defaultValue="false") 213 private boolean useStringDatatypes = false; 214 215 @ConfigOption(defaultValue="true", description = "skip combination of intersection between disjoint classes") 216 private boolean disjointChecks = true; 217 218 @ConfigOption(defaultValue="true") 219 private boolean instanceBasedDisjoints = true; 220 221 @ConfigOption(defaultValue="false", description = "if enabled, generalise by removing parts of a disjunction") 222 private boolean dropDisjuncts = false; 223 224 @ConfigOption(description="universal restrictions on a property r are only used when there is already a cardinality and/or existential restriction on r", 225 defaultValue="true") 226 private boolean useSomeOnly = true; 227 228 // caches for reasoner queries 229 private Map<OWLClassExpression,Map<OWLClassExpression,Boolean>> cachedDisjoints = new TreeMap<>(); 230 231// private Map<OWLClass,Map<OWLClass,Boolean>> abDisjoint = new TreeMap<OWLClass,Map<OWLClass,Boolean>>(); 232// private Map<OWLClass,Map<OWLClass,Boolean>> notABDisjoint = new TreeMap<OWLClass,Map<OWLClass,Boolean>>(); 233// private Map<OWLClass,Map<OWLClass,Boolean>> notABMeaningful = new TreeMap<OWLClass,Map<OWLClass,Boolean>>(); 234 235 @ConfigOption(description = "whether to generate object complement while refining", defaultValue = "false") 236 private boolean useObjectValueNegation = false; 237 238 @ConfigOption(description = "class expression length metric (should match learning algorithm usage)", defaultValue = "default cel_metric") 239 private OWLClassExpressionLengthMetric lengthMetric = OWLClassExpressionLengthMetric.getDefaultMetric(); 240 private OWLDataFactory df = new OWLDataFactoryImpl(); 241 242 public RhoDRDown() {} 243 244 /** 245 * Copy constructor 246 */ 247 public RhoDRDown(RhoDRDown op) { 248 setApplyAllFilter(op.applyAllFilter); 249 setCardinalityLimit(op.cardinalityLimit); 250 setClassHierarchy(op.classHierarchy); 251 setObjectPropertyHierarchy(op.objectPropertyHierarchy); 252 setDataPropertyHierarchy(op.dataPropertyHierarchy); 253 setDropDisjuncts(op.dropDisjuncts); 254 setInstanceBasedDisjoints(op.instanceBasedDisjoints); 255 setReasoner(op.reasoner); 256 setStartClass(op.startClass); 257 setUseAllConstructor(op.useAllConstructor); 258 setUseCardinalityRestrictions(op.useCardinalityRestrictions); 259 setUseExistsConstructor(op.useExistsConstructor); 260 setUseNegation(op.useNegation); 261 setUseHasValueConstructor(op.useHasValueConstructor); 262 setUseObjectValueNegation(op.useObjectValueNegation); 263 setFrequencyThreshold(op.frequencyThreshold); 264 setUseDataHasValueConstructor(op.useDataHasValueConstructor); 265 setUseBooleanDatatypes(op.useBooleanDatatypes); 266 setUseStringDatatypes(op.useStringDatatypes); 267 setUseNumericDatatypes(op.useNumericDatatypes); 268 setUseTimeDatatypes(op.useTimeDatatypes); 269 initialized = false; 270 } 271 272 private <T> Set<T> frequentObjects(Collection<? extends Collection<T>> c, int frequencyThreshold) { 273 final int t = frequencyThreshold; 274 return c.stream() 275 .flatMap(Collection::stream) 276 .collect(Collectors.collectingAndThen(Collectors.groupingBy(Function.identity(), Collectors.counting()), 277 map -> { 278 map.values().removeIf(v -> v < t); 279 return map.keySet(); 280 })); 281 } 282 283 @Override 284 public void init() throws ComponentInitException { 285 /* 286 if(initialized) { 287 throw new ComponentInitException("Refinement operator cannot be initialised twice."); 288 } 289 */ 290 291 if (classHierarchy == null) classHierarchy = reasoner.getClassHierarchy(); 292 if (dataPropertyHierarchy == null) dataPropertyHierarchy = reasoner.getDatatypePropertyHierarchy(); 293 if (objectPropertyHierarchy == null) objectPropertyHierarchy = reasoner.getObjectPropertyHierarchy(); 294 295 logger.debug("classHierarchy: " + classHierarchy); 296 logger.debug("object properties: " + reasoner.getObjectProperties()); 297 298 // query reasoner for domains and ranges 299 // (because they are used often in the operator) 300 opDomains = reasoner.getObjectPropertyDomains(); 301 opRanges = reasoner.getObjectPropertyRanges(); 302 dpDomains = reasoner.getDataPropertyDomains(); 303 304 // r. some {ind} 305 if (useHasValueConstructor) { 306 for (OWLObjectProperty op : objectPropertyHierarchy.getEntities()) { 307 308 Map<OWLIndividual, SortedSet<OWLIndividual>> propertyMembers = reasoner.getPropertyMembers(op); 309 310 // compute the frequency of all individuals used as object and filter by threshold 311 Set<OWLIndividual> frequentInds = frequentObjects(propertyMembers.values(), frequencyThreshold); 312 frequentValues.put(op, frequentInds); 313 314 // inv(r). some {ind} 315 if(useInverse) { 316 // it's a bit easier for inverse properties since we have a mapping from each individual to 317 // all related individuals, thus, the freuqncy of each individual as subject is just the number 318 // of objects 319 frequentInds = propertyMembers.entrySet().stream().collect(Collectors.collectingAndThen( 320 Collectors.toMap(Entry::getKey, e -> e.getValue().size()), map -> { 321 map.values().removeIf(v -> v < frequencyThreshold); 322 return map.keySet(); 323 })); 324 frequentValues.put(op.getInverseProperty(), frequentInds); 325 } 326 } 327 } 328 329 // r. some {lit} 330 if(useDataHasValueConstructor) { 331 for(OWLDataProperty dp : dataPropertyHierarchy.getEntities()) { 332 Set<OWLLiteral> frequentLiterals = frequentObjects(reasoner.getDatatypeMembers(dp).values(), frequencyThreshold); 333 frequentDataValues.put(dp, frequentLiterals); 334 } 335 } 336 337 // compute splits for numeric data properties 338 if(useNumericDatatypes) { 339 if(reasoner instanceof SPARQLReasoner 340 && !((SPARQLReasoner)reasoner).isUseGenericSplitsCode()) { 341 // TODO SPARQL support for splits 342 logger.warn("Numeric Facet restrictions are not (yet) implemented for " + AnnComponentManager.getName(reasoner) + ", option ignored"); 343 } else { 344 // create default splitter if none was set 345 if(numericValuesSplitter == null) { 346 numericValuesSplitter = new DefaultNumericValuesSplitter(reasoner, df, maxNrOfSplits); 347 } 348 splits.putAll(numericValuesSplitter.computeSplits()); 349 if (logger.isDebugEnabled()) { 350 logger.debug( sparql_debug, "Numeric Splits: {}", splits); 351 } 352 } 353 } 354 355 // compute splits for time data properties 356 if (useTimeDatatypes) { 357 if(reasoner instanceof SPARQLReasoner 358 && !((SPARQLReasoner)reasoner).isUseGenericSplitsCode()) { 359 // TODO SPARQL support for splits 360 logger.warn("Time based Facet restrictions are not (yet) implemented for " + AnnComponentManager.getName(reasoner) + ", option ignored"); 361 } else { 362 ValuesSplitter splitter = new DefaultDateTimeValuesSplitter(reasoner, df, maxNrOfSplits); 363 splits.putAll(splitter.computeSplits()); 364 } 365 } 366 367 // determine the maximum number of fillers for each role 368 // (up to a specified cardinality maximum) 369 if(useCardinalityRestrictions) { 370 if(reasoner instanceof SPARQLReasoner) { 371 logger.warn("Cardinality restrictions in Sparql not fully implemented, defaulting to 10."); 372 } 373 for(OWLObjectProperty op : objectPropertyHierarchy.getEntities()) { 374 if(reasoner instanceof SPARQLReasoner) { 375 // TODO SPARQL support for cardinalities 376 maxNrOfFillers.put(op, 10); 377 } else { 378 int maxFillers = Math.min(cardinalityLimit, 379 reasoner.getPropertyMembers(op).values().stream() 380 .mapToInt(Set::size) 381 .max().orElse(0)); 382 maxNrOfFillers.put(op, maxFillers); 383 384// int percentile95 = (int) new Percentile().evaluate( 385// reasoner.getPropertyMembers(op).entrySet().stream() 386// .mapToDouble(entry -> (double)entry.getValue().size()) 387// .toArray(), 95); 388// System.out.println("Prop " + op); 389// System.out.println("max: " + maxFillers); 390// System.out.println("95th: " + percentile95); 391 392 // handle inverse properties 393 if(useInverse) { 394 maxFillers = 0; 395 396 Multimap<OWLIndividual, OWLIndividual> map = HashMultimap.create(); 397 398 for (Entry<OWLIndividual, SortedSet<OWLIndividual>> entry : reasoner.getPropertyMembers(op).entrySet()) { 399 OWLIndividual subject = entry.getKey(); 400 SortedSet<OWLIndividual> objects = entry.getValue(); 401 402 for (OWLIndividual obj : objects) { 403 map.put(obj, subject); 404 } 405 } 406 407 for (Entry<OWLIndividual, Collection<OWLIndividual>> entry : map.asMap().entrySet()) { 408 Collection<OWLIndividual> inds = entry.getValue(); 409 if (inds.size() > maxFillers) 410 maxFillers = inds.size(); 411 if (maxFillers >= cardinalityLimit) { 412 maxFillers = cardinalityLimit; 413 break; 414 } 415 } 416 maxNrOfFillers.put(op.getInverseProperty(), maxFillers); 417 } 418 } 419 } 420 } 421 422 startClass = OWLAPIUtils.classExpressionPropertyExpanderChecked(startClass, reasoner, df, logger); 423 424 if(classHierarchy == null) { 425 classHierarchy = reasoner.getClassHierarchy(); 426 } 427 if(objectPropertyHierarchy == null) { 428 objectPropertyHierarchy = reasoner.getObjectPropertyHierarchy(); 429 } 430 if(dataPropertyHierarchy == null) { 431 dataPropertyHierarchy = reasoner.getDatatypePropertyHierarchy(); 432 } 433 434 initialized = true; 435 } 436 437 protected void isFinal() { 438 if (initialized) throw new IllegalStateException(this.getClass() + " already initialised in " + Thread.currentThread().getStackTrace()[2].getMethodName()); 439 } 440 441 @Override 442 public Set<OWLClassExpression> refine(OWLClassExpression concept) { 443 throw new RuntimeException(); 444 } 445 446 @Override 447 public Set<OWLClassExpression> refine(OWLClassExpression description, int maxLength) { 448 // check that maxLength is valid 449 if(maxLength < OWLClassExpressionUtils.getLength(description, lengthMetric)) { 450 throw new Error("length has to be at least class expression length (class expression: " + description + " with length " + OWLClassExpressionUtils.getLength(description, lengthMetric) +", and max length: " + maxLength + ")"); 451 } 452 return refine(description, maxLength, null, startClass); 453 } 454 455 @Override 456 public Set<OWLClassExpression> refine(OWLClassExpression description, int maxLength, 457 List<OWLClassExpression> knownRefinements) { 458 return refine(description, maxLength, knownRefinements, startClass); 459 } 460 461 @SuppressWarnings({"unchecked"}) 462 public Set<OWLClassExpression> refine(OWLClassExpression description, int maxLength, 463 List<OWLClassExpression> knownRefinements, OWLClassExpression currDomain) { 464 465// System.out.println("|- " + description + " " + currDomain + " " + maxLength); 466 467 // actions needing to be performed if this is the first time the 468 // current domain is used 469 if(!currDomain.isOWLThing() && !topARefinementsLength.containsKey(currDomain)){ 470 topARefinementsLength.put(currDomain, 0); 471 } 472 473 // check whether using list or set makes more sense 474 // here; and whether HashSet or TreeSet should be used 475 // => TreeSet because duplicates are possible 476 Set<OWLClassExpression> refinements = new TreeSet<>(); 477 478 // used as temporary variable 479 Set<OWLClassExpression> tmp; 480 481 if(description.isOWLThing()) { 482 // extends top refinements if necessary 483 if(currDomain.isOWLThing()) { 484 if(maxLength>topRefinementsLength) 485 computeTopRefinements(maxLength); 486 refinements = (TreeSet<OWLClassExpression>) topRefinementsCumulative.get(maxLength).clone(); 487 } else { 488 if(maxLength>topARefinementsLength.get(currDomain)) { 489 computeTopRefinements(maxLength, currDomain); 490 } 491 refinements = (TreeSet<OWLClassExpression>) topARefinementsCumulative.get(currDomain).get(maxLength).clone(); 492 } 493// refinements.addAll(classHierarchy.getMoreSpecialConcepts(description)); 494 } else if(description.isOWLNothing()) { 495 // cannot be further refined 496 } else if(!description.isAnonymous()) { 497 refinements.addAll(classHierarchy.getSubClasses(description, true)); 498 refinements.remove(df.getOWLNothing()); 499 } else if (description instanceof OWLObjectComplementOf) { 500 OWLClassExpression operand = ((OWLObjectComplementOf) description).getOperand(); 501 if(!operand.isAnonymous()){ 502 tmp = classHierarchy.getSuperClasses(operand, true); 503 504 for(OWLClassExpression c : tmp) { 505 if(!c.isOWLThing()){ 506 refinements.add(df.getOWLObjectComplementOf(c)); 507 } 508 } 509 } 510 } else if (description instanceof OWLObjectIntersectionOf) { 511 List<OWLClassExpression> operands = ((OWLObjectIntersectionOf) description).getOperandsAsList(); 512 // refine one of the elements 513 for(OWLClassExpression child : operands) { 514 // refine the child; the new max length is the current max length minus 515 // the currently considered concept plus the length of the child 516 // TODO: add better explanation 517 int length = OWLClassExpressionUtils.getLength(description, lengthMetric); 518 int childLength = OWLClassExpressionUtils.getLength(child, lengthMetric); 519 tmp = refine(child, maxLength - length + childLength, null, currDomain); 520 521 // create new intersection 522 for(OWLClassExpression c : tmp) { 523 if(!useSomeOnly || isCombinable(description, c)) { 524 List<OWLClassExpression> newChildren = new ArrayList<>(operands); 525 newChildren.add(c); 526 newChildren.remove(child); 527 Collections.sort(newChildren); 528 OWLClassExpression mc = new OWLObjectIntersectionOfImplExt(newChildren); 529 530 // clean concept and transform it to ordered negation normal form 531 // (non-recursive variant because only depth 1 was modified) 532 mc = ConceptTransformation.cleanConceptNonRecursive(mc); 533 mc = ConceptTransformation.nnf(mc); 534 535 // check whether the intersection is OK (sanity checks), then add it 536 if(checkIntersection((OWLObjectIntersectionOf) mc)) 537 refinements.add(mc); 538 } 539 } 540 541 } 542 543 } else if (description instanceof OWLObjectUnionOf) { 544 // refine one of the elements 545 List<OWLClassExpression> operands = ((OWLObjectUnionOf) description).getOperandsAsList(); 546 for(OWLClassExpression child : operands) { 547// System.out.println("union child: " + child + " " + maxLength + " " + description.getLength() + " " + child.getLength()); 548 549 // refine child 550 int length = OWLClassExpressionUtils.getLength(description, lengthMetric); 551 int childLength = OWLClassExpressionUtils.getLength(child, lengthMetric); 552 tmp = refine(child, maxLength - length + childLength, null, currDomain); 553 554 // construct union (see above) 555 for(OWLClassExpression c : tmp) { 556 List<OWLClassExpression> newChildren = new ArrayList<>(operands); 557 newChildren.remove(child); 558 newChildren.add(c); 559 Collections.sort(newChildren); 560 OWLClassExpression md = new OWLObjectUnionOfImplExt(newChildren); 561 562 // transform to ordered negation normal form 563 md = ConceptTransformation.nnf(md); 564 // note that we do not have to call clean here because a disjunction will 565 // never be nested in another disjunction in this operator 566 567 refinements.add(md); 568 } 569 } 570 571 // if enabled, we can remove elements of the disjunction 572 if(dropDisjuncts) { 573 // A1 OR A2 => {A1,A2} 574 if(operands.size() == 2) { 575 refinements.add(operands.get(0)); 576 refinements.add(operands.get(1)); 577 } else { 578 // copy children list and remove a different element in each turn 579 for (OWLClassExpression op : operands) { 580 List<OWLClassExpression> newChildren = new LinkedList<>(operands); 581 newChildren.remove(op); 582 OWLObjectUnionOf md = new OWLObjectUnionOfImplExt(newChildren); 583 refinements.add(md); 584 } 585 } 586 } 587 588 } else if (description instanceof OWLObjectSomeValuesFrom) { 589 OWLObjectPropertyExpression role = ((OWLObjectSomeValuesFrom) description).getProperty(); 590 OWLClassExpression filler = ((OWLObjectSomeValuesFrom) description).getFiller(); 591 592 // we need the context of the filler which is either the domain (in case of an inverse property) or the range of p 593 OWLClassExpression domain = role.isAnonymous() 594 ? opDomains.get(role.getNamedProperty()) // inv(p) -> D = domain(p) 595 : opRanges.get(role.asOWLObjectProperty()); // p -> D = range(p) 596 597 // rule 1: EXISTS r.D => EXISTS r.E 598 tmp = refine(filler, maxLength-lengthMetric.objectSomeValuesLength-lengthMetric.objectProperyLength, null, domain); 599 600 for(OWLClassExpression c : tmp){ 601 refinements.add(df.getOWLObjectSomeValuesFrom(role, c)); 602 } 603 604 // rule 2: EXISTS r.D => EXISTS s.D or EXISTS r^-1.D => EXISTS s^-1.D 605 Set<OWLObjectProperty> moreSpecialRoles = objectPropertyHierarchy.getMoreSpecialRoles(role.getNamedProperty()); 606 607 for (OWLObjectProperty moreSpecialRole : moreSpecialRoles) { 608 refinements.add(df.getOWLObjectSomeValuesFrom(moreSpecialRole, filler)); 609 } 610 611 // rule 3: EXISTS r.D => >= 2 r.D 612 // (length increases by 1 so we have to check whether max length is sufficient) 613 if (useCardinalityRestrictions && 614 maxLength > OWLClassExpressionUtils.getLength(description, lengthMetric) && 615 maxNrOfFillers.get(role) > 1) { 616 refinements.add(df.getOWLObjectMinCardinality(2, role, filler)); 617 618 } 619 620 // rule 4: EXISTS r.TOP => EXISTS r.{value} 621 if(useHasValueConstructor && filler.isOWLThing()){ // && !role.isAnonymous()) { 622 // watch out for frequent patterns 623 Set<OWLIndividual> frequentInds = frequentValues.get(role); 624 if(frequentInds != null) { 625 for(OWLIndividual ind : frequentInds) { 626 OWLObjectHasValue ovr = df.getOWLObjectHasValue(role, ind); 627 refinements.add(ovr); 628 // rule 4b : EXISTS r.TOP => EXISTS r.not {value} 629 if (useObjectValueNegation) { 630 if (maxLength > OWLClassExpressionUtils.getLength(description, lengthMetric)) { 631 refinements.add(df.getOWLObjectSomeValuesFrom(role, df.getOWLObjectComplementOf(df.getOWLObjectOneOf(ind)))); 632 } 633 } 634 } 635 } 636 } 637 638 } else if (description instanceof OWLObjectAllValuesFrom) { 639 refinements.addAll(refine((OWLObjectAllValuesFrom) description, maxLength)); 640 } else if (description instanceof OWLObjectCardinalityRestriction) { 641 OWLObjectPropertyExpression role = ((OWLObjectCardinalityRestriction) description).getProperty(); 642 OWLClassExpression filler = ((OWLObjectCardinalityRestriction) description).getFiller(); 643 OWLClassExpression range = role.isAnonymous() ? opDomains.get(role.getNamedProperty()) : opRanges.get(role); 644 int cardinality = ((OWLObjectCardinalityRestriction) description).getCardinality(); 645 if(description instanceof OWLObjectMaxCardinality) { 646 // rule 1: <= x r.C => <= x r.D 647 if(useNegation || cardinality > 0){ 648 tmp = refine(filler, maxLength-lengthMetric.objectCardinalityLength-lengthMetric.objectProperyLength, null, range); 649 650 for(OWLClassExpression d : tmp) { 651 refinements.add(df.getOWLObjectMaxCardinality(cardinality,role,d)); 652 } 653 } 654 655 // rule 2: <= x r.C => <= (x-1) r.C 656// int number = max.getNumber(); 657 if((useNegation && cardinality > 1) || (!useNegation && cardinality > 2)){ 658 refinements.add(df.getOWLObjectMaxCardinality(cardinality-1,role,filler)); 659 } 660 661 } else if(description instanceof OWLObjectMinCardinality) { 662 tmp = refine(filler, maxLength-lengthMetric.objectCardinalityLength-lengthMetric.objectProperyLength, null, range); 663 664 for(OWLClassExpression d : tmp) { 665 refinements.add(df.getOWLObjectMinCardinality(cardinality,role,d)); 666 } 667 668 // >= x r.C => >= (x+1) r.C 669// int number = min.getNumber(); 670 if(cardinality < maxNrOfFillers.get(role)){ 671 refinements.add(df.getOWLObjectMinCardinality(cardinality+1,role,filler)); 672 } 673 } else if(description instanceof OWLObjectExactCardinality) { 674 tmp = refine(filler, maxLength-lengthMetric.objectCardinalityLength-lengthMetric.objectProperyLength, null, range); 675 676 for(OWLClassExpression d : tmp) { 677 refinements.add(df.getOWLObjectExactCardinality(cardinality,role,d)); 678 } 679 680 // >= x r.C => >= (x+1) r.C 681// int number = min.getNumber(); 682 if(cardinality < maxNrOfFillers.get(role)){ 683 refinements.add(df.getOWLObjectExactCardinality(cardinality+1,role,filler)); 684 } 685 } 686 } else if (description instanceof OWLDataSomeValuesFrom) { 687 OWLDataProperty dp = ((OWLDataSomeValuesFrom) description).getProperty().asOWLDataProperty(); 688 OWLDataRange dr = ((OWLDataSomeValuesFrom) description).getFiller(); 689 if(dr instanceof OWLDatatypeRestriction){ 690 OWLDatatype datatype = ((OWLDatatypeRestriction) dr).getDatatype(); 691 Set<OWLFacetRestriction> facetRestrictions = ((OWLDatatypeRestriction) dr).getFacetRestrictions(); 692 693 OWLDatatypeRestriction newDatatypeRestriction = null; 694 if(OWLAPIUtils.isNumericDatatype(datatype) || OWLAPIUtils.dtDatatypes.contains(datatype)){ 695 for (OWLFacetRestriction facetRestriction : facetRestrictions) { 696 OWLFacet facet = facetRestriction.getFacet(); 697 698 OWLLiteral value = facetRestriction.getFacetValue(); 699 700 if(facet == OWLFacet.MAX_INCLUSIVE){ 701 // find out which split value was used 702 int splitIndex = splits.get(dp).lastIndexOf(value); 703 if(splitIndex == -1) 704 throw new Error("split error"); 705 int newSplitIndex = splitIndex - 1; 706 if(newSplitIndex >= 0) { 707 OWLLiteral newValue = splits.get(dp).get(newSplitIndex); 708 newDatatypeRestriction = asDatatypeRestriction(dp, newValue, facet); 709 } 710 } else if(facet == OWLFacet.MIN_INCLUSIVE){ 711 // find out which split value was used 712 int splitIndex = splits.get(dp).lastIndexOf(value); 713 if(splitIndex == -1) 714 throw new Error("split error"); 715 int newSplitIndex = splitIndex + 1; 716 if(newSplitIndex < splits.get(dp).size()) { 717 OWLLiteral newValue = splits.get(dp).get(newSplitIndex); 718 newDatatypeRestriction = asDatatypeRestriction(dp, newValue, facet); 719 } 720 } 721 } 722 } 723 if(newDatatypeRestriction != null){ 724 refinements.add(df.getOWLDataSomeValuesFrom(dp, newDatatypeRestriction)); 725 } 726 } 727 728 } else if (description instanceof OWLDataHasValue) { 729 OWLDataPropertyExpression dp = ((OWLDataHasValue) description).getProperty(); 730 OWLLiteral value = ((OWLDataHasValue) description).getFiller(); 731 732 if(!dp.isAnonymous()){ 733 Set<OWLDataProperty> subDPs = dataPropertyHierarchy.getMoreSpecialRoles(dp.asOWLDataProperty()); 734 for(OWLDataProperty subDP : subDPs) { 735 refinements.add(df.getOWLDataHasValue(subDP, value)); 736 } 737 } 738 } 739 740 // if a refinement is not Bottom, Top, ALL r.Bottom a refinement of top can be appended 741 if(!description.isOWLThing() && !description.isOWLNothing() 742 && !(description instanceof OWLObjectAllValuesFrom && ((OWLObjectAllValuesFrom)description).getFiller().isOWLNothing())) { 743 // -1 because of the AND symbol which is appended 744 int topRefLength = maxLength - OWLClassExpressionUtils.getLength(description, lengthMetric) - 1; 745 746 // maybe we have to compute new top refinements here 747 if(currDomain.isOWLThing()) { 748 if(topRefLength > topRefinementsLength) 749 computeTopRefinements(topRefLength); 750 } else if(topRefLength > topARefinementsLength.get(currDomain)) 751 computeTopRefinements(topRefLength, currDomain); 752 753 if(topRefLength>0) { 754 Set<OWLClassExpression> topRefs; 755 if(currDomain.isOWLThing()) 756 topRefs = topRefinementsCumulative.get(topRefLength); 757 else 758 topRefs = topARefinementsCumulative.get(currDomain).get(topRefLength); 759 760 for(OWLClassExpression c : topRefs) { 761 // true if refinement should be skipped due to filters, 762 // false otherwise 763 boolean skip = false; 764 765 // if a refinement of of the form ALL r, we check whether ALL r 766 // does not occur already 767 if(applyAllFilter) { 768 if(c instanceof OWLObjectAllValuesFrom) { 769 if(description instanceof OWLNaryBooleanClassExpression){ 770 for(OWLClassExpression child : ((OWLNaryBooleanClassExpression) description).getOperands()) { 771 if(child instanceof OWLObjectAllValuesFrom) { 772 OWLObjectPropertyExpression r1 = ((OWLObjectAllValuesFrom) c).getProperty(); 773 OWLObjectPropertyExpression r2 = ((OWLObjectAllValuesFrom) child).getProperty(); 774 if(r1.equals(r2)){ 775 skip = true; 776 break; 777 } 778 } 779 } 780 } 781 } 782 } 783 784 // we only add \forall r.C to an intersection if there is 785 // already some existential restriction \exists r.C 786 if(useSomeOnly) { 787 skip = !isCombinable(description, c); 788 } 789 790 // check for double datatype properties 791 /* 792 if(c instanceof DatatypeSomeRestriction && 793 description instanceof DatatypeSomeRestriction) { 794 DataRange dr = ((DatatypeSomeRestriction)c).getDataRange(); 795 DataRange dr2 = ((DatatypeSomeRestriction)description).getDataRange(); 796 // it does not make sense to have statements like height >= 1.8 AND height >= 1.7 797 if((dr instanceof DoubleMaxValue && dr2 instanceof DoubleMaxValue) 798 ||(dr instanceof DoubleMinValue && dr2 instanceof DoubleMinValue)) 799 skip = true; 800 }*/ 801 802 // perform a disjointness check when named classes are added; 803 // this can avoid a lot of superfluous computation in the algorithm e.g. 804 // when A1 looks good, so many refinements of the form (A1 OR (A2 AND A3)) 805 // are generated which are all equal to A1 due to disjointness of A2 and A3 806 if(disjointChecks && !c.isAnonymous() && !description.isAnonymous() && isDisjoint(description, c)) { 807 skip = true; 808// System.out.println(c + " ignored when refining " + description); 809 } 810 811 if(!skip) { 812 List<OWLClassExpression> operands = Lists.newArrayList(description, c); 813 Collections.sort(operands); 814 OWLObjectIntersectionOf mc = new OWLObjectIntersectionOfImplExt(operands); 815 816 // clean and transform to ordered negation normal form 817 mc = (OWLObjectIntersectionOf) ConceptTransformation.cleanConceptNonRecursive(mc); 818 mc = (OWLObjectIntersectionOf) ConceptTransformation.nnf(mc); 819 820 // last check before intersection is added 821 if(checkIntersection(mc)) 822 refinements.add(mc); 823 } 824 } 825 } 826 } 827 828// for(OWLClassExpression refinement : refinements) { 829// if((refinement instanceof Intersection || refinement instanceof Union) && refinement.getChildren().size()<2) { 830// System.out.println(OWLClassExpression + " " + refinement + " " + currDomain + " " + maxLength); 831// System.exit(0); 832// } 833// } 834// System.out.println("++++++++\nREFINING: " + description + " maxLength:" + maxLength); 835// System.out.println(refinements); 836 return refinements; 837 } 838 839 private boolean isCombinable(OWLClassExpression ce, OWLClassExpression child) { 840 boolean combinable = true; 841 842 if(child instanceof OWLObjectAllValuesFrom) { 843 boolean tmp = false; 844 OWLObjectPropertyExpression r1 = ((OWLObjectAllValuesFrom) child).getProperty(); 845 if(ce instanceof OWLObjectIntersectionOf){ 846 for(OWLClassExpression operand : ((OWLObjectIntersectionOf) ce).getOperands()) { 847 if(child instanceof OWLObjectSomeValuesFrom) { 848 OWLObjectPropertyExpression r2 = ((OWLObjectSomeValuesFrom) operand).getProperty(); 849 if(r1.equals(r2)){ 850 tmp = true; 851 break; 852 } 853 } 854 } 855 } else if(ce instanceof OWLObjectSomeValuesFrom) { 856 OWLObjectPropertyExpression r2 = ((OWLObjectSomeValuesFrom) ce).getProperty(); 857 if(r1.equals(r2)){ 858 tmp = true; 859 } 860 } 861 combinable = tmp; 862 } 863 return combinable; 864 } 865 866 private Set<OWLClassExpression> refine(OWLObjectAllValuesFrom ce, int maxLength) { 867 Set<OWLClassExpression> refinements = new HashSet<>(); 868 869 OWLObjectPropertyExpression role = ce.getProperty(); 870 OWLClassExpression filler = ce.getFiller(); 871 OWLClassExpression range = role.isAnonymous() ? opDomains.get(role.getNamedProperty()) : opRanges.get(role); 872 873 // rule 1: ALL r.D => ALL r.E 874 Set<OWLClassExpression> tmp = refine(filler, maxLength-lengthMetric.objectAllValuesLength-lengthMetric.objectProperyLength, null, range); 875 876 for(OWLClassExpression c : tmp) { 877 refinements.add(df.getOWLObjectAllValuesFrom(role, c)); 878 } 879 880 // rule 2: ALL r.D => ALL r.BOTTOM if D is a most specific atomic concept 881 if(!filler.isOWLNothing() && !filler.isAnonymous() && tmp.size()==0) { 882 refinements.add(df.getOWLObjectAllValuesFrom(role, df.getOWLNothing())); 883 } 884 885 // rule 3: ALL r.D => ALL s.D or ALL r^-1.D => ALL s^-1.D 886 Set<OWLObjectProperty> subProperties = objectPropertyHierarchy.getMoreSpecialRoles(role.getNamedProperty()); 887 888 for (OWLObjectProperty subProperty : subProperties) { 889 refinements.add(df.getOWLObjectAllValuesFrom(subProperty, filler)); 890 } 891 892 // rule 4: ALL r.D => <= (maxFillers-1) r.D 893 // (length increases by 1 so we have to check whether max length is sufficient) 894 // => commented out because this is actually not a downward refinement 895// if(useCardinalityRestrictions) { 896// if(maxLength > ce.getLength() && maxNrOfFillers.get(ar)>1) { 897// ObjectMaxCardinalityRestriction max = new ObjectMaxCardinalityRestriction(maxNrOfFillers.get(ar)-1,role,description.getChild(0)); 898// refinements.add(max); 899// } 900// } 901 902 return refinements; 903 } 904 905 // when a child of an intersection is refined and reintegrated into the 906 // intersection, we can perform some sanity checks; 907 // method returns true if everything is OK and false otherwise 908 // TODO: can be implemented more efficiently if the newly added child 909 // is given as parameter 910 public static boolean checkIntersection(OWLObjectIntersectionOf intersection) { 911 // rule 1: max. restrictions at most once 912 boolean maxDoubleOccurence = false; 913 // rule 2: min restrictions at most once 914 boolean minDoubleOccurence = false; 915 // rule 3: no double occurences of boolean datatypes 916 TreeSet<OWLDataProperty> occuredDP = new TreeSet<>(); 917 // rule 4: no double occurences of hasValue restrictions 918 TreeSet<OWLObjectPropertyExpression> occuredVR = new TreeSet<>(); 919 // rule 5: max. restrictions at most once 920 boolean maxIntOccurence = false; 921 // rule 6: min restrictions at most once 922 boolean minIntOccurence = false; 923 924 for(OWLClassExpression child : intersection.getOperands()) { 925 if(child instanceof OWLDataSomeValuesFrom) { 926 OWLDataRange dr = ((OWLDataSomeValuesFrom)child).getFiller(); 927 if(dr instanceof OWLDatatypeRestriction){ 928 OWLDatatype datatype = ((OWLDatatypeRestriction) dr).getDatatype(); 929 for (OWLFacetRestriction facetRestriction : ((OWLDatatypeRestriction) dr).getFacetRestrictions()) { 930 OWLFacet facet = facetRestriction.getFacet(); 931 if (facet == OWLFacet.MIN_INCLUSIVE) { 932 if (datatype.isDouble()) { 933 if (minDoubleOccurence) { 934 return false; 935 } else { 936 minDoubleOccurence = true; 937 } 938 } else if (datatype.isInteger()) { 939 if (minIntOccurence) { 940 return false; 941 } else { 942 minIntOccurence = true; 943 } 944 } 945 } else if (facet == OWLFacet.MAX_INCLUSIVE) { 946 if (datatype.isDouble()) { 947 if (maxDoubleOccurence) { 948 return false; 949 } else { 950 maxDoubleOccurence = true; 951 } 952 } else if (datatype.isInteger()) { 953 if (maxIntOccurence) { 954 return false; 955 } else { 956 maxIntOccurence = true; 957 } 958 } 959 } 960 } 961 } 962 } else if(child instanceof OWLDataHasValue) { 963 OWLDataProperty dp = ((OWLDataHasValue) child).getProperty().asOWLDataProperty(); 964// System.out.println("dp: " + dp); 965 // return false if the boolean property exists already 966 if(!occuredDP.add(dp)) 967 return false; 968 } else if(child instanceof OWLObjectHasValue) { 969 OWLObjectPropertyExpression op = ((OWLObjectHasValue) child).getProperty(); 970 if(!occuredVR.add(op)) 971 return false; 972 } 973// System.out.println(child.getClass()); 974 } 975 return true; 976 } 977 978 /** 979 * By default, the operator does not specialize e.g. (A or B) to A, because 980 * it only guarantees weak completeness. Under certain circumstances, e.g. 981 * refinement of a fixed given concept, it can be useful to allow such 982 * refinements, which can be done by passing the parameter true to this method. 983 * @param dropDisjuncts Whether to remove disjuncts in refinement process. 984 */ 985 public void setDropDisjuncts(boolean dropDisjuncts) { 986 this.dropDisjuncts = dropDisjuncts; 987 } 988 989 private void computeTopRefinements(int maxLength) { 990 computeTopRefinements(maxLength, null); 991 } 992 993 private void computeTopRefinements(int maxLength, OWLClassExpression domain) { 994 long topComputationTimeStartNs = System.nanoTime(); 995// System.out.println("computing top refinements for " + domain + " up to length " + maxLength); 996 997 if(domain == null && m.size() == 0) 998 computeM(); 999 1000 if(domain != null && !mA.containsKey(domain)) 1001 computeM(domain); 1002 1003 int refinementsLength; 1004 1005 if(domain == null) { 1006 refinementsLength = topRefinementsLength; 1007 } else { 1008 if(!topARefinementsLength.containsKey(domain)) 1009 topARefinementsLength.put(domain,0); 1010 1011 refinementsLength = topARefinementsLength.get(domain); 1012 } 1013 1014 // compute all possible combinations of the disjunction 1015 for(int i = refinementsLength+1; i <= maxLength; i++) { 1016 combos.put(i,MathOperations.getCombos(i, mMaxLength)); 1017 1018 // initialise the refinements with empty sets 1019 if(domain == null) { 1020 topRefinements.put(i, new TreeSet<>()); 1021 } else { 1022 if(!topARefinements.containsKey(domain)) 1023 topARefinements.put(domain, new TreeMap<>()); 1024 topARefinements.get(domain).put(i, new TreeSet<>()); 1025 } 1026 1027 for(List<Integer> combo : combos.get(i)) { 1028 1029 // combination is a single number => try to use M 1030 if(combo.size()==1) { 1031 // note we cannot use "put" instead of "addAll" because there 1032 // can be several combos for one length 1033 if(domain == null) 1034 topRefinements.get(i).addAll(m.get(i)); 1035 else 1036 topARefinements.get(domain).get(i).addAll(mA.get(domain).get(i)); 1037 // combinations has several numbers => generate disjunct 1038 } else { 1039 1040 // check whether the combination makes sense, i.e. whether 1041 // all lengths mentioned in it have corresponding elements 1042 // e.g. when negation is deactivated there won't be elements of 1043 // length 2 in M 1044 boolean validCombo = true; 1045 for(Integer j : combo) { 1046 if((domain == null && m.get(j).size()==0) || 1047 (domain != null && mA.get(domain).get(j).size()==0)) 1048 validCombo = false; 1049 } 1050 1051 if(validCombo) { 1052 1053 SortedSet<OWLObjectUnionOf> baseSet = new TreeSet<>(); 1054 for(Integer j : combo) { 1055 if(domain == null) 1056 baseSet = MathOperations.incCrossProduct(baseSet, m.get(j)); 1057 else 1058 baseSet = MathOperations.incCrossProduct(baseSet, mA.get(domain).get(j)); 1059 } 1060 1061 // convert all concepts in ordered negation normal form 1062 Set<OWLObjectUnionOf> tmp = new HashSet<>(); 1063 for(OWLClassExpression concept : baseSet) { 1064 tmp.add((OWLObjectUnionOf) ConceptTransformation.nnf(concept)); 1065 } 1066 baseSet = new TreeSet<>(tmp); 1067 1068 // apply the exists filter (throwing out all refinements with 1069 // double \exists r for any r) 1070 // TODO: similar filtering can be done for boolean datatype 1071 // properties 1072 if(applyExistsFilter) { 1073 baseSet.removeIf(MathOperations::containsDoubleObjectSomeRestriction); 1074 } 1075 1076 // add computed refinements 1077 if(domain == null) { 1078 topRefinements.get(i).addAll(baseSet); 1079 } else { 1080 topARefinements.get(domain).get(i).addAll(baseSet); 1081 } 1082 } 1083 } 1084 } 1085 1086 // create cumulative versions of refinements such that they can 1087 // be accessed easily 1088 TreeSet<OWLClassExpression> cumulativeRefinements = new TreeSet<>(); 1089 for(int j=1; j<=i; j++) { 1090 if(domain == null) { 1091 cumulativeRefinements.addAll(topRefinements.get(j)); 1092 } else { 1093 cumulativeRefinements.addAll(topARefinements.get(domain).get(j)); 1094 } 1095 } 1096 1097 if(domain == null) { 1098 topRefinementsCumulative.put(i, cumulativeRefinements); 1099 } else { 1100 if(!topARefinementsCumulative.containsKey(domain)) 1101 topARefinementsCumulative.put(domain, new TreeMap<>()); 1102 topARefinementsCumulative.get(domain).put(i, cumulativeRefinements); 1103 } 1104 } 1105 1106 // register new top refinements length 1107 if(domain == null) 1108 topRefinementsLength = maxLength; 1109 else 1110 topARefinementsLength.put(domain,maxLength); 1111 1112 topComputationTimeNs += System.nanoTime() - topComputationTimeStartNs; 1113 1114// if(domain == null) { 1115// System.out.println("computed top refinements up to length " + topRefinementsLength + ": " + topRefinementsCumulative.get(maxLength)); 1116// } else { 1117// System.out.println("computed top refinements up to length " + topARefinementsLength + ": (domain: "+domain+"): " + topARefinementsCumulative.get(domain).get(maxLength)); 1118// } 1119 } 1120 1121 // compute M_\top 1122 private void computeM() { 1123 long mComputationTimeStartNs = System.nanoTime(); 1124 logger.debug(sparql_debug, "computeM"); 1125 // initialise all possible lengths (1 to mMaxLength) 1126 for(int i=1; i<=mMaxLength; i++) { 1127 m.put(i, new TreeSet<>()); 1128 } 1129 1130 SortedSet<OWLClassExpression> m1 = classHierarchy.getSubClasses(df.getOWLThing(), true); 1131 m.get(lengthMetric.classLength).addAll(m1); 1132 1133 if(useNegation) { 1134 int lc = lengthMetric.objectComplementLength + lengthMetric.classLength; 1135 Set<OWLClassExpression> m2tmp = classHierarchy.getSuperClasses(df.getOWLNothing(), true); 1136 for(OWLClassExpression c : m2tmp) { 1137 if(!c.isOWLThing()) { 1138 m.get(lc).add(df.getOWLObjectComplementOf(c)); 1139 } 1140 } 1141 } 1142 1143 if(useExistsConstructor) { 1144 int lc = lengthMetric.objectSomeValuesLength + lengthMetric.objectProperyLength + lengthMetric.classLength; 1145 int lc_i = lengthMetric.objectSomeValuesLength + lengthMetric.objectInverseLength + lengthMetric.classLength; 1146 for(OWLObjectProperty r : objectPropertyHierarchy.getMostGeneralRoles()) { 1147 m.get(lc).add(df.getOWLObjectSomeValuesFrom(r, df.getOWLThing())); 1148 1149 if(useInverse) { 1150 m.get(lc_i).add(df.getOWLObjectSomeValuesFrom(r.getInverseProperty(), df.getOWLThing())); 1151 } 1152 } 1153 } 1154 1155 if(useAllConstructor) { 1156 // we allow \forall r.\top here because otherwise the operator 1157 // becomes too difficult to manage due to dependencies between 1158 // M_A and M_A' where A'=ran(r) 1159 int lc = lengthMetric.objectAllValuesLength + lengthMetric.objectProperyLength + lengthMetric.classLength; 1160 int lc_i = lengthMetric.objectAllValuesLength + lengthMetric.objectInverseLength + lengthMetric.classLength; 1161 for(OWLObjectProperty r : objectPropertyHierarchy.getMostGeneralRoles()) { 1162 m.get(lc).add(df.getOWLObjectAllValuesFrom(r, df.getOWLThing())); 1163 1164 if(useInverse) { 1165 m.get(lc_i).add(df.getOWLObjectAllValuesFrom(r.getInverseProperty(), df.getOWLThing())); 1166 } 1167 } 1168 } 1169 1170 // boolean datatypes, e.g. testPositive = true 1171 if(useBooleanDatatypes) { 1172 Set<OWLDataProperty> booleanDPs = Sets.intersection(dataPropertyHierarchy.getEntities(), reasoner.getBooleanDatatypeProperties()); 1173 logger.debug(sparql_debug, "BOOL DPs:"+booleanDPs); 1174 int lc = lengthMetric.dataHasValueLength + lengthMetric.dataProperyLength; 1175 for(OWLDataProperty dp : booleanDPs) { 1176 m.get(lc).add(df.getOWLDataHasValue(dp, df.getOWLLiteral(true))); 1177 m.get(lc).add(df.getOWLDataHasValue(dp, df.getOWLLiteral(false))); 1178 } 1179 } 1180 1181 if(useNumericDatatypes) { 1182 Set<OWLDataProperty> numericDPs = Sets.intersection(dataPropertyHierarchy.getEntities(), reasoner.getNumericDataProperties()); 1183 logger.debug(sparql_debug, "Numeric DPs:"+numericDPs); 1184 int lc = lengthMetric.dataSomeValuesLength + lengthMetric.dataProperyLength + 1; 1185 for(OWLDataProperty dp : numericDPs) { 1186 addNumericFacetRestrictions(lc, dp); 1187 } 1188 } 1189 1190 if(useTimeDatatypes) { 1191 Set<OWLDataProperty> dataProperties = dataPropertyHierarchy.getEntities().stream() 1192 .filter(dp -> 1193 { 1194 OWLDatatype datatype = reasoner.getDatatype(dp); 1195 return (datatype != null && OWLAPIUtils.dtDatatypes.contains(datatype)); 1196 }).collect(Collectors.toSet()); 1197 int lc = lengthMetric.dataSomeValuesLength + lengthMetric.dataProperyLength + 1; 1198 for(OWLDataProperty dp : dataProperties) { 1199 addNumericFacetRestrictions(lc, dp); 1200 } 1201 } 1202 1203 if(useDataHasValueConstructor) { 1204 Set<OWLDataProperty> stringDPs = Sets.intersection(dataPropertyHierarchy.getEntities(), reasoner.getStringDatatypeProperties()); 1205 logger.debug(sparql_debug, "STRING DPs:"+stringDPs); 1206 int lc = lengthMetric.dataHasValueLength + lengthMetric.dataProperyLength; 1207 for(OWLDataProperty dp : stringDPs) { 1208 // loop over frequent values 1209 Set<OWLLiteral> freqValues = frequentDataValues.get(dp); 1210 for(OWLLiteral lit : freqValues) { 1211 m.get(lc).add(df.getOWLDataHasValue(dp, lit)); 1212 } 1213 } 1214 } 1215 1216 if(useHasValueConstructor) { 1217 int lc = lengthMetric.objectHasValueLength + lengthMetric.objectProperyLength; 1218 int lc_i = lengthMetric.objectHasValueLength + lengthMetric.objectInverseLength; 1219 1220 for(OWLObjectProperty p : objectPropertyHierarchy.getMostGeneralRoles()) { 1221 Set<OWLIndividual> values = frequentValues.get(p); 1222 values.forEach(val -> m.get(lc).add(df.getOWLObjectHasValue(p, val))); 1223 1224 if(useInverse) { 1225 values.forEach(val -> m.get(lc_i).add(df.getOWLObjectHasValue(p.getInverseProperty(), val))); 1226 } 1227 } 1228 } 1229 1230 if(useCardinalityRestrictions) { 1231 logger.debug(sparql_debug, "most general properties:"); 1232 int lc = lengthMetric.objectCardinalityLength + lengthMetric.objectProperyLength + lengthMetric.classLength; 1233 for(OWLObjectProperty r : objectPropertyHierarchy.getMostGeneralRoles()) { 1234 int maxFillers = maxNrOfFillers.get(r); 1235 logger.debug(sparql_debug, "`"+r+"="+maxFillers); 1236 // zero fillers: <= -1 r.C does not make sense 1237 // one filler: <= 0 r.C is equivalent to NOT EXISTS r.C, 1238 // but we still keep it, because ALL r.NOT C may be difficult to reach 1239 if((useNegation && maxFillers > 0) || (!useNegation && maxFillers > 1)) 1240 m.get(lc).add(df.getOWLObjectMaxCardinality(maxFillers-1, r, df.getOWLThing())); 1241 1242// m.get(lc).add(df.getOWLObjectExactCardinality(1, r, df.getOWLThing())); 1243 } 1244 } 1245 1246 if(useHasSelf) { 1247 int lc = lengthMetric.objectSomeValuesLength + lengthMetric.objectProperyLength + lengthMetric.objectHasSelfLength; 1248 for(OWLObjectProperty p : objectPropertyHierarchy.getMostGeneralRoles()) { 1249 m.get(lc).add(df.getOWLObjectHasSelf(p)); 1250 } 1251 } 1252 1253 logger.debug(sparql_debug, "m: " + m); 1254 1255 mComputationTimeNs += System.nanoTime() - mComputationTimeStartNs; 1256 } 1257 1258 private void addNumericFacetRestrictions(int lc, OWLDataProperty dp) { 1259 if(splits.get(dp) != null && splits.get(dp).size() > 0) { 1260 OWLLiteral min = splits.get(dp).get(0); 1261 OWLLiteral max = splits.get(dp).get(splits.get(dp).size()-1); 1262 1263 OWLDatatypeRestriction restriction = asDatatypeRestriction(dp, min, OWLFacet.MIN_INCLUSIVE); 1264 m.get(lc).add(df.getOWLDataSomeValuesFrom(dp, restriction)); 1265 1266 restriction = asDatatypeRestriction(dp, max, OWLFacet.MAX_INCLUSIVE); 1267 m.get(lc).add(df.getOWLDataSomeValuesFrom(dp, restriction)); 1268 } 1269 } 1270 1271 private OWLDatatypeRestriction asDatatypeRestriction(OWLDataProperty dp, OWLLiteral value, OWLFacet facet) { 1272 OWLDatatype datatype = reasoner.getDatatype(dp); 1273 1274 OWLDatatypeRestriction restriction = df.getOWLDatatypeRestriction( 1275 datatype, 1276 Collections.singleton(df.getOWLFacetRestriction( 1277 facet, 1278 value))); 1279 return restriction; 1280 } 1281 1282 // computation of the set M_A 1283 // a major difference compared to the ILP 2007 \rho operator is that 1284 // M is finite and contains elements of length (currently) at most 3 1285 private void computeM(OWLClassExpression nc) { 1286 long mComputationTimeStartNs = System.nanoTime(); 1287 1288 mA.put(nc, new TreeMap<>()); 1289 // initialise all possible lengths (1 to mMaxLength) 1290 for(int i=1; i<=mMaxLength; i++) { 1291 mA.get(nc).put(i, new TreeSet<>()); 1292 } 1293 1294 // most general classes, which are not disjoint with nc and provide real refinement 1295 SortedSet<OWLClassExpression> m1 = getClassCandidates(nc); 1296 mA.get(nc).get(lengthMetric.classLength).addAll(m1); 1297 1298 // most specific negated classes, which are not disjoint with nc 1299 if(useNegation) { 1300 SortedSet<OWLClassExpression> m2; 1301 m2 = getNegClassCandidates(nc); 1302 mA.get(nc).get(lengthMetric.classLength + lengthMetric.objectComplementLength).addAll(m2); 1303 } 1304 1305 // compute applicable properties 1306 computeMg(nc); 1307 1308 // boolean datatypes, e.g. testPositive = true 1309 if(useBooleanDatatypes) { 1310 int lc = lengthMetric.dataHasValueLength + lengthMetric.dataProperyLength; 1311 Set<OWLDataProperty> booleanDPs = mgbd.get(nc); 1312 for (OWLDataProperty dp : booleanDPs) { 1313 mA.get(nc).get(lc).add(df.getOWLDataHasValue(dp, df.getOWLLiteral(true))); 1314 mA.get(nc).get(lc).add(df.getOWLDataHasValue(dp, df.getOWLLiteral(false))); 1315 } 1316 } 1317 1318 if(useExistsConstructor) { 1319 int lc = lengthMetric.objectSomeValuesLength + lengthMetric.objectProperyLength + lengthMetric.classLength; 1320 for(OWLObjectProperty r : mgr.get(nc)) { 1321 mA.get(nc).get(lc).add(df.getOWLObjectSomeValuesFrom(r, df.getOWLThing())); 1322 } 1323 } 1324 1325 if(useAllConstructor) { 1326 // we allow \forall r.\top here because otherwise the operator 1327 // becomes too difficult to manage due to dependencies between 1328 // M_A and M_A' where A'=ran(r) 1329 int lc = lengthMetric.objectAllValuesLength + lengthMetric.objectProperyLength + lengthMetric.classLength; 1330 for(OWLObjectProperty r : mgr.get(nc)) { 1331 mA.get(nc).get(lc).add(df.getOWLObjectAllValuesFrom(r, df.getOWLThing())); 1332 } 1333 } 1334 1335 if(useNumericDatatypes) { 1336 Set<OWLDataProperty> numericDPs = mgNumeric.get(nc); 1337 int lc = lengthMetric.dataSomeValuesLength + lengthMetric.dataProperyLength + 1; 1338 1339 for(OWLDataProperty dp : numericDPs) { 1340 List<OWLLiteral> splitLiterals = splits.get(dp); 1341 if(splitLiterals != null && splitLiterals.size() > 0) { 1342 OWLLiteral min = splits.get(dp).get(0); 1343 OWLLiteral max = splits.get(dp).get(splits.get(dp).size()-1); 1344 mA.get(nc).get(lc).add(df.getOWLDataSomeValuesFrom(dp, asDatatypeRestriction(dp, min, OWLFacet.MIN_INCLUSIVE))); 1345 mA.get(nc).get(lc).add(df.getOWLDataSomeValuesFrom(dp, asDatatypeRestriction(dp, max, OWLFacet.MAX_INCLUSIVE))); 1346 } 1347 } 1348 } 1349 1350 if(useTimeDatatypes) { 1351 Set<OWLDataProperty> dtDPs = mgDT.get(nc); 1352 int lc = lengthMetric.dataSomeValuesLength + lengthMetric.dataProperyLength + 1; 1353 1354 for(OWLDataProperty dp : dtDPs) { 1355 if(splits.get(dp).size() > 0) { 1356 OWLLiteral min = splits.get(dp).get(0); 1357 OWLLiteral max = splits.get(dp).get(splits.get(dp).size()-1); 1358 mA.get(nc).get(lc).add(df.getOWLDataSomeValuesFrom(dp, asDatatypeRestriction(dp, min, OWLFacet.MIN_INCLUSIVE))); 1359 mA.get(nc).get(lc).add(df.getOWLDataSomeValuesFrom(dp, asDatatypeRestriction(dp, max, OWLFacet.MAX_INCLUSIVE))); 1360 } 1361 } 1362 } 1363 1364 if(useDataHasValueConstructor) { 1365 Set<OWLDataProperty> stringDPs = mgsd.get(nc); 1366 int lc = lengthMetric.dataHasValueLength + lengthMetric.dataProperyLength; 1367 for(OWLDataProperty dp : stringDPs) { 1368 // loop over frequent values 1369 Set<OWLLiteral> freqValues = frequentDataValues.get(dp); 1370 for(OWLLiteral lit : freqValues) { 1371 mA.get(nc).get(lc).add(df.getOWLDataHasValue(dp, lit)); 1372 } 1373 } 1374 } 1375 1376 if(useHasValueConstructor) { 1377 int lc = lengthMetric.objectHasValueLength + lengthMetric.objectProperyLength; 1378 int lc_i = lengthMetric.objectHasValueLength + lengthMetric.objectInverseLength; 1379// 1380// m.get(lc).addAll( 1381// mgr.get(nc).stream() 1382// .flatMap(p -> frequentValues.get(p).stream() 1383// .map(val -> df.getOWLObjectHasValue(p, val))) 1384// .collect(Collectors.toSet())); 1385 for(OWLObjectProperty p : mgr.get(nc)) { 1386 Set<OWLIndividual> values = frequentValues.get(p); 1387 values.forEach(val -> m.get(lc).add(df.getOWLObjectHasValue(p, val))); 1388 1389 if(useInverse) { 1390 values.forEach(val -> m.get(lc_i).add(df.getOWLObjectHasValue(p.getInverseProperty(), val))); 1391 } 1392 } 1393 } 1394 1395 if(useCardinalityRestrictions) { 1396 int lc = lengthMetric.objectCardinalityLength + lengthMetric.objectProperyLength + lengthMetric.classLength; 1397 for(OWLObjectProperty r : mgr.get(nc)) { 1398 int maxFillers = maxNrOfFillers.get(r); 1399 // zero fillers: <= -1 r.C does not make sense 1400 // one filler: <= 0 r.C is equivalent to NOT EXISTS r.C, 1401 // but we still keep it, because ALL r.NOT C may be difficult to reach 1402 if((useNegation && maxFillers > 0) || (!useNegation && maxFillers > 1)) { 1403 mA.get(nc).get(lc).add(df.getOWLObjectMaxCardinality(maxFillers-1, r, df.getOWLThing())); 1404 } 1405 1406 // = 1 r.C 1407// mA.get(nc).get(lc).add(df.getOWLObjectExactCardinality(1, r, df.getOWLThing())); 1408 } 1409 } 1410 1411 if(useHasSelf) { 1412 int lc = lengthMetric.objectSomeValuesLength + lengthMetric.objectProperyLength + lengthMetric.objectHasSelfLength; 1413 for(OWLObjectProperty p : mgr.get(nc)) { 1414 m.get(lc).add(df.getOWLObjectHasSelf(p)); 1415 } 1416 } 1417 1418 logger.debug(sparql_debug, "m for " + nc + ": " + mA.get(nc)); 1419 1420 mComputationTimeNs += System.nanoTime() - mComputationTimeStartNs; 1421 } 1422 1423 // get candidates for a refinement of \top restricted to a class B 1424 public SortedSet<OWLClassExpression> getClassCandidates(OWLClassExpression index) { 1425 return getClassCandidatesRecursive(index, df.getOWLThing()); 1426 } 1427 1428 private SortedSet<OWLClassExpression> getClassCandidatesRecursive(OWLClassExpression index, OWLClassExpression upperClass) { 1429 SortedSet<OWLClassExpression> candidates = new TreeSet<>(); 1430 1431 SortedSet<OWLClassExpression> subClasses = classHierarchy.getSubClasses(upperClass, true); 1432 1433 if(reasoner instanceof SPARQLReasoner) { 1434 Collection<? extends OWLClassExpression> meaningfulClasses = ((SPARQLReasoner)reasoner).getMeaningfulClasses(index, subClasses); 1435 candidates.addAll(meaningfulClasses); 1436 // recursive call, i.e. go class hierarchy down for non-meaningful classes 1437// for (OWLClassExpression cls : Sets.difference(superClasses, meaningfulClasses)) { 1438// candidates.addAll(getNegClassCandidatesRecursive(index, cls)); 1439// } 1440 } else { 1441 1442 // we descend the subsumption hierarchy to ensure that we get 1443 // the most general concepts satisfying the criteria 1444 for(OWLClassExpression candidate : subClasses) { 1445// System.out.println("testing " + candidate + " ... "); 1446 // NamedClass candidate = (OWLClass) d; 1447 // check disjointness with index (if not no further traversal downwards is necessary) 1448 if(!isDisjoint(candidate,index)) { 1449// System.out.println( " passed disjointness test ... "); 1450 // check whether the class is meaningful, i.e. adds something to the index 1451 // to do this, we need to make sure that the class is not a superclass of the 1452 // index (otherwise we get nothing new) - for instance based disjoints, we 1453 // make sure that there is at least one individual, which is not already in the 1454 // upper class 1455 boolean meaningful; 1456 if(instanceBasedDisjoints) { 1457 // bug: tests should be performed against the index, not the upper class 1458 // SortedSet<OWLIndividual> tmp = rs.getIndividuals(upperClass); 1459 SortedSet<OWLIndividual> tmp = reasoner.getIndividuals(index); 1460 tmp.removeAll(reasoner.getIndividuals(candidate)); 1461 // System.out.println(" instances of " + index + " and not " + candidate + ": " + tmp.size()); 1462 meaningful = tmp.size() != 0; 1463 } else { 1464 meaningful = !isDisjoint(df.getOWLObjectComplementOf(candidate),index); 1465 } 1466 1467 if(meaningful) { 1468 // candidate went successfully through all checks 1469 candidates.add(candidate); 1470 // System.out.println(" real refinement"); 1471 } else { 1472 // descend subsumption hierarchy to find candidates 1473 // System.out.println(" enter recursion"); 1474 candidates.addAll(getClassCandidatesRecursive(index, candidate)); 1475 } 1476 } 1477 // else { 1478 // System.out.println(" ruled out, because it is disjoint"); 1479 // } 1480 } 1481 } 1482// System.out.println("cc method exit"); 1483 return candidates; 1484 } 1485 1486 // get candidates for a refinement of \top restricted to a class B 1487 public SortedSet<OWLClassExpression> getNegClassCandidates(OWLClassExpression index) { 1488 return getNegClassCandidatesRecursive(index, df.getOWLNothing(), null); 1489 } 1490 1491 private SortedSet<OWLClassExpression> getNegClassCandidatesRecursive(OWLClassExpression index, OWLClassExpression lowerClass, Set<OWLClassExpression> seenClasses) { 1492 if (seenClasses == null) { seenClasses = new TreeSet<>(); } 1493 SortedSet<OWLClassExpression> candidates = new TreeSet<>(); 1494// System.out.println("index " + index + " lower class " + lowerClass); 1495 1496 SortedSet<OWLClassExpression> superClasses = classHierarchy.getSuperClasses(lowerClass); 1497 1498 if(reasoner instanceof SPARQLReasoner) { 1499 Collection<? extends OWLClassExpression> meaningfulClasses = ((SPARQLReasoner)reasoner).getMeaningfulClasses(index, superClasses); 1500 candidates.addAll(meaningfulClasses); 1501 // recursive call, i.e. go class hierarchy up for non-meaningful classes 1502// for (OWLClassExpression cls : Sets.difference(superClasses, meaningfulClasses)) { 1503// candidates.addAll(getNegClassCandidatesRecursive(index, cls)); 1504// } 1505 } else { 1506 for(OWLClassExpression candidate : superClasses) { 1507 if(!candidate.isOWLThing()) { 1508 OWLObjectComplementOf negatedCandidate = df.getOWLObjectComplementOf(candidate); 1509 1510 // check disjointness with index/range (should not be disjoint otherwise not useful) 1511 if(!isDisjoint(negatedCandidate,index)) { 1512 boolean meaningful; 1513 1514 if(instanceBasedDisjoints) { 1515 SortedSet<OWLIndividual> tmp = reasoner.getIndividuals(index); 1516 tmp.removeAll(reasoner.getIndividuals(negatedCandidate)); 1517 meaningful = tmp.size() != 0; 1518 } else { 1519 meaningful = !isDisjoint(candidate,index); 1520 } 1521 1522 if(meaningful) { 1523 candidates.add(negatedCandidate); 1524 } else if (!seenClasses.contains(candidate)) { 1525 seenClasses.add(candidate); 1526 candidates.addAll(getNegClassCandidatesRecursive(index, candidate, seenClasses)); 1527 } 1528 } 1529 } 1530 } 1531 } 1532 1533 return candidates; 1534 } 1535 1536 private void computeMg(OWLClassExpression domain) { 1537 // compute the applicable properties if this has not been done yet 1538 if(appOP.get(domain) == null) 1539 computeApp(domain); 1540 1541 // initialise mgr, mgbd, mgdd, mgsd 1542 mgr.put(domain, new TreeSet<>()); 1543 mgbd.put(domain, new TreeSet<>()); 1544 mgNumeric.put(domain, new TreeSet<>()); 1545 mgsd.put(domain, new TreeSet<>()); 1546 mgDT.put(domain, new TreeSet<>()); 1547 1548 SortedSet<OWLObjectProperty> mostGeneral = objectPropertyHierarchy.getMostGeneralRoles(); 1549 computeMgrRecursive(domain, mostGeneral, mgr.get(domain)); 1550 SortedSet<OWLDataProperty> mostGeneralDP = dataPropertyHierarchy.getMostGeneralRoles(); 1551 // we make the (reasonable) assumption here that all sub and super 1552 // datatype properties have the same type (e.g. boolean, integer, double) 1553 Set<OWLDataProperty> mostGeneralBDP = Sets.intersection(mostGeneralDP, reasoner.getBooleanDatatypeProperties()); 1554 Set<OWLDataProperty> mostGeneralNumericDPs = Sets.intersection(mostGeneralDP, reasoner.getNumericDataProperties()); 1555 Set<OWLDataProperty> mostGeneralStringDPs = Sets.intersection(mostGeneralDP, reasoner.getStringDatatypeProperties()); 1556 computeMgbdRecursive(domain, mostGeneralBDP, mgbd.get(domain)); 1557 computeMostGeneralNumericDPRecursive(domain, mostGeneralNumericDPs, mgNumeric.get(domain)); 1558 computeMostGeneralStringDPRecursive(domain, mostGeneralStringDPs, mgsd.get(domain)); 1559 } 1560 1561 private void computeMgrRecursive(OWLClassExpression domain, Set<OWLObjectProperty> currProperties, Set<OWLObjectProperty> mgrTmp) { 1562 for(OWLObjectProperty prop : currProperties) { 1563 if(appOP.get(domain).contains(prop)) 1564 mgrTmp.add(prop); 1565 else 1566 computeMgrRecursive(domain, objectPropertyHierarchy.getMoreSpecialRoles(prop), mgrTmp); 1567 } 1568 } 1569 1570 private void computeMgbdRecursive(OWLClassExpression domain, Set<OWLDataProperty> currProperties, Set<OWLDataProperty> mgbdTmp) { 1571 for(OWLDataProperty prop : currProperties) { 1572 if(appBD.get(domain).contains(prop)) 1573 mgbdTmp.add(prop); 1574 else 1575 computeMgbdRecursive(domain, dataPropertyHierarchy.getMoreSpecialRoles(prop), mgbdTmp); 1576 } 1577 } 1578 1579 private void computeMostGeneralNumericDPRecursive(OWLClassExpression domain, Set<OWLDataProperty> currProperties, Set<OWLDataProperty> mgddTmp) { 1580 for(OWLDataProperty prop : currProperties) { 1581 if(appNumeric.get(domain).contains(prop)) 1582 mgddTmp.add(prop); 1583 else 1584 computeMostGeneralNumericDPRecursive(domain, dataPropertyHierarchy.getMoreSpecialRoles(prop), mgddTmp); 1585 } 1586 } 1587 private void computeMostGeneralStringDPRecursive(OWLClassExpression domain, Set<OWLDataProperty> currProperties, Set<OWLDataProperty> mgddTmp) { 1588 for(OWLDataProperty prop : currProperties) { 1589 if(appSD.get(domain).contains(prop)) 1590 mgddTmp.add(prop); 1591 else 1592 computeMostGeneralStringDPRecursive(domain, dataPropertyHierarchy.getMoreSpecialRoles(prop), mgddTmp); 1593 } 1594 } 1595 1596 // computes the set of applicable properties for a given class 1597 private void computeApp(OWLClassExpression domain) { 1598 Set<OWLObjectProperty> applicableRoles = new TreeSet<>(); 1599 Set<OWLObjectProperty> mostGeneral = objectPropertyHierarchy.getMostGeneralRoles(); 1600 if (reasoner instanceof SPARQLReasoner) { 1601 Set<OWLObjectProperty> roleAppRoles = ((SPARQLReasoner) reasoner).getApplicableProperties(domain, mostGeneral); 1602 applicableRoles.addAll(roleAppRoles); 1603 } else { 1604 SortedSet<OWLIndividual> individuals1 = reasoner.getIndividuals(domain); 1605 // object properties 1606 for (OWLObjectProperty role : mostGeneral) { 1607 // TODO: currently we just rely on named classes as roles, 1608 // instead of computing dom(r) and ran(r) 1609 OWLClassExpression d = opDomains.get(role); 1610 1611 Set<OWLIndividual> individuals2 = new HashSet<>(); 1612 for (Entry<OWLIndividual, SortedSet<OWLIndividual>> entry : reasoner.getPropertyMembers(role).entrySet()) { 1613 OWLIndividual ind = entry.getKey(); 1614 if (!entry.getValue().isEmpty()) { 1615 individuals2.add(ind); 1616 } 1617 } 1618 1619 boolean disjoint = Sets.intersection(individuals1, individuals2).isEmpty(); 1620// if(!isDisjoint(domain,d)) 1621 if (!disjoint) { 1622 applicableRoles.add(role); 1623 } 1624 1625 } 1626 } 1627 1628 appOP.put(domain, applicableRoles); 1629 1630 // boolean datatype properties 1631 Set<OWLDataProperty> allBDPs = Sets.intersection(dataPropertyHierarchy.getEntities(), reasoner.getBooleanDatatypeProperties()); 1632 Set<OWLDataProperty> applicableBDPs = new TreeSet<>(); 1633 for(OWLDataProperty role : allBDPs) { 1634// Description d = (OWLClass) rs.getDomain(role); 1635 OWLClassExpression d = dpDomains.get(role); 1636 if(!isDisjoint(domain,d)) 1637 applicableBDPs.add(role); 1638 } 1639 appBD.put(domain, applicableBDPs); 1640 1641 // numeric data properties 1642 Set<OWLDataProperty> allNumericDPs = Sets.intersection(dataPropertyHierarchy.getEntities(), reasoner.getNumericDataProperties()); 1643 Set<OWLDataProperty> applicableNumericDPs = new TreeSet<>(); 1644 for(OWLDataProperty role : allNumericDPs) { 1645 // get domain of property 1646 OWLClassExpression d = dpDomains.get(role); 1647 // check if it's not disjoint with current class expression 1648 if(!isDisjoint(domain,d)) 1649 applicableNumericDPs.add(role); 1650 } 1651 appNumeric.put(domain, applicableNumericDPs); 1652 1653 // string datatype properties 1654 Set<OWLDataProperty> allSDPs = Sets.intersection(dataPropertyHierarchy.getEntities(), reasoner.getStringDatatypeProperties()); 1655 Set<OWLDataProperty> applicableSDPs = new TreeSet<>(); 1656 for(OWLDataProperty role : allSDPs) { 1657// Description d = (OWLClass) rs.getDomain(role); 1658 OWLClassExpression d = dpDomains.get(role); 1659// System.out.println("domain: " + d); 1660 if(!isDisjoint(domain,d)) 1661 applicableSDPs.add(role); 1662 } 1663 appSD.put(domain, applicableSDPs); 1664 1665 } 1666 1667 // returns true if the intersection contains elements disjoint 1668 // to the given OWLClassExpression (if true adding the OWLClassExpression to 1669 // the intersection results in a OWLClassExpression equivalent to bottom) 1670 // e.g. OldPerson AND YoungPerson; Nitrogen-34 AND Tin-113 1671 // Note: currently we only check named classes in the intersection, 1672 // it would be interesting to see whether it makes sense to extend this 1673 // (advantage: less refinements, drawback: operator will need infinitely many 1674 // reasoner queries in the long run) 1675 @SuppressWarnings({"unused"}) 1676 private boolean containsDisjoints(OWLObjectIntersectionOf intersection, OWLClassExpression d) { 1677 List<OWLClassExpression> children = intersection.getOperandsAsList(); 1678 for(OWLClassExpression child : children) { 1679 if(d.isOWLNothing()) 1680 return true; 1681 else if(!child.isAnonymous()) { 1682 if(isDisjoint(child, d)) 1683 return true; 1684 } 1685 } 1686 return false; 1687 } 1688 1689 private boolean isDisjoint(OWLClassExpression d1, OWLClassExpression d2) { 1690 if(d1.isOWLThing() || d2.isOWLThing()) { 1691 return false; 1692 } 1693 1694 if(d1.isOWLNothing() || d2.isOWLNothing()) { 1695 return true; 1696 } 1697 1698 // check whether we have cached this query 1699 Map<OWLClassExpression,Boolean> tmp = cachedDisjoints.get(d1); 1700 if(tmp != null && tmp.containsKey(d2)) { 1701 return tmp.get(d2); 1702 } 1703 1704 // compute the disjointness 1705 Boolean result; 1706 if (instanceBasedDisjoints) { 1707 result = isDisjointInstanceBased(d1, d2); 1708 } else { 1709 OWLClassExpression d = df.getOWLObjectIntersectionOf(d1, d2); 1710 result = reasoner.isSuperClassOf(df.getOWLNothing(), d); 1711 } 1712 // add the result to the cache (we add it twice such that 1713 // the order of access does not matter) 1714 1715 // create new entries if necessary 1716 if (tmp == null) 1717 cachedDisjoints.put(d1, new TreeMap<>()); 1718 if (!cachedDisjoints.containsKey(d2)) 1719 cachedDisjoints.put(d2, new TreeMap<>()); 1720 1721 // add result symmetrically in the OWLClassExpression matrix 1722 cachedDisjoints.get(d1).put(d2, result); 1723 cachedDisjoints.get(d2).put(d1, result); 1724 // System.out.println("---"); 1725 return result; 1726 } 1727 1728 private boolean isDisjointInstanceBased(OWLClassExpression d1, OWLClassExpression d2) { 1729 if(reasoner instanceof SPARQLReasoner) { 1730 SortedSet<OWLIndividual> individuals = reasoner.getIndividuals(df.getOWLObjectIntersectionOf(d1, d2)); 1731 return individuals.isEmpty(); 1732 } else { 1733 SortedSet<OWLIndividual> d1Instances = reasoner.getIndividuals(d1); 1734 SortedSet<OWLIndividual> d2Instances = reasoner.getIndividuals(d2); 1735 1736 for(OWLIndividual d1Instance : d1Instances) { 1737 if(d2Instances.contains(d1Instance)) 1738 return false; 1739 } 1740 return true; 1741 } 1742 } 1743 1744 /* 1745 // computes whether two classes are disjoint; this should be computed 1746 // by the reasoner only ones and otherwise taken from a matrix 1747 private boolean isDisjoint(OWLClass a, OWLClassExpression d) { 1748 // we need to test whether A AND B is equivalent to BOTTOM 1749 Description d2 = new Intersection(a, d); 1750 return rs.subsumes(new Nothing(), d2); 1751 }*/ 1752 1753 // we need to test whether NOT A AND B is equivalent to BOTTOM 1754 @SuppressWarnings("unused") 1755 private boolean isNotADisjoint(OWLClass a, OWLClass b) { 1756// Map<OWLClass,Boolean> tmp = notABDisjoint.get(a); 1757// Boolean tmp2 = null; 1758// if(tmp != null) 1759// tmp2 = tmp.get(b); 1760// 1761// if(tmp2==null) { 1762 OWLClassExpression notA = df.getOWLObjectComplementOf(a); 1763 OWLClassExpression d = df.getOWLObjectIntersectionOf(notA, b); 1764 Boolean result = reasoner.isSuperClassOf(df.getOWLNothing(), d); 1765 // ... add to cache ... 1766 return result; 1767// } else 1768// return tmp2; 1769 } 1770 1771 // we need to test whether NOT A AND B = B 1772 // (if not then NOT A is not meaningful in the sense that it does 1773 // not semantically add anything to B) 1774 @SuppressWarnings("unused") 1775 private boolean isNotAMeaningful(OWLClass a, OWLClass b) { 1776 OWLClassExpression notA = df.getOWLObjectComplementOf(a); 1777 OWLClassExpression d = df.getOWLObjectIntersectionOf(notA, b); 1778 // check b subClassOf b AND NOT A (if yes then it is not meaningful) 1779 return !reasoner.isSuperClassOf(d, b); 1780 } 1781 1782 public int getFrequencyThreshold() { 1783 return frequencyThreshold; 1784 } 1785 1786 public void setFrequencyThreshold(int frequencyThreshold) { 1787 this.frequencyThreshold = frequencyThreshold; 1788 } 1789 1790 public boolean isUseDataHasValueConstructor() { 1791 return useDataHasValueConstructor; 1792 } 1793 1794 public void setUseDataHasValueConstructor(boolean useDataHasValueConstructor) { 1795 isFinal(); 1796 this.useDataHasValueConstructor = useDataHasValueConstructor; 1797 } 1798 1799 public boolean isApplyAllFilter() { 1800 return applyAllFilter; 1801 } 1802 1803 public void setApplyAllFilter(boolean applyAllFilter) { 1804 this.applyAllFilter = applyAllFilter; 1805 } 1806 1807 public boolean isApplyExistsFilter() { 1808 return applyExistsFilter; 1809 } 1810 1811 public void setApplyExistsFilter(boolean applyExistsFilter) { 1812 this.applyExistsFilter = applyExistsFilter; 1813 } 1814 1815 public boolean isUseAllConstructor() { 1816 return useAllConstructor; 1817 } 1818 1819 public void setUseAllConstructor(boolean useAllConstructor) { 1820 this.useAllConstructor = useAllConstructor; 1821 } 1822 1823 public boolean isUseExistsConstructor() { 1824 return useExistsConstructor; 1825 } 1826 1827 public void setUseExistsConstructor(boolean useExistsConstructor) { 1828 this.useExistsConstructor = useExistsConstructor; 1829 } 1830 1831 public boolean isUseHasValueConstructor() { 1832 return useHasValueConstructor; 1833 } 1834 1835 public void setUseHasValueConstructor(boolean useHasValueConstructor) { 1836 isFinal(); 1837 this.useHasValueConstructor = useHasValueConstructor; 1838 } 1839 1840 public boolean isUseCardinalityRestrictions() { 1841 return useCardinalityRestrictions; 1842 } 1843 1844 public void setUseCardinalityRestrictions(boolean useCardinalityRestrictions) { 1845 isFinal(); 1846 this.useCardinalityRestrictions = useCardinalityRestrictions; 1847 } 1848 1849 public boolean isUseHasSelf() { 1850 return useHasSelf; 1851 } 1852 1853 public void setUseHasSelf(boolean useHasSelf) { 1854 this.useHasSelf = useHasSelf; 1855 } 1856 1857 public boolean isUseNegation() { 1858 return useNegation; 1859 } 1860 1861 public void setUseNegation(boolean useNegation) { 1862 this.useNegation = useNegation; 1863 } 1864 1865 public boolean isUseBooleanDatatypes() { 1866 return useBooleanDatatypes; 1867 } 1868 1869 public void setUseBooleanDatatypes(boolean useBooleanDatatypes) { 1870 this.useBooleanDatatypes = useBooleanDatatypes; 1871 } 1872 1873 public boolean isUseStringDatatypes() { 1874 return useStringDatatypes; 1875 } 1876 1877 public void setUseStringDatatypes(boolean useStringDatatypes) { 1878 this.useStringDatatypes = useStringDatatypes; 1879 } 1880 1881 public boolean isInstanceBasedDisjoints() { 1882 return instanceBasedDisjoints; 1883 } 1884 1885 public void setInstanceBasedDisjoints(boolean instanceBasedDisjoints) { 1886 this.instanceBasedDisjoints = instanceBasedDisjoints; 1887 } 1888 1889 public AbstractReasonerComponent getReasoner() { 1890 return reasoner; 1891 } 1892 1893 @Autowired 1894 public void setReasoner(AbstractReasonerComponent reasoner) { 1895 this.reasoner = reasoner; 1896 } 1897 1898 public ClassHierarchy getSubHierarchy() { 1899 return classHierarchy; 1900 } 1901 1902 public void setSubHierarchy(ClassHierarchy subHierarchy) { 1903 this.classHierarchy = subHierarchy; 1904 } 1905 1906 public OWLClassExpression getStartClass() { 1907 return startClass; 1908 } 1909 1910 @Override 1911 public void setStartClass(OWLClassExpression startClass) { 1912 this.startClass = startClass; 1913 } 1914 1915 public int getCardinalityLimit() { 1916 return cardinalityLimit; 1917 } 1918 1919 public void setCardinalityLimit(int cardinalityLimit) { 1920 this.cardinalityLimit = cardinalityLimit; 1921 } 1922 1923 public ObjectPropertyHierarchy getObjectPropertyHierarchy() { 1924 return objectPropertyHierarchy; 1925 } 1926 1927 @Override 1928 public void setObjectPropertyHierarchy(ObjectPropertyHierarchy objectPropertyHierarchy) { 1929 this.objectPropertyHierarchy = objectPropertyHierarchy; 1930 } 1931 1932 public DatatypePropertyHierarchy getDataPropertyHierarchy() { 1933 return dataPropertyHierarchy; 1934 } 1935 1936 @Override 1937 public void setDataPropertyHierarchy(DatatypePropertyHierarchy dataPropertyHierarchy) { 1938 this.dataPropertyHierarchy = dataPropertyHierarchy; 1939 } 1940 1941 @Override 1942 public void setReasoner(Reasoner reasoner) { 1943 this.reasoner = (AbstractReasonerComponent) reasoner; 1944 } 1945 1946 @Override @NoConfigOption 1947 public void setClassHierarchy(ClassHierarchy classHierarchy) { 1948 this.classHierarchy = classHierarchy; 1949 } 1950 1951 /** 1952 * @param useObjectValueNegation the useObjectValueNegation to set 1953 */ 1954 public void setUseObjectValueNegation(boolean useObjectValueNegation) { 1955 this.useObjectValueNegation = useObjectValueNegation; 1956 } 1957 1958 public boolean isUseNumericDatatypes() { 1959 return useNumericDatatypes; 1960 } 1961 1962 public void setUseNumericDatatypes(boolean useNumericDatatypes) { 1963 isFinal(); 1964 this.useNumericDatatypes = useNumericDatatypes; 1965 } 1966 1967 /** 1968 * @param useInverse whether to use inverse properties in property restrictions 1969 */ 1970 public void setUseInverse(boolean useInverse) { 1971 isFinal(); 1972 this.useInverse = useInverse; 1973 } 1974 1975 /** 1976 * @param useSomeOnly whether to allow universal restrictions on a property r only if there exists already 1977 * a existential restriction on the same property in an intersection 1978 */ 1979 public void setUseSomeOnly(boolean useSomeOnly) { 1980 this.useSomeOnly = useSomeOnly; 1981 } 1982 1983 /** 1984 * @param useTimeDatatypes whether to use data/time literal restrictions 1985 */ 1986 public void setUseTimeDatatypes(boolean useTimeDatatypes) { 1987 isFinal(); 1988 this.useTimeDatatypes = useTimeDatatypes; 1989 } 1990 1991 public int getMaxNrOfSplits() { 1992 return maxNrOfSplits; 1993 } 1994 1995 public void setMaxNrOfSplits(int maxNrOfSplits) { 1996 this.maxNrOfSplits = maxNrOfSplits; 1997 } 1998 1999 public boolean isDisjointChecks() { 2000 return disjointChecks; 2001 } 2002 2003 public void setDisjointChecks(boolean disjointChecks) { 2004 this.disjointChecks = disjointChecks; 2005 } 2006 2007 @Override 2008 public OWLClassExpressionLengthMetric getLengthMetric() { 2009 return lengthMetric; 2010 } 2011 2012 @Override 2013 public void setLengthMetric(OWLClassExpressionLengthMetric lengthMetric) { 2014 this.lengthMetric = lengthMetric; 2015 2016 mMaxLength = max ( 2017 lengthMetric.classLength, 2018 lengthMetric.objectComplementLength + lengthMetric.classLength, 2019 lengthMetric.objectSomeValuesLength + lengthMetric.objectProperyLength + lengthMetric.classLength, 2020 lengthMetric.objectSomeValuesLength + lengthMetric.objectProperyLength + lengthMetric.classLength + lengthMetric.objectInverseLength, 2021 lengthMetric.objectAllValuesLength + lengthMetric.objectProperyLength + lengthMetric.classLength, 2022 lengthMetric.objectAllValuesLength + lengthMetric.objectProperyLength + lengthMetric.classLength + lengthMetric.objectInverseLength, 2023 lengthMetric.dataHasValueLength + lengthMetric.dataProperyLength, 2024 lengthMetric.dataSomeValuesLength + lengthMetric.dataProperyLength + 1, 2025 lengthMetric.objectCardinalityLength + lengthMetric.objectProperyLength + lengthMetric.classLength); 2026 2027 logger.debug("mMaxLength = " + mMaxLength); 2028 } 2029 2030 /** 2031 * Set the splitter used to precompute possible splits for data properties with numeric ranges. Those splits 2032 * will then be used to create facet restrictions during refinement. 2033 * 2034 * @param numericValuesSplitter 2035 */ 2036 public void setNumericValuesSplitter(ValuesSplitter numericValuesSplitter) { 2037 this.numericValuesSplitter = numericValuesSplitter; 2038 } 2039}