001package org.dllearner.algorithms.probabilistic.structure.unife.leap; 002 003import java.math.BigDecimal; 004import java.math.RoundingMode; 005import java.util.Collections; 006import java.util.HashSet; 007import java.util.LinkedHashSet; 008import java.util.List; 009import java.util.Map; 010import java.util.NavigableSet; 011import java.util.Set; 012import org.dllearner.algorithms.probabilistic.parameter.unife.edge.AbstractEDGE; 013import org.dllearner.core.AbstractCELA; 014import org.dllearner.core.ComponentAnn; 015import org.dllearner.core.ComponentInitException; 016import org.dllearner.core.EvaluatedDescription; 017import org.dllearner.learningproblems.ClassLearningProblem; 018import org.slf4j.Logger; 019import org.slf4j.LoggerFactory; 020import org.dllearner.core.probabilistic.unife.StructureLearningException; 021import org.dllearner.exceptions.UnsupportedLearnedAxiom; 022 023import org.dllearner.utils.unife.OWLUtils; 024import org.semanticweb.owlapi.apibinding.OWLManager; 025import org.semanticweb.owlapi.model.AxiomType; 026import org.semanticweb.owlapi.model.OWLAnnotation; 027import org.semanticweb.owlapi.model.OWLAxiom; 028import org.semanticweb.owlapi.model.OWLDataFactory; 029import org.semanticweb.owlapi.model.OWLEquivalentClassesAxiom; 030import org.semanticweb.owlapi.model.OWLOntology; 031import org.semanticweb.owlapi.model.OWLOntologyManager; 032import org.semanticweb.owlapi.model.OWLOntologyStorageException; 033import org.semanticweb.owlapi.model.OWLSubClassOfAxiom; 034import unife.bundle.exception.InconsistencyException; 035import unife.bundle.utilities.BundleUtilities; 036import unife.math.utilities.MathUtilities; 037import static unife.utilities.GeneralUtils.safe; 038 039@ComponentAnn(name = "LEAP", shortName = "leap", version = 1.0) 040public class LEAP extends AbstractLEAP { 041 042 private static final Logger logger = LoggerFactory.getLogger(LEAP.class); 043 044 public LEAP() { 045 046 } 047 048 public LEAP(AbstractCELA cela, AbstractEDGE lpr) { 049 super(cela, lpr); 050 } 051 052 @Override 053 public void init() throws ComponentInitException { 054 super.init(); 055 } 056 057 @Override 058 public void start() { 059 stop = false; 060 isRunning = true; 061 062 long totalTimeMills = System.currentTimeMillis(); 063 long celaTimeMills = 0; 064 //AbstractEDGEDistributed edge = (AbstractEDGEDistributed) pla; 065 // First step: run Distributed EDGE 066 //edge.start(); 067 068// logger.debug("First EDGE cycle terminated."); 069 //logger.debug("Initial Log-likelihood: " + edge.getLL()); 070 //OWLOntology originalOntology = edge.getLearnedOntology(); 071 logger.debug("Starting structure learner LEAP"); 072// Set<KnowledgeSource> newSources = Collections.singleton((KnowledgeSource) new OWLAPIOntology(ontology)); 073// AbstractReasonerComponent reasoner = cela.getReasoner(); 074// reasoner.changeSources(newSources); 075// try { 076// reasoner.init(); 077// cela.init(); 078// } catch (ComponentInitException cie) { 079// logger.error("Error: " + cie.getMessage()); 080// throw new StructureLearningException(cie); 081// } 082 // start class expression learning algorithm 083 celaTimeMills = System.currentTimeMillis(); 084 cela.start(); 085 cela.getReasoner().releaseKB(); 086 celaTimeMills = System.currentTimeMillis() - celaTimeMills; 087 // get the best class expressions 088 NavigableSet<? extends EvaluatedDescription> evaluatedDescriptions = cela.getCurrentlyBestEvaluatedDescriptions(); 089 // convert the class expressions into axioms 090// OWLOntologyManager manager = OWLManager.createOWLOntologyManager(); 091 OWLOntologyManager manager = edge.getSourcesOntology().getOWLOntologyManager(); 092 List<? extends OWLAxiom> candidateAxioms; 093 if (getClassAxiomType().equalsIgnoreCase("subClassOf") || getClassAxiomType().equalsIgnoreCase("both")) { 094 candidateAxioms = convertIntoSubClassOfAxioms(manager, evaluatedDescriptions); 095 } else { 096 candidateAxioms = convertIntoEquivalentClassesAxioms(manager, evaluatedDescriptions); 097 } 098 // perform a greedy search 099 logger.info("Start greedy search"); 100 // temporaneo i raffinamenti dopo dovranno essere assegnati ad ogni processo 101 Set<OWLAxiom> learnedAxioms = null; 102 try { 103 learnedAxioms = greedySearch(candidateAxioms); 104 } catch (UnsupportedLearnedAxiom ex) { 105 logger.error(ex.getMessage()); 106 System.exit(-1); 107 } 108 logger.info("Greedy search finished"); 109 110 OWLOntology finalOntology = edge.getSourcesOntology(); 111 // In case replace super class 112 if (cela.getLearningProblem() instanceof ClassLearningProblem) { 113 try { 114 finalOntology = replaceDummyClass(finalOntology, learnedAxioms); 115 } catch (UnsupportedLearnedAxiom ex) { 116 logger.error(ex.getMessage()); 117 System.exit(-1); 118 } 119 } else { 120 for (OWLAxiom axiom : safe(learnedAxioms)) { 121 logger.info("Learned Axiom: " + axiom); 122 } 123 } 124 // final step save the ontology 125 try { 126 logger.info("Saving the learned ontology"); 127 OWLUtils.saveOntology(finalOntology, outputFile, outFormat); 128 } catch (OWLOntologyStorageException e) { 129 String msg = "Cannot save the learned ontology: " + e.getMessage(); 130 throw new StructureLearningException(msg); 131 } 132 totalTimeMills = System.currentTimeMillis() - totalTimeMills; 133 printTimings(totalTimeMills, celaTimeMills, timers); 134 } 135 136 /** 137 * Returns the name of the algorithm. 138 * 139 * @return "LEAP" 140 */ 141 public static String getName() { 142 return "LEAP"; 143 } 144 145 /** 146 * This method performs a greedy search in the space of Theories. Given a 147 * set of axioms it executes a loop for each axiom. For each iteration it 148 * adds an axiom into the knowledge base and if the log-likelihood (LL) 149 * increases the axiom is kept otherwise it is removed from the ontology. 150 * For EquivalentClassesAxiom: if we keep an EquivalentClassesAxiom because 151 * the LL has increased but a SubClassOfAxiom with the same classes has been 152 * already added, the SubClassOfAxiom is removed. 153 * 154 * @param candidateAxioms the set of candidate axiom that we would like to 155 * add in the knowledge base. 156 * @return the set of axioms added in the knowledge base. 157 */ 158 private Set<OWLAxiom> greedySearch(List<? extends OWLAxiom> candidateAxioms) throws UnsupportedLearnedAxiom { 159 BigDecimal bestLL; 160 if (edge instanceof AbstractEDGE) { 161 bestLL = ((AbstractEDGE) edge).getLOGZERO().multiply( 162 new BigDecimal(edge.getPositiveExampleAxioms().size())); 163 bestLL = bestLL.setScale(accuracy, RoundingMode.HALF_UP); 164 } else { 165 bestLL = BigDecimal.ZERO.multiply( 166 new BigDecimal(edge.getPositiveExampleAxioms().size())); 167 bestLL = bestLL.setScale(accuracy, RoundingMode.HALF_UP); 168 } 169 logger.debug("Initial Log-likelihood: " + bestLL.toString()); 170// BigDecimal bestLL = edge.getLL(); 171// logger.debug("Resetting EDGE"); 172// edge.reset(); 173 OWLOntology ontology = edge.getLearnedOntology(); 174 edge.changeSourcesOntology(ontology); // queste operazioni fanno perdere tempo, sono da ottimizzare 175 LinkedHashSet<OWLAxiom> learnedAxioms = new LinkedHashSet<>(); 176 OWLDataFactory df = ontology.getOWLOntologyManager().getOWLDataFactory(); 177 String infoMsg = "Type of axiom to learn: "; 178 switch (getClassAxiomType().toLowerCase()) { 179 case "subclassof": 180 infoMsg += "subClassOf axioms"; 181 break; 182 case "equivalentclasses": 183 infoMsg += "equivalentClasses axioms"; 184 break; 185 case "both": 186 throw new UnsupportedLearnedAxiom("LEAP cannot learn this type of axioms: " + getClassAxiomType()); 187// infoMsg += "subClassOf and equivalentClasses axioms"; 188// break; 189 default: 190 throw new UnsupportedLearnedAxiom("LEAP cannot learn this type of axioms: " + getClassAxiomType()); 191 } 192 logger.info(infoMsg); 193// int i = 0; 194 int numChunks = (int) Math.ceil((double) candidateAxioms.size() / blockSizeGreedySearch); 195 logger.info("number of axiom chunks: " + numChunks); 196// for (OWLAxiom axiom : candidateAxioms) { 197 for (int i = 0; i < numChunks; i++) { 198 int lastIndex = i < numChunks - 1 ? (i + 1) * blockSizeGreedySearch : candidateAxioms.size(); 199 List<? extends OWLAxiom> axioms = candidateAxioms.subList(i * blockSizeGreedySearch, lastIndex); 200 if (i >= 0) { 201 for (OWLAxiom axiom : axioms) { 202 logger.info("Adding axiom: " + axiom); 203 } 204 try { 205 addAxioms(ontology, axioms); 206 } catch (InconsistencyException iex) { 207 logger.info(iex.getMessage()); 208 logger.info("Trying with the next class expression"); 209 continue; 210 } 211 logger.info("Running parameter learner"); 212 edge.start(); 213 BigDecimal currLL = edge.getLL(); 214 logger.info("Current Log-Likelihood: " + currLL); 215 if (getClassAxiomType().equalsIgnoreCase("both")) { 216 // Not supported yet 217 // TO DO: I need to save the probabilistic values before adding 218 // the equivalentClasses axiom. Because if I add the equivalentClasses axiom 219 // and the resulting Log-likelihood is worse than adding the 220 // subClassOf axiom, then I need to recover the previously 221 // computated probabilistic values 222 // (without running EDGE again) 223 throw new UnsupportedOperationException("Not supported yet."); 224 /* To uncomment in the future after the modifications of EDGE 225 logger.info("Trying to add the corresponding equivalent axiom"); 226 logger.debug("but first I remove the axiom: " + axiom); 227 removeAxiom(ontology, axiom); 228 // here i need to sava the probabilistic values. A solution could be 229 // to modify EDGE. It should update the PMap object when it learns the 230 // parameters and EDGE should read it when the learned ontology 231 // is requested 232 OWLEquivalentClassesAxiom equivAxiom = OWLUtils.convertSubClassOfIntoEquivalentClassesAxiom((OWLSubClassOfAxiom) axiom); 233 logger.debug("Adding axiom: " + equivAxiom); 234 try { 235 addAxiom(ontology, axiom); 236 } catch (InconsistencyException iex) { 237 logger.info(iex.getMessage()); 238 logger.info("Trying with the next class expression"); 239 continue; 240 } 241 logger.info("Running parameter learner"); 242 edge.start(); 243 BigDecimal currLL2 = edge.getLL(); 244 // if adding the equivalentClasses axioms leads to better log-likelihood I keep it 245 if (currLL2.compareTo(currLL) > 0) { 246 currLL = currLL2; 247 axiom = equivAxiom; 248 } else { 249 removeAxiom(ontology, equivAxiom); 250 ontology.getOWLOntologyManager().addAxiom(ontology, axiom); 251 }*/ 252 } 253 if (currLL.compareTo(bestLL) > 0) { 254 logger.info("Log-Likelihood enhanced. Updating ontologies..."); 255 // I recover the annotation containing the learned probabilistic values 256 257 for (OWLAxiom axiom : axioms) { 258 OWLAxiom updatedAxiom; 259 260 OWLAnnotation annotation = df. 261 getOWLAnnotation(BundleUtilities.PROBABILISTIC_ANNOTATION_PROPERTY, 262 df.getOWLLiteral(edge.getParameter(axiom).doubleValue())); 263 264 if (axiom.isOfType(AxiomType.SUBCLASS_OF)) { 265 updatedAxiom = df.getOWLSubClassOfAxiom( 266 ((OWLSubClassOfAxiom) axiom).getSubClass(), 267 ((OWLSubClassOfAxiom) axiom).getSuperClass(), 268 Collections.singleton(annotation)); 269 } else { 270 if (axiom.isOfType(AxiomType.EQUIVALENT_CLASSES)) { 271 272 // I have to remove the subsumption a 273// for (OWLClass subClass : ((OWLEquivalentClassesAxiom) axiom).getNamedClasses()) { 274// if (subClass.compareTo(getDummyClass()) != 0) { 275//// subClass = c; 276// for (OWLAxiom lax : learnedAxioms) { 277// 278// if (lax.isOfType(AxiomType.SUBCLASS_OF) 279// && ((OWLSubClassOfAxiom) lax).getSubClass().compareTo(subClass) == 0) { 280// logger.in removeAxiom(ontology, lax 281// ); 282// learnedAxioms.remove(lax); 283// break; 284// } 285// } 286// break; 287// } 288// } 289 updatedAxiom = df.getOWLEquivalentClassesAxiom( 290 ((OWLEquivalentClassesAxiom) axiom).getClassExpressions(), 291 Collections.singleton(annotation)); 292 } else { 293 throw new UnsupportedLearnedAxiom("The axiom to add is not supported: " 294 + BundleUtilities.getManchesterSyntaxString(axiom)); 295 } 296 } 297 learnedAxioms.add(updatedAxiom); 298 } 299 updateOntology(); // queste operazioni fanno perdere tempo, sono da ottimizzare 300 bestLL = currLL; 301 } else { 302 logger.info("Log-Likelihood worsened. Removing Last Axioms..."); 303 removeAxioms(ontology, axioms); 304 } 305 for (Map.Entry<String, Long> timer : edge.getTimeMap().entrySet()) { 306 Long previousValue = timers.get(timer.getKey()); 307 if (previousValue == null) { 308 previousValue = 0L; 309 } 310 timers.put(timer.getKey(), previousValue + timer.getValue()); 311 } 312 } 313 } 314 return learnedAxioms; 315 } 316 317 private void updateOntology() { 318 logger.debug("Updating ontology"); 319 OWLOntology ontology = edge.getLearnedOntology(); 320 edge.changeSourcesOntology(ontology); 321 logger.debug("Ontology Updated"); 322 } 323 324}