001 /**
002 * Copyright (C) 2007-2008, 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 *
019 */
020
021 package org.dllearner.algorithms.refinement2;
022
023 import java.io.File;
024 import java.util.Collection;
025 import java.util.LinkedList;
026 import java.util.List;
027 import java.util.Set;
028 import java.util.SortedSet;
029
030 import org.apache.log4j.Level;
031 import org.apache.log4j.Logger;
032 import org.dllearner.core.ComponentInitException;
033 import org.dllearner.core.LearningAlgorithm;
034 import org.dllearner.core.LearningProblem;
035 import org.dllearner.core.ReasonerComponent;
036 import org.dllearner.core.configurators.ROLComponent2Configurator;
037 import org.dllearner.core.options.BooleanConfigOption;
038 import org.dllearner.core.options.CommonConfigMappings;
039 import org.dllearner.core.options.CommonConfigOptions;
040 import org.dllearner.core.options.ConfigEntry;
041 import org.dllearner.core.options.ConfigOption;
042 import org.dllearner.core.options.DoubleConfigOption;
043 import org.dllearner.core.options.IntegerConfigOption;
044 import org.dllearner.core.options.InvalidConfigOptionValueException;
045 import org.dllearner.core.options.StringConfigOption;
046 import org.dllearner.core.owl.ClassHierarchy;
047 import org.dllearner.core.owl.Description;
048 import org.dllearner.core.owl.NamedClass;
049 import org.dllearner.core.owl.ObjectProperty;
050 import org.dllearner.learningproblems.EvaluatedDescriptionPosNeg;
051 import org.dllearner.learningproblems.PosNegLP;
052 import org.dllearner.learningproblems.PosOnlyLP;
053 import org.dllearner.learningproblems.ScorePosNeg;
054 import org.dllearner.reasoning.ReasonerType;
055 import org.dllearner.refinementoperators.RhoDRDown;
056 import org.dllearner.utilities.Files;
057 import org.dllearner.utilities.Helper;
058
059 /**
060 * The DL-Learner learning algorithm component for the example
061 * based refinement operator approach. It handles all
062 * configuration options, creates the corresponding objects and
063 * passes them to the actual refinement operator, heuristic, and
064 * learning algorithm implementations.
065 *
066 * Note: The options supported by the ROLearner component and this
067 * one are not equal. Options that have been dropped for now:
068 * - horizontal expansion factor: The goal of the algorithm will
069 * be to (hopefully) be able to learn long and complex concepts
070 * more efficiently.
071 * A horizontal expansion factor has its benefits, but limits
072 * the length of concepts learnable in reasonable time to
073 * about 15 with its default value of 0.6 and a small sized
074 * background knowledge base. We hope to get more fine-grained
075 * control of whether it makes sense to extend a node with
076 * more sophisticated heuristics.
077 * Dropping the horizontal expansion factor means that the
078 * completeness of the algorithm depends on the heuristic.
079 *
080 * @author Jens Lehmann
081 *
082 */
083 public class ROLComponent2 extends LearningAlgorithm {
084
085 private ROLComponent2Configurator configurator;
086 @Override
087 public ROLComponent2Configurator getConfigurator(){
088 return configurator;
089 }
090
091 // actual algorithm
092 private ROLearner2 algorithm;
093 private static Logger logger = Logger
094 .getLogger(ROLComponent2.class);
095 private String logLevel = CommonConfigOptions.logLevelDefault;
096
097 // configuration options
098 private boolean writeSearchTree;
099 private File searchTreeFile;
100 private boolean replaceSearchTree = false;
101 private static String defaultSearchTreeFile = "log/searchTree.txt";
102 private String heuristic = "multi";
103 Set<NamedClass> allowedConcepts;
104 Set<ObjectProperty> allowedRoles;
105 Set<NamedClass> ignoredConcepts;
106 Set<ObjectProperty> ignoredRoles;
107 // these are computed as the result of the previous four settings
108 Set<NamedClass> usedConcepts;
109 Set<ObjectProperty> usedRoles;
110 private boolean applyAllFilter = true;
111 private boolean applyExistsFilter = true;
112 private boolean useTooWeakList = true;
113 private boolean useOverlyGeneralList = true;
114 private boolean useShortConceptConstruction = true;
115 private boolean improveSubsumptionHierarchy = true;
116 private boolean useAllConstructor = CommonConfigOptions.useAllConstructorDefault;
117 private boolean useExistsConstructor = CommonConfigOptions.useExistsConstructorDefault;
118 private boolean useHasValueConstructor = CommonConfigOptions.useHasValueConstructorDefault;
119 private int valueFrequencyThreshold = CommonConfigOptions.valueFrequencyThresholdDefault;
120 private boolean useCardinalityRestrictions = CommonConfigOptions.useCardinalityRestrictionsDefault;
121 private boolean useNegation = CommonConfigOptions.useNegationDefault;
122 private boolean useBooleanDatatypes = CommonConfigOptions.useBooleanDatatypesDefault;
123 private boolean useDoubleDatatypes = CommonConfigOptions.useDoubleDatatypesDefault;
124 private static double noisePercentageDefault = 0.0;
125 private double noisePercentage = noisePercentageDefault;
126 private NamedClass startClass = null;
127 //refactor this
128 private static boolean usePropernessChecksDefault = false;
129 private boolean usePropernessChecks = usePropernessChecksDefault;
130 // refactor this
131 private static int maxPosOnlyExpansionDefault = 4;
132 private int maxPosOnlyExpansion = maxPosOnlyExpansionDefault;
133 private boolean forceRefinementLengthIncrease = true;
134 //extended Options
135 //in seconds
136 private int maxExecutionTimeInSeconds = CommonConfigOptions.maxExecutionTimeInSecondsDefault;
137 private int minExecutionTimeInSeconds = CommonConfigOptions.minExecutionTimeInSecondsDefault;
138 private int guaranteeXgoodDescriptions = CommonConfigOptions.guaranteeXgoodDescriptionsDefault;
139 private int maxClassDescriptionTests = CommonConfigOptions.maxClassDescriptionTestsDefault;
140
141 // Variablen zur Einstellung der Protokollierung
142 // boolean quiet = false;
143 boolean showBenchmarkInformation = false;
144 // boolean createTreeString = false;
145 // String searchTree = new String();
146
147 // Konfiguration des Algorithmus
148 // Faktor für horizontale Erweiterung (notwendig für completeness)
149 // double horizontalExpansionFactor = 0.6;
150
151 // soll später einen Operator und eine Heuristik entgegennehmen
152 // public ROLearner(LearningProblem learningProblem, LearningProblem learningProblem2) {
153 public ROLComponent2(PosNegLP learningProblem, ReasonerComponent reasoningService) {
154 super(learningProblem, reasoningService);
155 this.configurator = new ROLComponent2Configurator(this);
156 }
157
158 public ROLComponent2(PosOnlyLP learningProblem, ReasonerComponent reasoningService) {
159 super(learningProblem, reasoningService);
160 this.configurator = new ROLComponent2Configurator(this);
161 }
162
163 public static Collection<Class<? extends LearningProblem>> supportedLearningProblems() {
164 Collection<Class<? extends LearningProblem>> problems = new LinkedList<Class<? extends LearningProblem>>();
165 problems.add(PosNegLP.class);
166 problems.add(PosOnlyLP.class);
167 return problems;
168 }
169
170 public static Collection<ConfigOption<?>> createConfigOptions() {
171 Collection<ConfigOption<?>> options = new LinkedList<ConfigOption<?>>();
172 options.add(new BooleanConfigOption("writeSearchTree", "specifies whether to write a search tree", false));
173 options.add(new StringConfigOption("searchTreeFile","file to use for the search tree", defaultSearchTreeFile));
174 options.add(new BooleanConfigOption("replaceSearchTree","specifies whether to replace the search tree in the log file after each run or append the new search tree", false));
175 StringConfigOption heuristicOption = new StringConfigOption("heuristic", "specifiy the heuristic to use", "lexicographic");
176 heuristicOption.setAllowedValues(new String[] {"lexicographic", "flexible"});
177 options.add(heuristicOption);
178 options.add(new BooleanConfigOption("applyAllFilter", "usage of equivalence ALL R.C AND ALL R.D = ALL R.(C AND D)", true));
179 options.add(new BooleanConfigOption("applyExistsFilter", "usage of equivalence EXISTS R.C OR EXISTS R.D = EXISTS R.(C OR D)", true));
180 options.add(new BooleanConfigOption("useTooWeakList", "try to filter out too weak concepts without sending them to the reasoner", true));
181 options.add(new BooleanConfigOption("useOverlyGeneralList", "try to find overly general concept without sending them to the reasoner", true));
182 options.add(new BooleanConfigOption("useShortConceptConstruction", "shorten concept to see whether they already exist", true));
183 DoubleConfigOption horizExp = new DoubleConfigOption("horizontalExpansionFactor", "horizontal expansion factor (see publication for description)", 0.6);
184 horizExp.setLowerLimit(0.0);
185 horizExp.setUpperLimit(1.0);
186 options.add(horizExp);
187 options.add(new BooleanConfigOption("improveSubsumptionHierarchy", "simplify subsumption hierarchy to reduce search space (see publication for description)", true));
188 // allowed/ignored concepts/roles could also be a reasoner option (?)
189 options.add(CommonConfigOptions.allowedConcepts());
190 options.add(CommonConfigOptions.ignoredConcepts());
191 options.add(CommonConfigOptions.allowedRoles());
192 options.add(CommonConfigOptions.ignoredRoles());
193 options.add(CommonConfigOptions.useAllConstructor());
194 options.add(CommonConfigOptions.useExistsConstructor());
195 options.add(CommonConfigOptions.useHasValueConstructor());
196 options.add(CommonConfigOptions.valueFreqencyThreshold());
197 options.add(CommonConfigOptions.useCardinalityRestrictions());
198 options.add(CommonConfigOptions.cardinalityLimit());
199 options.add(CommonConfigOptions.useNegation());
200 options.add(CommonConfigOptions.useBooleanDatatypes());
201 options.add(CommonConfigOptions.useDoubleDatatypes());
202 options.add(CommonConfigOptions.maxExecutionTimeInSeconds());
203 options.add(CommonConfigOptions.minExecutionTimeInSeconds());
204 options.add(CommonConfigOptions.guaranteeXgoodDescriptions());
205 options.add(CommonConfigOptions.maxClassDescriptionTests());
206 options.add(CommonConfigOptions.getLogLevel());
207 options.add(new BooleanConfigOption("usePropernessChecks", "specifies whether to check for equivalence (i.e. discard equivalent refinements)",usePropernessChecksDefault));
208 options.add(new IntegerConfigOption("maxPosOnlyExpansion", "specifies how often a node in the search tree of a posonly learning problem needs to be expanded before it is" +
209 " considered as solution candidate",maxPosOnlyExpansionDefault));
210 options.add(CommonConfigOptions.getNoisePercentage());
211 options.add(CommonConfigOptions.getTerminateOnNoiseReached());
212 options.add(new StringConfigOption("startClass", "the named class which should be used to start the algorithm (GUI: needs a widget for selecting a class)"));
213 options.add(new BooleanConfigOption("forceRefinementLengthIncrease", "specifies whether nodes should be expanded until only longer refinements are reached"));
214 options.add(new DoubleConfigOption("negativeWeight", "Used to penalise errors on negative examples different from those of positive examples (lower = less importance for negatives).",1.0));
215 options.add(new DoubleConfigOption("startNodeBonus", "You can use this to give a heuristic bonus on the start node (= initially broader exploration of search space).",0.0));
216 options.add(new IntegerConfigOption("negationPenalty", "Penalty on negations (TODO: better explanation).", 0));
217 options.add(CommonConfigOptions.getExpansionPenaltyFactor(0.02));
218 return options;
219 }
220
221 /* (non-Javadoc)
222 * @see org.dllearner.core.Component#applyConfigEntry(org.dllearner.core.ConfigEntry)
223 */
224 @Override
225 @SuppressWarnings({"unchecked"})
226 public <T> void applyConfigEntry(ConfigEntry<T> entry) throws InvalidConfigOptionValueException {
227 String name = entry.getOptionName();
228 if(name.equals("writeSearchTree"))
229 writeSearchTree = (Boolean) entry.getValue();
230 else if(name.equals("searchTreeFile"))
231 searchTreeFile = new File((String)entry.getValue());
232 else if(name.equals("replaceSearchTree"))
233 replaceSearchTree = (Boolean) entry.getValue();
234 else if(name.equals("heuristic")) {
235 String value = (String) entry.getValue();
236 if(value.equals("lexicographic"))
237 heuristic = "lexicographic";
238 else
239 heuristic = "flexible";
240 } else if(name.equals("allowedConcepts")) {
241 allowedConcepts = CommonConfigMappings.getAtomicConceptSet((Set<String>)entry.getValue());
242 } else if(name.equals("allowedRoles")) {
243 allowedRoles = CommonConfigMappings.getAtomicRoleSet((Set<String>)entry.getValue());
244 } else if(name.equals("ignoredConcepts")) {
245 ignoredConcepts = CommonConfigMappings.getAtomicConceptSet((Set<String>)entry.getValue());
246 } else if(name.equals("ignoredRoles")) {
247 ignoredRoles = CommonConfigMappings.getAtomicRoleSet((Set<String>)entry.getValue());
248 } else if(name.equals("applyAllFilter")) {
249 applyAllFilter = (Boolean) entry.getValue();
250 } else if(name.equals("applyExistsFilter")) {
251 applyExistsFilter = (Boolean) entry.getValue();
252 } else if(name.equals("useTooWeakList")) {
253 useTooWeakList = (Boolean) entry.getValue();
254 } else if(name.equals("useOverlyGeneralList")) {
255 useOverlyGeneralList = (Boolean) entry.getValue();
256 } else if(name.equals("useShortConceptConstruction")) {
257 useShortConceptConstruction = (Boolean) entry.getValue();
258 } else if(name.equals("improveSubsumptionHierarchy")) {
259 improveSubsumptionHierarchy = (Boolean) entry.getValue();
260 } else if(name.equals("useAllConstructor")) {
261 useAllConstructor = (Boolean) entry.getValue();
262 } else if(name.equals("useExistsConstructor")) {
263 useExistsConstructor = (Boolean) entry.getValue();
264 } else if(name.equals("useHasValueConstructor")) {
265 useHasValueConstructor = (Boolean) entry.getValue();
266 } else if(name.equals("valueFrequencyThreshold")) {
267 valueFrequencyThreshold = (Integer) entry.getValue();
268 } else if(name.equals("useCardinalityRestrictions")) {
269 useCardinalityRestrictions = (Boolean) entry.getValue();
270 } else if(name.equals("useNegation")) {
271 useNegation = (Boolean) entry.getValue();
272 } else if(name.equals("noisePercentage")) {
273 noisePercentage = (Double) entry.getValue();
274 } else if(name.equals("useBooleanDatatypes")) {
275 useBooleanDatatypes = (Boolean) entry.getValue();
276 } else if(name.equals("useDoubleDatatypes")) {
277 useDoubleDatatypes = (Boolean) entry.getValue();
278 } else if(name.equals("usePropernessChecks")) {
279 usePropernessChecks = (Boolean) entry.getValue();
280 } else if(name.equals("maxPosOnlyExpansion")) {
281 maxPosOnlyExpansion = (Integer) entry.getValue();
282 } else if(name.equals("startClass")) {
283 startClass = new NamedClass((String)entry.getValue());
284 }else if(name.equals("maxExecutionTimeInSeconds")) {
285 maxExecutionTimeInSeconds = (Integer) entry.getValue();
286 }else if(name.equals("minExecutionTimeInSeconds")) {
287 minExecutionTimeInSeconds = (Integer) entry.getValue();
288 }else if(name.equals("guaranteeXgoodDescriptions")) {
289 guaranteeXgoodDescriptions = (Integer) entry.getValue();
290 } else if(name.equals("maxClassDescriptionTests")) {
291 maxClassDescriptionTests = (Integer) entry.getValue();
292 } else if(name.equals("logLevel")) {
293 logLevel = ((String)entry.getValue()).toUpperCase();
294 } else if(name.equals("forceRefinementLengthIncrease")) {
295 forceRefinementLengthIncrease = (Boolean) entry.getValue();
296 }
297 }
298
299 /* (non-Javadoc)
300 * @see org.dllearner.core.Component#init()
301 */
302 @Override
303 public void init() throws ComponentInitException {
304
305 // exit with a ComponentInitException if the reasoner is unsupported for this learning algorithm
306 if(reasoner.getReasonerType() == ReasonerType.DIG) {
307 throw new ComponentInitException("DIG does not support the inferences needed in the selected learning algorithm component: " + getName());
308 }
309
310 // set log level if the option has been set
311 if(!logLevel.equals(CommonConfigOptions.logLevelDefault))
312 logger.setLevel(Level.toLevel(logLevel,Level.toLevel(CommonConfigOptions.logLevelDefault)));
313
314 if(searchTreeFile == null)
315 searchTreeFile = new File(defaultSearchTreeFile);
316
317 if(writeSearchTree)
318 Files.clearFile(searchTreeFile);
319
320 // adjust heuristic
321 ExampleBasedHeuristic algHeuristic;
322
323 if(heuristic == "lexicographic")
324 algHeuristic = new LexicographicHeuristic();
325 else if(heuristic == "flexible") {
326 if(learningProblem instanceof PosOnlyLP) {
327 throw new RuntimeException("does not work with positive examples only yet");
328 }
329 algHeuristic = new FlexibleHeuristic(((PosNegLP)learningProblem).getNegativeExamples().size(), ((PosNegLP)learningProblem).getPercentPerLengthUnit());
330 } else {
331 if(learningProblem instanceof PosOnlyLP) {
332 // throw new RuntimeException("does not work with positive examples only yet");
333 algHeuristic = new MultiHeuristic(((PosOnlyLP)learningProblem).getPositiveExamples().size(),0, configurator);
334 } else {
335 algHeuristic = new MultiHeuristic(((PosNegLP)learningProblem).getPositiveExamples().size(),((PosNegLP)learningProblem).getNegativeExamples().size(), configurator);
336 }
337 }
338
339 // compute used concepts/roles from allowed/ignored
340 // concepts/roles
341 if(allowedConcepts != null) {
342 // sanity check to control if no non-existing concepts are in the list
343 Helper.checkConcepts(reasoner, allowedConcepts);
344 usedConcepts = allowedConcepts;
345 } else if(ignoredConcepts != null) {
346 usedConcepts = Helper.computeConceptsUsingIgnoreList(reasoner, ignoredConcepts);
347 } else {
348 usedConcepts = Helper.computeConcepts(reasoner);
349 }
350
351 if(allowedRoles != null) {
352 Helper.checkRoles(reasoner, allowedRoles);
353 usedRoles = allowedRoles;
354 } else if(ignoredRoles != null) {
355 Helper.checkRoles(reasoner, ignoredRoles);
356 usedRoles = Helper.difference(reasoner.getObjectProperties(), ignoredRoles);
357 } else {
358 usedRoles = reasoner.getObjectProperties();
359 }
360
361 // prepare subsumption and role hierarchies, because they are needed
362 // during the run of the algorithm;
363 // in contrast to before, the learning algorithms have to maintain their
364 // own view on the class hierarchy
365 ClassHierarchy classHierarchy = reasoner.getClassHierarchy().cloneAndRestrict(usedConcepts);
366 if(improveSubsumptionHierarchy)
367 classHierarchy.thinOutSubsumptionHierarchy();
368
369 // reasoner.prepareRoleHierarchy(usedRoles);
370 // prepare datatype hierarchy only if necessary
371 // if(reasoner.hasDatatypeSupport())
372 // reasoner.prepareDatatypePropertyHierarchy();
373
374 // create a refinement operator and pass all configuration
375 // variables to it
376 RhoDRDown operator = new RhoDRDown(
377 reasoner,
378 classHierarchy,
379 configurator,
380 applyAllFilter,
381 applyExistsFilter,
382 useAllConstructor,
383 useExistsConstructor,
384 useHasValueConstructor,
385 valueFrequencyThreshold,
386 useCardinalityRestrictions,
387 useNegation,
388 useBooleanDatatypes,
389 useDoubleDatatypes,
390 startClass
391 );
392
393 // create an algorithm object and pass all configuration
394 // options to it
395 algorithm = new ROLearner2(
396 configurator,
397 learningProblem,
398 reasoner,
399 operator,
400 algHeuristic,
401 startClass,
402 // usedConcepts,
403 // usedRoles,
404 noisePercentage/(double)100,
405 writeSearchTree,
406 replaceSearchTree,
407 searchTreeFile,
408 useTooWeakList,
409 useOverlyGeneralList,
410 useShortConceptConstruction,
411 usePropernessChecks,
412 maxPosOnlyExpansion,
413 maxExecutionTimeInSeconds,
414 minExecutionTimeInSeconds,
415 guaranteeXgoodDescriptions,
416 maxClassDescriptionTests,
417 forceRefinementLengthIncrease
418 );
419 // note: used concepts and roles do not need to be passed
420 // as argument, because it is sufficient to prepare the
421 // concept and role hierarchy accordingly
422 }
423
424 public static String getName() {
425 return "refinement operator based learning algorithm II";
426 }
427
428 public static String getUsage() {
429 return "algorithm = refexamples;";
430 }
431
432 @Override
433 public void start() {
434 algorithm.start();
435 }
436
437 // @Override
438 public ScorePosNeg getSolutionScore() {
439 return algorithm.getSolutionScore();
440 }
441
442 @Override
443 public Description getCurrentlyBestDescription() {
444 return algorithm.getBestSolution();
445 }
446
447 @Override
448 public synchronized List<Description> getCurrentlyBestDescriptions() {
449 return algorithm.getCurrentlyBestDescriptions();
450 }
451
452 @Override
453 public EvaluatedDescriptionPosNeg getCurrentlyBestEvaluatedDescription() {
454 return new EvaluatedDescriptionPosNeg(algorithm.getBestSolution(),algorithm.getSolutionScore());
455 }
456
457 @Override
458 public synchronized SortedSet<EvaluatedDescriptionPosNeg> getCurrentlyBestEvaluatedDescriptions() {
459 return algorithm.getCurrentlyBestEvaluatedDescriptions();
460 }
461
462 /** {@inheritDoc} */
463 @Override
464 public void stop() {
465 algorithm.stop();
466 }
467
468 public ExampleBasedNode getStartNode() {
469 return algorithm.getStartNode();
470 }
471
472 /** {@inheritDoc} */
473 @Override
474 public void pause() {
475 // TODO: not implemented
476 }
477
478 /** {@inheritDoc} */
479 @Override
480 public void resume() {
481 // TODO: not implemented
482 }
483
484 /* (non-Javadoc)
485 * @see org.dllearner.core.LearningAlgorithm#isRunning()
486 */
487 /** {@inheritDoc} */
488 @Override
489 public boolean isRunning() {
490 return algorithm.isRunning();
491 }
492
493 }