001: /******************************************************************
002: * File: GenericRuleReasoner.java
003: * Created by: Dave Reynolds
004: * Created on: 08-Jun-2003
005: *
006: * (c) Copyright 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
007: * [See end of file]
008: * $Id: GenericRuleReasoner.java,v 1.35 2008/01/22 16:16:24 chris-dollin Exp $
009: *****************************************************************/package com.hp.hpl.jena.reasoner.rulesys;
010:
011: import com.hp.hpl.jena.reasoner.*;
012: import com.hp.hpl.jena.reasoner.rulesys.impl.*;
013: import com.hp.hpl.jena.vocabulary.*;
014: import com.hp.hpl.jena.graph.*;
015: import com.hp.hpl.jena.rdf.model.*;
016:
017: import java.util.*;
018:
019: /**
020: * A reasoner interface that is able to invoke any of the useful
021: * rule engine combinations. The rule set can be set after the reasoner
022: * instance is created. The mode can be set to forward, backward or hybrid.
023: * The OWL-specific rule augmentation can be included. Each of these settings
024: * can be controlled using the configuration graph, specific methods calls or
025: * generic setParameter calls.
026: *
027: * @author <a href="mailto:der@hplb.hpl.hp.com">Dave Reynolds</a>
028: * @version $Revision: 1.35 $ on $Date: 2008/01/22 16:16:24 $
029: */
030: public class GenericRuleReasoner extends FBRuleReasoner {
031:
032: /** Prepared set of rules used for Backward-only mode */
033: protected LPRuleStore bRuleStore;
034:
035: /** the current rule mode */
036: protected RuleMode mode = HYBRID;
037:
038: /** Flag, if true we cache the closure of the pure rule set with its axioms */
039: protected static final boolean cachePreload = true;
040:
041: /** Flag, if true then subClass and subProperty lattices will be optimized using TGCs, only applicable to HYBRID reasoners */
042: protected boolean enableTGCCaching = false;
043:
044: /** Flag, if true then rules will be augmented by OWL translations of the schema */
045: protected boolean enableOWLTranslation = false;
046:
047: /** Optional set of preprocessing hooks to be run in sequence during preparation time, only applicable to HYBRID modes */
048: protected HashSet preprocessorHooks;
049:
050: /** Flag, if true then find results will be filtered to remove functors and illegal RDF */
051: public boolean filterFunctors = true;
052:
053: /** A prebuilt copy of the OWL translation hook */
054: private static final OWLRuleTranslationHook owlTranslator = new OWLRuleTranslationHook();
055:
056: /** Constant - the mode description for pure forward chaining */
057: public static final RuleMode FORWARD = new RuleMode("forward");
058:
059: /** Constant - the mode description for pure forward chaining, using RETE engine */
060: public static final RuleMode FORWARD_RETE = new RuleMode(
061: "forwardRETE");
062:
063: /** Constant - the mode description for pure backward chaining */
064: public static final RuleMode BACKWARD = new RuleMode("backward");
065:
066: /** Constant - the mode description for mixed forward/backward, this is the default mode */
067: public static final RuleMode HYBRID = new RuleMode("hybrid");
068:
069: // =======================================================================
070: // Constructors
071:
072: /**
073: * Constructor. This is the raw version that does not reference a ReasonerFactory
074: * and so has no capabilities description.
075: * @param rules a list of Rule instances which defines the ruleset to process
076: */
077: public GenericRuleReasoner(List rules) {
078: super (rules);
079: }
080:
081: /**
082: * Constructor
083: * @param factory the parent reasoner factory which is consulted to answer capability questions
084: * @param configuration RDF node to configure the rule set and mode, can be null
085: */
086: public GenericRuleReasoner(ReasonerFactory factory,
087: Resource configuration) {
088: super (factory);
089: this .configuration = configuration;
090: if (configuration != null)
091: loadConfiguration(configuration);
092: }
093:
094: /**
095: * Constructor
096: * @param rules a list of Rule instances which defines the ruleset to process
097: * @param factory the parent reasoner factory which is consulted to answer capability questions
098: */
099: public GenericRuleReasoner(List rules, ReasonerFactory factory) {
100: super (rules, factory);
101: }
102:
103: /**
104: * Internal constructor, used to generated a partial binding of a schema
105: * to a rule reasoner instance.
106: */
107: protected GenericRuleReasoner(List rules, Graph schemaGraph,
108: ReasonerFactory factory, RuleMode mode) {
109: this (rules, factory);
110: this .schemaGraph = schemaGraph;
111: this .mode = mode;
112: }
113:
114: // =======================================================================
115: // Parameter control
116:
117: /**
118: * Set the direction of rule inference desired.
119: * If set to a pure mode (FORWARD, BACKWARD) then the rules will be
120: * interpreted as operating in that direction which ever direction
121: * they were written in. In HYBRID mode then the direction of the rule
122: * itself which control whether it is used in forward or backward mode.
123: * In addition, HYBRID mode allows forward rules to generate addition
124: * backward rules.
125: */
126: public void setMode(RuleMode mode) {
127: if (schemaGraph != null) {
128: throw new ReasonerException(
129: "Can't change mode of a reasoner bound to a schema");
130: }
131: this .mode = mode;
132: preload = null;
133: bRuleStore = null;
134: }
135:
136: /**
137: * Set (or change) the rule set that this reasoner should execute.
138: * This will not affect inference models already created from this reasoner.
139: * @param rules a list of Rule objects
140: */
141: public void setRules(List rules) {
142: // Currently redunant but it will differ from the inherited
143: // version in the future
144: super .setRules(rules);
145: }
146:
147: /**
148: * Set to true to enable translation of selected parts of an OWL schema
149: * to additional rules. At present only intersction statements are handled this way.
150: * The translation is only applicable in HYBRID mode.
151: */
152: public void setOWLTranslation(boolean enableOWLTranslation) {
153: if (enableOWLTranslation && (mode != HYBRID)) {
154: throw new ReasonerException(
155: "Can only enable OWL rule translation in HYBRID mode");
156: }
157: this .enableOWLTranslation = enableOWLTranslation;
158: if (enableOWLTranslation) {
159: addPreprocessingHook(owlTranslator);
160: } else {
161: removePreprocessingHook(owlTranslator);
162: }
163: }
164:
165: /**
166: * Set to true to enable caching of subclass/subproperty lattices in a
167: * specialized cache rather than using the rule systems. This has substantially
168: * higher performance but it is done as a separate initialization pass and so
169: * can only work correct with some rule sets. This is only guaranteed to be implemented
170: * for the HYBRID mode.
171: */
172: public void setTransitiveClosureCaching(boolean enableTGCCaching) {
173: this .enableTGCCaching = enableTGCCaching;
174: }
175:
176: /**
177: * Set to true to cause functor-valued literals to be dropped from rule output.
178: * Default is true.
179: */
180: public void setFunctorFiltering(boolean param) {
181: filterFunctors = param;
182: }
183:
184: /**
185: * Add a new preprocessing hook defining an operation that
186: * should be run when the inference graph is being prepared. This can be
187: * used to generate additional data-dependent rules or translations.
188: * This is only guaranted to be implemented for the HYBRID mode.
189: */
190: public void addPreprocessingHook(RulePreprocessHook hook) {
191: if (preprocessorHooks == null) {
192: preprocessorHooks = new HashSet();
193: }
194: preprocessorHooks.add(hook);
195: }
196:
197: /**
198: * Remove a preprocessing hook. defining an operation that
199: */
200: public void removePreprocessingHook(RulePreprocessHook hook) {
201: if (preprocessorHooks != null) {
202: preprocessorHooks.remove(hook);
203: }
204: }
205:
206: protected boolean doSetResourceParameter(Property parameter,
207: Resource value) {
208: if (isRuleSetURL(parameter))
209: addRules(Rule.rulesFromURL(value.getURI()));
210: else if (isRuleSet(parameter)) {
211: addRulesFromURLs(value);
212: addRulesFromStrings(value);
213: } else
214: return false;
215: return true;
216: }
217:
218: private void addRulesFromStrings(Resource value) {
219: Iterator it = getHasRuleStatements(value);
220: while (it.hasNext())
221: addRuleFromString(((Statement) it.next()).getString());
222: }
223:
224: private void addRuleFromString(String ruleString) {
225: addRules(Rule.parseRules(ruleString));
226: }
227:
228: private void addRulesFromURLs(Resource value) {
229: Iterator that = getRuleSetURLStatements(value);
230: while (that.hasNext())
231: addRules(Rule.rulesFromURL(((Statement) that.next())
232: .getResource().getURI()));
233: }
234:
235: private Iterator getHasRuleStatements(Resource value) {
236: return value.listProperties(JenaModelSpec.hasRule).andThen(
237: value.listProperties(ReasonerVocabulary.hasRule));
238: }
239:
240: private Iterator getRuleSetURLStatements(Resource value) {
241: return value.listProperties(JenaModelSpec.ruleSetURL).andThen(
242: value.listProperties(ReasonerVocabulary.ruleSetURL));
243: }
244:
245: private boolean isHasRule(Property parameter) {
246: return parameter.equals(JenaModelSpec.hasRule)
247: && obsolete("hasRule")
248: || parameter.equals(ReasonerVocabulary.hasRule);
249: }
250:
251: private boolean isRuleSet(Property parameter) {
252: return parameter.equals(JenaModelSpec.ruleSet)
253: && obsolete("ruleSet")
254: || parameter.equals(ReasonerVocabulary.ruleSet);
255: }
256:
257: private boolean isRuleSetURL(Property parameter) {
258: return parameter.equals(JenaModelSpec.ruleSetURL)
259: && obsolete("ruleSetURL")
260: || parameter.equals(ReasonerVocabulary.ruleSetURL);
261: }
262:
263: private boolean obsolete(String property) {
264: // System.err.println( ">> OBSOLETE: jms:" + property + "; use " + ReasonerVocabulary.JenaReasonerNS + property + "." );
265: return true;
266: }
267:
268: /**
269: * Internal version of setParameter that does not directly raise an
270: * exception on parameters it does not reconize.
271: * @return false if the parameter was not recognized
272: */
273: protected boolean doSetParameter(Property parameter, Object value) {
274: if (parameter
275: .equals(ReasonerVocabulary.PROPenableFunctorFiltering)) {
276: filterFunctors = Util.convertBooleanPredicateArg(parameter,
277: value);
278: } else if (isHasRule(parameter)) {
279: addRuleFromString(value.toString());
280: } else if (parameter
281: .equals(ReasonerVocabulary.PROPenableOWLTranslation)) {
282: enableOWLTranslation = Util.convertBooleanPredicateArg(
283: parameter, value);
284: if (enableOWLTranslation) {
285: addPreprocessingHook(owlTranslator);
286: } else {
287: removePreprocessingHook(owlTranslator);
288: }
289:
290: } else if (parameter
291: .equals(ReasonerVocabulary.PROPenableTGCCaching)) {
292: enableTGCCaching = Util.convertBooleanPredicateArg(
293: parameter, value);
294:
295: } else if (parameter.equals(ReasonerVocabulary.PROPruleMode)) {
296: if (value.equals(FORWARD.name)) {
297: mode = FORWARD;
298: } else if (value.equals(FORWARD_RETE.name)) {
299: mode = FORWARD_RETE;
300: } else if (value.equals(BACKWARD.name)) {
301: mode = BACKWARD;
302: } else if (value.equals(HYBRID.name)) {
303: mode = HYBRID;
304: } else {
305: throw new IllegalParameterException(
306: "PROPruleMode can only be 'forward'm 'forwardRETE', 'backward', 'hybrid', not "
307: + value);
308: }
309:
310: } else if (parameter.equals(ReasonerVocabulary.PROPruleSet)) {
311: if (value instanceof String) {
312: addRules(loadRules((String) value));
313: } else {
314: throw new IllegalParameterException(
315: "PROPruleSet value should be a URI string. Was a "
316: + value.getClass());
317: }
318: } else {
319: return super .doSetParameter(parameter, value);
320: }
321: return true;
322: }
323:
324: // =======================================================================
325: // Implementation methods
326:
327: /**
328: * Precompute the implications of a schema graph. The statements in the graph
329: * will be combined with the data when the final InfGraph is created.
330: */
331: public Reasoner bindSchema(Graph tbox) throws ReasonerException {
332: if (schemaGraph != null) {
333: throw new ReasonerException(
334: "Can only bind one schema at a time to a GenericRuleReasoner");
335: }
336: Graph graph = null;
337: if (mode == FORWARD) {
338: graph = new BasicForwardRuleInfGraph(this , rules, null,
339: tbox);
340: ((InfGraph) graph).prepare();
341: } else if (mode == FORWARD_RETE) {
342: graph = new RETERuleInfGraph(this , rules, null, tbox);
343: ((InfGraph) graph).prepare();
344: } else if (mode == BACKWARD) {
345: graph = tbox;
346: } else {
347: List ruleSet = rules;
348: graph = new FBRuleInfGraph(this , ruleSet, getPreload(),
349: tbox);
350: if (enableTGCCaching)
351: ((FBRuleInfGraph) graph).setUseTGCCache();
352: ((FBRuleInfGraph) graph).prepare();
353: }
354: GenericRuleReasoner grr = new GenericRuleReasoner(rules, graph,
355: factory, mode);
356: grr.setDerivationLogging(recordDerivations);
357: grr.setTraceOn(traceOn);
358: grr.setTransitiveClosureCaching(enableTGCCaching);
359: grr.setFunctorFiltering(filterFunctors);
360: if (preprocessorHooks != null) {
361: for (Iterator i = preprocessorHooks.iterator(); i.hasNext();) {
362: grr.addPreprocessingHook((RulePreprocessHook) i.next());
363: }
364: }
365: return grr;
366: }
367:
368: /**
369: * Attach the reasoner to a set of RDF data to process.
370: * The reasoner may already have been bound to specific rules or ontology
371: * axioms (encoded in RDF) through earlier bindRuleset calls.
372: *
373: * @param data the RDF data to be processed, some reasoners may restrict
374: * the range of RDF which is legal here (e.g. syntactic restrictions in OWL).
375: * @return an inference graph through which the data+reasoner can be queried.
376: * @throws ReasonerException if the data is ill-formed according to the
377: * constraints imposed by this reasoner.
378: */
379: public InfGraph bind(Graph data) throws ReasonerException {
380: Graph schemaArg = schemaGraph == null ? getPreload()
381: : schemaGraph;
382: InfGraph graph = null;
383: if (mode == FORWARD) {
384: graph = new BasicForwardRuleInfGraph(this , rules, schemaArg);
385: ((BasicForwardRuleInfGraph) graph).setTraceOn(traceOn);
386: } else if (mode == FORWARD_RETE) {
387: graph = new RETERuleInfGraph(this , rules, schemaArg);
388: ((BasicForwardRuleInfGraph) graph).setTraceOn(traceOn);
389: } else if (mode == BACKWARD) {
390: graph = new LPBackwardRuleInfGraph(this , getBruleStore(),
391: data, schemaArg);
392: ((LPBackwardRuleInfGraph) graph).setTraceOn(traceOn);
393: } else {
394: List ruleSet = ((FBRuleInfGraph) schemaArg).getRules();
395: FBRuleInfGraph fbgraph = new FBRuleInfGraph(this , ruleSet,
396: schemaArg);
397: graph = fbgraph;
398: if (enableTGCCaching)
399: fbgraph.setUseTGCCache();
400: fbgraph.setTraceOn(traceOn);
401: fbgraph.setFunctorFiltering(filterFunctors);
402: if (preprocessorHooks != null) {
403: for (Iterator i = preprocessorHooks.iterator(); i
404: .hasNext();) {
405: fbgraph.addPreprocessingHook((RulePreprocessHook) i
406: .next());
407: }
408: }
409: }
410: graph.setDerivationLogging(recordDerivations);
411: graph.rebind(data);
412: return graph;
413: }
414:
415: /**
416: * Get the single static precomputed rule closure.
417: */
418: protected synchronized InfGraph getPreload() {
419: // We only support this in HYBRID mode
420: if (cachePreload && preload == null && mode == HYBRID) {
421: preload = new FBRuleInfGraph(this , rules, null, Factory
422: .createDefaultGraph());
423: if (enableTGCCaching)
424: ((FBRuleInfGraph) preload).setUseTGCCache();
425: preload.prepare();
426: }
427: return preload;
428: }
429:
430: /**
431: * Return the prepared backward only rules.
432: */
433: protected LPRuleStore getBruleStore() {
434: if (bRuleStore == null) {
435: bRuleStore = new LPRuleStore(rules);
436: }
437: return bRuleStore;
438: }
439:
440: // =======================================================================
441: // Inner classes
442:
443: /**
444: * Class used as an enum for describing rule modes.
445: */
446: public static class RuleMode {
447: /** Name for the mode */
448: String name;
449:
450: /** Constructor */
451: protected RuleMode(String name) {
452: this .name = name;
453: }
454:
455: public String toString() {
456: return name;
457: }
458: }
459:
460: }
461:
462: /*
463: (c) Copyright 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
464: All rights reserved.
465:
466: Redistribution and use in source and binary forms, with or without
467: modification, are permitted provided that the following conditions
468: are met:
469:
470: 1. Redistributions of source code must retain the above copyright
471: notice, this list of conditions and the following disclaimer.
472:
473: 2. Redistributions in binary form must reproduce the above copyright
474: notice, this list of conditions and the following disclaimer in the
475: documentation and/or other materials provided with the distribution.
476:
477: 3. The name of the author may not be used to endorse or promote products
478: derived from this software without specific prior written permission.
479:
480: THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
481: IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
482: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
483: IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
484: INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
485: NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
486: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
487: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
488: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
489: THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
490: */
|