001 /* 002 * To change this license header, choose License Headers in Project Properties. 003 * To change this template file, choose Tools | Templates 004 * and open the template in the editor. 005 */ 006package org.dllearner.algorithms.probabilistic.structure.unife.leap; 007 008import com.clarkparsia.pellet.owlapiv3.PelletReasoner; 009import com.clarkparsia.pellet.owlapiv3.PelletReasonerFactory; 010import com.google.common.collect.Sets; 011import edu.stanford.nlp.util.Factory; 012import java.util.Collections; 013import java.util.HashSet; 014import java.util.LinkedHashMap; 015import java.util.LinkedHashSet; 016import java.util.LinkedList; 017import java.util.List; 018import java.util.Map; 019import java.util.Map.Entry; 020import java.util.NavigableSet; 021import java.util.Set; 022import java.util.TreeMap; 023import java.util.TreeSet; 024import org.apache.commons.lang.StringUtils; 025import org.dllearner.core.AbstractClassExpressionLearningProblem; 026import org.dllearner.core.ComponentInitException; 027import org.dllearner.core.EvaluatedDescription; 028import org.dllearner.core.LearningProblemUnsupportedException; 029import org.dllearner.core.config.ConfigOption; 030import org.dllearner.learningproblems.ClassLearningProblem; 031import org.dllearner.learningproblems.PosNegLP; 032import org.dllearner.learningproblems.PosOnlyLP; 033import org.dllearner.core.probabilistic.unife.AbstractPSLA; 034import org.dllearner.core.probabilistic.unife.StructureLearningException; 035import org.dllearner.algorithms.probabilistic.parameter.unife.edge.AbstractEDGE; 036import org.dllearner.core.AbstractCELA; 037import org.dllearner.core.AbstractReasonerComponent; 038import org.dllearner.core.probabilistic.unife.AbstractParameterLearningAlgorithm; 039import org.dllearner.exceptions.UnsupportedLearnedAxiom; 040import org.dllearner.reasoning.ClosedWorldReasoner; 041import org.dllearner.utils.unife.OWLClassExpressionSimplifierVisitorImpl; 042import org.dllearner.utils.unife.ReflectionHelper; 043import org.dllearner.utils.unife.OWLUtils; 044import org.semanticweb.owlapi.apibinding.OWLManager; 045import org.semanticweb.owlapi.model.AxiomType; 046import org.semanticweb.owlapi.model.ClassExpressionType; 047import org.semanticweb.owlapi.model.IRI; 048import org.semanticweb.owlapi.model.OWLAnnotation; 049import org.semanticweb.owlapi.model.OWLAxiom; 050import org.semanticweb.owlapi.model.OWLClass; 051import org.semanticweb.owlapi.model.OWLClassExpression; 052import org.semanticweb.owlapi.model.OWLDataFactory; 053import org.semanticweb.owlapi.model.OWLEquivalentClassesAxiom; 054import org.semanticweb.owlapi.model.OWLIndividual; 055import org.semanticweb.owlapi.model.OWLObjectUnionOf; 056import org.semanticweb.owlapi.model.OWLOntology; 057import org.semanticweb.owlapi.model.OWLOntologyManager; 058import org.semanticweb.owlapi.model.OWLSubClassOfAxiom; 059import org.semanticweb.owlapi.model.parameters.Imports; 060import org.slf4j.Logger; 061import org.slf4j.LoggerFactory; 062import org.springframework.beans.factory.annotation.Autowired; 063import unife.bundle.exception.InconsistencyException; 064import unife.bundle.utilities.BundleUtilities; 065import unife.constants.UniFeIRI; 066 067/** 068 * There could be different version of LEAP (sequential, multi-thread, 069 * distributed), so we need an abstract class. 070 * 071 * @author Giuseppe Cota <giuseppe.cota@unife.it>, Riccardo Zese 072 * <riccardo.zese@unife.it> 073 */ 074public abstract class AbstractLEAP extends AbstractPSLA { 075 076 private static final Logger logger = LoggerFactory.getLogger(AbstractLEAP.class); 077 078 @ConfigOption(defaultValue = "owl:learnedClass", description = "You can " 079 + "specify a start class for the algorithm. To do this, you have to " 080 + "use Manchester OWL syntax without using prefixes.") 081 protected OWLClass dummyClass; 082 083 @ConfigOption(description = "accuracy used during the computation of the " 084 + "probabilistic values (number of digital places)", defaultValue = "5") 085 protected int accuracy = 5; 086 087 @ConfigOption(description = "This is used to set the type of class axiom to learn. Accepted values (case insensitive): 'subClassOf', 'equivalentClasses', 'both'", 088 required = false, 089 defaultValue = "subClassOf") 090 protected String classAxiomType = "subClassOf"; 091 092 @ConfigOption(defaultValue = "10", description = "maximum execution of the algorithm in seconds") 093 protected int maxExecutionTimeInSeconds = 10; // TO DO: stop when execution time is over 094 095 @ConfigOption(defaultValue = "1", 096 required = false, 097 description = "the number of probabilistic axioms that LEAP tries to " 098 + "add into the ontology at each iteration of the greedy search") 099 protected int blockSizeGreedySearch = 1; 100 101 protected TreeMap<String, Long> timers; 102 103 protected AbstractEDGE edge; 104 105 public AbstractLEAP() { 106 107 } 108 109 public AbstractLEAP(AbstractCELA cela, AbstractParameterLearningAlgorithm pla) { 110 super(cela, pla); 111 } 112 113 @Override 114 public void init() throws ComponentInitException { 115 116 timers = new TreeMap<>(); 117 118 //OWLOntologyManager manager = OWLManager.createOWLOntologyManager(); 119 OWLOntologyManager manager = edge.getSourcesOntology().getOWLOntologyManager(); 120 // create dummy class 121 if (dummyClass == null) { 122 OWLDataFactory owlFactory = manager.getOWLDataFactory(); 123 dummyClass = owlFactory.getOWLClass(IRI.create(UniFeIRI.DISPONTE + "/learnedClass")); 124 OWLSubClassOfAxiom axiom = owlFactory. 125 getOWLSubClassOfAxiom(dummyClass, owlFactory.getOWLThing()); 126 manager.addAxiom(edge.getSourcesOntology(), axiom); 127 } 128 129 logger.debug("getting the individuals"); 130 Set<OWLIndividual> positiveIndividuals; 131 Set<OWLIndividual> negativeIndividuals; 132 if (learningProblem == null) { 133 learningProblem = cela.getLearningProblem(); 134 } 135 if (learningProblem instanceof PosNegLP) { 136 positiveIndividuals = ((PosNegLP) learningProblem).getPositiveExamples(); 137 negativeIndividuals = ((PosNegLP) learningProblem).getNegativeExamples(); 138 } else if (learningProblem instanceof PosOnlyLP) { 139 positiveIndividuals = ((PosOnlyLP) learningProblem).getPositiveExamples(); 140 // use pseudo-negative individuals 141 negativeIndividuals = Sets.difference(learningProblem.getReasoner().getIndividuals(), positiveIndividuals); 142 } else if (learningProblem instanceof ClassLearningProblem) { 143 // Java Reflection has been used to get values from private fields. 144 //It's neither a conventional way nor the universally suggested idea, 145 // but in this case is the only way to extract positive and negative individuals 146 // without modifing the DLLearner code (the creation of a plugin is the objective) 147 try { 148 List<OWLIndividual> positiveIndividualsList = ReflectionHelper.getPrivateField(learningProblem, "classInstances"); 149 positiveIndividuals = new TreeSet<>(positiveIndividualsList); 150 negativeIndividuals = new TreeSet<>((List<OWLIndividual>) ReflectionHelper.getPrivateField(learningProblem, "superClassInstances")); 151 } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) { 152 String msg = "Cannot extract the individuals from" 153 + " learning problem: " + e.getMessage(); 154 logger.error(msg); 155 throw new ComponentInitException(msg); 156 } 157 158 } else { 159 try { 160 throw new LearningProblemUnsupportedException(((AbstractClassExpressionLearningProblem) learningProblem).getClass(), this.getClass()); 161 } catch (LearningProblemUnsupportedException e) { 162 throw new ComponentInitException(e.getMessage()); 163 } 164 } 165 // convert the individuals into assertional axioms 166 logger.debug("convert the individuals into assertional axioms"); 167// OWLDataFactory owlFactory = manager.getOWLDataFactory(); 168 Set<OWLAxiom> positiveExamples = OWLUtils.convertIndividualsToAssertionalAxioms(positiveIndividuals, dummyClass); 169// Set<OWLAxiom> positiveExamples = new HashSet<>(); 170// for (OWLIndividual ind : positiveIndividuals) { 171// OWLAxiom axiom = owlFactory.getOWLClassAssertionAxiom(dummyClass, ind); 172// positiveExamples.add(axiom); 173// } 174 175 Set<OWLAxiom> negativeExamples = OWLUtils.convertIndividualsToAssertionalAxioms(negativeIndividuals, dummyClass); 176// Set<OWLAxiom> negativeExamples = new HashSet<>(); 177// for (OWLIndividual ind : negativeIndividuals) { 178// OWLAxiom axiom = owlFactory.getOWLClassAssertionAxiom(dummyClass, ind); 179// negativeExamples.add(axiom); 180// } 181 182 edge.setPositiveExampleAxioms(positiveExamples); 183 edge.setNegativeExampleAxioms(negativeExamples); 184 185 } 186 187 protected void printTimings(long totalTimeMills, long celaTimeMills, TreeMap<String, Long> timeMap) { 188 logger.info("Main: " + totalTimeMills + " ms"); 189 logger.info("CELOE: " + celaTimeMills + " ms"); 190 long timeOther = totalTimeMills - celaTimeMills; 191 for (Entry<String, Long> time : timeMap.entrySet()) { 192 String names[] = time.getKey().split("\\."); 193 if (names.length == 1) { 194 timeOther -= time.getValue(); 195// logger.info(timeMap.subMap(names[0], names[0] + Character.MAX_VALUE).toString()); 196 } 197 198 String output = StringUtils.repeat("\t", names.length - 1); 199 output += names[names.length - 1] + ": " + time.getValue() + " ms"; 200 logger.info(output); 201 202 } 203 204// logger.info("EDGE: " + (timeMap.get("EM") + timeMap.get("Bundle")) + " ms"); 205// logger.info("\tBundle: " + timeMap.get("Bundle") + " ms"); 206// logger.info("\tEM: " + timeMap.get("EM") + " ms"); 207// long timeOther = totalTimeMills - celaTimeMills - (timeMap.get("EM") + timeMap.get("Bundle")); 208 logger.info("Other: " + timeOther + " ms"); 209 logger.info("Program client: execution successfully terminated"); 210 } 211 212 /** 213 * @return the dummyClass 214 */ 215 public OWLClass getDummyClass() { 216 return dummyClass; 217 } 218 219 /** 220 * @param dummyClass the dummyClass to set 221 */ 222 public void setDummyClass(OWLClass dummyClass) { 223 this.dummyClass = dummyClass; 224 } 225 226 /** 227 * @return the edge 228 */ 229 public AbstractEDGE getEdge() { 230 return edge; 231 } 232 233 /** 234 * @param edge the edge to set 235 */ 236 @Autowired 237 public void setEdge(AbstractEDGE edge) { 238 this.edge = edge; 239 } 240 241 /** 242 * @return the accuracy 243 */ 244 public int getAccuracy() { 245 return accuracy; 246 } 247 248 /** 249 * @param accuracy the accuracy to set 250 */ 251 public void setAccuracy(int accuracy) { 252 this.accuracy = accuracy; 253 } 254 255 private <T extends OWLAxiom> List<T> convertIntoAxioms(Class<T> type, OWLOntologyManager manager, 256 NavigableSet<? extends EvaluatedDescription> evaluatedDescriptions) { 257 List<T> axioms = new LinkedList<>(); 258 OWLDataFactory factory = manager.getOWLDataFactory(); 259 for (EvaluatedDescription description : evaluatedDescriptions.descendingSet()) { 260 OWLClassExpression ce = (OWLClassExpression) description.getDescription(); 261 ce = OWLClassExpressionSimplifierVisitorImpl.getOWLClassExpression(ce, manager); 262 OWLAnnotation annotation = factory. 263 getOWLAnnotation(BundleUtilities.PROBABILISTIC_ANNOTATION_PROPERTY, factory.getOWLLiteral(description.getAccuracy())); 264// if (classAxiomType.equalsIgnoreCase("subClassOf") || classAxiomType.equalsIgnoreCase("both")) { 265 T axiom; 266 if (type == OWLEquivalentClassesAxiom.class) { 267 axiom = (T) factory.getOWLEquivalentClassesAxiom(ce, dummyClass, Collections.singleton(annotation)); 268 269 } else if (type == OWLSubClassOfAxiom.class) { 270 axiom = (T) factory.getOWLSubClassOfAxiom(ce, dummyClass, Collections.singleton(annotation)); 271 } else { 272 throw new RuntimeException("convertIntoAxioms only works with " 273 + "equivalent and subclassOf axioms"); 274 } 275 axioms.add(axiom); 276 } 277 return axioms; 278 } 279 280 protected List<OWLSubClassOfAxiom> convertIntoSubClassOfAxioms(OWLOntologyManager manager, NavigableSet<? extends EvaluatedDescription> evaluatedDescriptions) { 281 List<OWLSubClassOfAxiom> axioms = convertIntoAxioms(OWLSubClassOfAxiom.class, manager, evaluatedDescriptions); 282 return axioms; 283 } 284 285 protected List<OWLEquivalentClassesAxiom> convertIntoEquivalentClassesAxioms(OWLOntologyManager manager, NavigableSet<? extends EvaluatedDescription> evaluatedDescriptions) { 286 List<OWLEquivalentClassesAxiom> axioms = convertIntoAxioms(OWLEquivalentClassesAxiom.class, manager, evaluatedDescriptions); 287 return axioms; 288 289 } 290 291 /* 292 protected List<OWLSubClassOfAxiom> convertIntoSubClassOfAxioms(OWLOntologyManager manager, NavigableSet<? extends EvaluatedDescription> evaluatedDescriptions) { 293 List<OWLSubClassOfAxiom> axioms = new LinkedList<>(); 294 OWLDataFactory factory = manager.getOWLDataFactory(); 295 for (EvaluatedDescription description : evaluatedDescriptions.descendingSet()) { 296 OWLClassExpression ce = (OWLClassExpression) description.getDescription(); 297 ce = OWLClassExpressionSimplifierVisitorImpl.getOWLClassExpression(ce, manager); 298 OWLAnnotation annotation = factory. 299 getOWLAnnotation(BundleUtilities.PROBABILISTIC_ANNOTATION_PROPERTY, factory.getOWLLiteral(description.getAccuracy())); 300 OWLSubClassOfAxiom axiom = factory. 301 getOWLSubClassOfAxiom(ce, dummyClass, Collections.singleton(annotation)); 302 axioms.add(axiom); 303 } 304 return axioms; 305 } 306 307 protected LinkedHashSet<OWLEquivalentClassesAxiom> convertIntoEquivalentClassesAxioms(OWLOntologyManager manager, NavigableSet<? extends EvaluatedDescription> evaluatedDescriptions) { 308 LinkedHashSet<OWLEquivalentClassesAxiom> axioms = new LinkedHashSet<>(evaluatedDescriptions.size()); 309 OWLDataFactory factory = manager.getOWLDataFactory(); 310 for (EvaluatedDescription description : evaluatedDescriptions.descendingSet()) { 311 OWLClassExpression ce = (OWLClassExpression) description.getDescription(); 312 ce = OWLClassExpressionSimplifierVisitorImpl.getOWLClassExpression(ce, manager); 313 OWLAnnotation annotation = factory. 314 getOWLAnnotation(BundleUtilities.PROBABILISTIC_ANNOTATION_PROPERTY, factory.getOWLLiteral(description.getAccuracy())); 315 316 OWLEquivalentClassesAxiom axiom = factory.getOWLEquivalentClassesAxiom(ce, dummyClass, Collections.singleton(annotation)); 317 axioms.add(axiom); 318 319 } 320 return axioms; 321 } 322 */ 323 /** 324 * 325 * @param finalOntology 326 * @param learnedAxioms 327 * @return 328 */ 329 protected OWLOntology replaceDummyClass(OWLOntology finalOntology, Set<OWLAxiom> learnedAxioms) throws UnsupportedLearnedAxiom { 330 logger.debug("Replacing super class \"dummyClass\" with \"classToDescribe\""); 331 ClassLearningProblem clp = (ClassLearningProblem) cela.getLearningProblem(); 332 OWLOntologyManager man = finalOntology.getOWLOntologyManager(); 333 OWLDataFactory df = man.getOWLDataFactory(); 334 int numInitialAxioms = finalOntology.getLogicalAxiomCount(); 335 // remove the learned Axioms 336 //man.removeAxiom(finalOntology, learnedAxioms.iterator().next()); 337 Set<OWLAxiom> learnedAxiomsCopy = new LinkedHashSet<>(learnedAxioms); 338 for (OWLAxiom axiom : finalOntology.getLogicalAxioms(Imports.EXCLUDED)) { 339 for (OWLAxiom axiomToRemove : learnedAxiomsCopy) { 340 // conviene usare una copia di probAddedAxioms 341 //in maniera tale da eliminare gli assiomi gi� trovati durante la ricerca e 342 //quindi ridurre il numero di check 343 //logger.debug("Learned axiom to remove: " + BundleUtilities.getManchesterSyntaxString(axiomToRemove)); 344 if (axiomToRemove.equalsIgnoreAnnotations(axiom)) { 345 man.removeAxiom(finalOntology, axiom); 346 learnedAxiomsCopy.remove(axiomToRemove); 347 break; 348 } 349 } 350 } 351 int numAxiomsAfterRemove = finalOntology.getLogicalAxiomCount(); 352 // check if correctly removed 353 if (numAxiomsAfterRemove != numInitialAxioms - learnedAxioms.size()) { 354 String msg = "Error during the replacement of super class: " 355 + "Axiom remotion was incorrect. " 356 + "numAxiomsAfterRemove: " + numAxiomsAfterRemove 357 + " numInitialAxioms: " + numInitialAxioms 358 + " numAxioms to remove: " + learnedAxioms.size() 359 + " numAxioms removed: " + (numInitialAxioms - numAxiomsAfterRemove); 360 logger.error(msg); 361 throw new StructureLearningException(msg); 362 } 363 LinkedHashSet<OWLAxiom> newAxioms = new LinkedHashSet<>(); 364 for (OWLAxiom axiom : learnedAxioms) { 365 OWLAxiom newAxiom; 366 if (axiom.isOfType(AxiomType.SUBCLASS_OF)) { 367 newAxiom = df.getOWLSubClassOfAxiom( 368 ((OWLSubClassOfAxiom) axiom).getSubClass(), 369 clp.getClassToDescribe(), 370 axiom.getAnnotations()); 371 } else if (axiom.isOfType(AxiomType.EQUIVALENT_CLASSES)) { 372 OWLClassExpression clazz = null; 373 for (OWLClassExpression c : ((OWLEquivalentClassesAxiom) axiom).getClassExpressions()) { 374 375 if (c.compareTo(getDummyClass()) != 0) { 376 clazz = c; 377 break; 378 } 379 } 380 if (clazz == null) { 381 throw new UnsupportedLearnedAxiom("The learned axiom " + axiom 382 + "has a null class"); 383 } 384 newAxiom = df.getOWLEquivalentClassesAxiom( 385 clazz, 386 clp.getClassToDescribe(), 387 axiom.getAnnotations()); 388 } else { 389 throw new UnsupportedLearnedAxiom("The learned axiom " + axiom 390 + "is not supported"); 391 } 392 newAxioms.add(newAxiom); 393 logger.info("Learned Axiom: " + newAxiom); 394 } 395 man.addAxioms(finalOntology, newAxioms); 396 // check if correctly added 397 if (numInitialAxioms != numAxiomsAfterRemove + learnedAxioms.size()) { 398 String msg = "Error during the replacement of super class: " 399 + "Axiom addition was incorrect." 400 + " numAxiomsAfterRemove: " + numAxiomsAfterRemove 401 + " numAxioms to add: " + learnedAxioms.size() 402 + " numAxioms added: " + (numInitialAxioms - numAxiomsAfterRemove);; 403 logger.error(msg); 404 throw new StructureLearningException(msg); 405 } 406 logger.debug("Replaced all the super classes"); 407 return finalOntology; 408 } 409 410 /** 411 * It tries to add a set of axioms into the ontology. If there is an inconsistency 412 * after adding the axiom the axiom is removed from the ontology and an 413 * InconsistencyException is thrown. 414 * 415 * @param ontology ontology to modify 416 * @param axioms axioms to add 417 * @throws InconsistencyException if adding the exceptions leads to an 418 * inconsistency 419 */ 420 protected void addAxioms(OWLOntology ontology, List<? extends OWLAxiom> axioms) throws InconsistencyException { 421 OWLOntologyManager manager = ontology.getOWLOntologyManager(); 422 manager.addAxioms(ontology, new HashSet<>(axioms)); 423// PelletReasoner pelletReasoner = new PelletReasonerFactory().createReasoner(ontology); 424 PelletReasoner pelletReasoner = PelletReasonerFactory.getInstance().createNonBufferingReasoner(ontology); 425 if (!pelletReasoner.isConsistent()) { 426 String message = "The axiom will make the KB inconsistent.\n" 427 + "It will NOT be added"; 428 logger.warn(message); 429 manager.removeAxioms(ontology, new HashSet<>(axioms)); 430 pelletReasoner.dispose(); 431 throw new InconsistencyException(message); 432 } 433 pelletReasoner.dispose(); 434 } 435 436 protected void removeAxioms(OWLOntology ontology, List<? extends OWLAxiom> axioms) { 437 OWLOntologyManager manager = ontology.getOWLOntologyManager(); 438 manager.removeAxioms(ontology, new HashSet<>(axioms)); 439 } 440 441 /** 442 * @return the classAxiomType 443 */ 444 public String getClassAxiomType() { 445 return classAxiomType; 446 } 447 448 /** 449 * @param classAxiomType the classAxiomType to set 450 */ 451 public void setClassAxiomType(String classAxiomType) { 452 this.classAxiomType = classAxiomType; 453 } 454 455 /** 456 * @param maxExecutionTimeInSeconds the maxExecutionTimeInSeconds to set 457 */ 458 public void setMaxExecutionTimeInSeconds(int maxExecutionTimeInSeconds) { 459 this.maxExecutionTimeInSeconds = maxExecutionTimeInSeconds; 460 } 461 462 /** 463 * @param blockSizeGreedySearch the blockSizeGreedySearch to set 464 */ 465 public void setBlockSizeGreedySearch(int blockSizeGreedySearch) { 466 this.blockSizeGreedySearch = blockSizeGreedySearch; 467 } 468 469}