001: /******************************************************************
002: * File: BasicForwardRuleInfGraph.java
003: * Created by: Dave Reynolds
004: * Created on: 30-Mar-03
005: *
006: * (c) Copyright 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
007: * [See end of file]
008: * $Id: BasicForwardRuleInfGraph.java,v 1.50 2008/01/02 12:07:47 andy_seaborne 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.shared.ReificationStyle;
014: import com.hp.hpl.jena.graph.*;
015:
016: import java.util.*;
017:
018: import com.hp.hpl.jena.util.OneToManyMap;
019: import com.hp.hpl.jena.util.iterator.*;
020:
021: import org.apache.commons.logging.Log;
022: import org.apache.commons.logging.LogFactory;
023:
024: /**
025: * An inference graph interface that runs a set of forward chaining
026: * rules to conclusion on each added triple and stores the entire
027: * result set.
028: * <p>
029: * This implementation has a horribly inefficient rule chainer built in.
030: * Once we have this working generalize this to an interface than
031: * can call out to a rule engine and build a real rule engine (e.g. Rete style). </p>
032: *
033: * @author <a href="mailto:der@hplb.hpl.hp.com">Dave Reynolds</a>
034: * @version $Revision: 1.50 $ on $Date: 2008/01/02 12:07:47 $
035: */
036: public class BasicForwardRuleInfGraph extends BaseInfGraph implements
037: ForwardRuleInfGraphI {
038:
039: //=======================================================================
040: // variables
041:
042: /** Table of derivation records, maps from triple to RuleDerivation */
043: protected OneToManyMap derivations;
044:
045: /** The set of deduced triples, this is in addition to base triples in the fdata graph */
046: protected FGraph fdeductions;
047:
048: /** Reference to any schema graph data bound into the parent reasoner */
049: protected Graph schemaGraph;
050:
051: /** The forward rule engine being used */
052: protected FRuleEngineI engine;
053:
054: /** The original rule set as supplied */
055: protected List rules;
056:
057: /** Flag which, if true, enables tracing of rule actions to logger.info */
058: protected boolean traceOn = false;
059:
060: protected static Log logger = LogFactory
061: .getLog(BasicForwardRuleInfGraph.class);
062:
063: //=======================================================================
064: // Core methods
065:
066: /**
067: * Constructor. Creates a new inference graph to which a (compiled) rule set
068: * and a data graph can be attached. This separation of binding is useful to allow
069: * any configuration parameters (such as logging) to be set before the data is added.
070: * Note that until the data is added using {@link #rebind rebind} then any operations
071: * like add, remove, find will result in errors.
072: *
073: * @param reasoner the parent reasoner
074: * @param schema the (optional) schema data which is being processed
075: */
076: public BasicForwardRuleInfGraph(Reasoner reasoner, Graph schema) {
077: super (null, reasoner);
078: instantiateRuleEngine(null);
079: this .schemaGraph = schema;
080: }
081:
082: /**
083: * Constructor. Creates a new inference graph based on the given rule set.
084: * No data graph is attached at this stage. This is to allow
085: * any configuration parameters (such as logging) to be set before the data is added.
086: * Note that until the data is added using {@link #rebind rebind} then any operations
087: * like add, remove, find will result in errors.
088: *
089: * @param reasoner the parent reasoner
090: * @param rules the list of rules to use this time
091: * @param schema the (optional) schema or preload data which is being processed
092: */
093: public BasicForwardRuleInfGraph(Reasoner reasoner, List rules,
094: Graph schema) {
095: this (reasoner, rules, schema, ReificationStyle.Minimal);
096: }
097:
098: public BasicForwardRuleInfGraph(Reasoner reasoner, List rules,
099: Graph schema, ReificationStyle style) {
100: super (null, reasoner, style);
101: instantiateRuleEngine(rules);
102: this .rules = rules;
103: this .schemaGraph = schema;
104: }
105:
106: /**
107: * Constructor. Creates a new inference graph based on the given rule set
108: * then processes the initial data graph. No precomputed deductions are loaded.
109: *
110: * @param reasoner the parent reasoner
111: * @param rules the list of rules to use this time
112: * @param schema the (optional) schema or preload data which is being processed
113: * @param data the data graph to be processed
114: */
115: public BasicForwardRuleInfGraph(Reasoner reasoner, List rules,
116: Graph schema, Graph data) {
117: this (reasoner, rules, schema);
118: rebind(data);
119: }
120:
121: /**
122: * Instantiate the forward rule engine to use.
123: * Subclasses can override this to switch to, say, a RETE imlementation.
124: * @param rules the rule set or null if there are not rules bound in yet.
125: */
126: protected void instantiateRuleEngine(List rules) {
127: if (rules != null) {
128: engine = new FRuleEngine(this , rules);
129: } else {
130: engine = new FRuleEngine(this );
131: }
132: }
133:
134: /**
135: * Attach a compiled rule set to this inference graph.
136: * @param ruleStore a compiled set of rules (i.e. the result of an FRuleEngine.compile).
137: */
138: public void setRuleStore(Object ruleStore) {
139: engine.setRuleStore(ruleStore);
140: }
141:
142: /**
143: * Replace the underlying data graph for this inference graph and start any
144: * inferences over again. This is primarily using in setting up ontology imports
145: * processing to allow an imports multiunion graph to be inserted between the
146: * inference graph and the raw data, before processing.
147: * @param data the new raw data graph
148: */
149: public void rebind(Graph data) {
150: fdata = new FGraph(data);
151: rebind();
152: }
153:
154: /**
155: * Cause the inference graph to reconsult the underlying graph to take
156: * into account changes. Normally changes are made through the InfGraph's add and
157: * remove calls are will be handled appropriately. However, in some cases changes
158: * are made "behind the InfGraph's back" and this forces a full reconsult of
159: * the changed data.
160: */
161: public void rebind() {
162: version++;
163: isPrepared = false;
164: }
165:
166: /**
167: * Return the schema graph, if any, bound into this inference graph.
168: */
169: public Graph getSchemaGraph() {
170: return schemaGraph;
171: }
172:
173: /**
174: * Perform any initial processing and caching. This call is optional. Most
175: * engines either have negligable set up work or will perform an implicit
176: * "prepare" if necessary. The call is provided for those occasions where
177: * substantial preparation work is possible (e.g. running a forward chaining
178: * rule system) and where an application might wish greater control over when
179: * this prepration is done.
180: */
181: public synchronized void prepare() {
182: if (isPrepared)
183: return;
184: isPrepared = true;
185: // initilize the deductions graph
186: fdeductions = new FGraph(createDeductionsGraph());
187: boolean rulesLoaded = false;
188: if (schemaGraph != null) {
189: rulesLoaded = preloadDeductions(schemaGraph);
190: }
191: if (rulesLoaded) {
192: engine.fastInit(fdata);
193: } else {
194: engine.init(true, fdata);
195: }
196: }
197:
198: /**
199: * Adds a set of precomputed triples to the deductions store. These do not, themselves,
200: * fire any rules but provide additional axioms that might enable future rule
201: * firing when real data is added. Used to implement bindSchema processing
202: * in the parent Reasoner.
203: * @return return true if the rule set has also been loaded
204: */
205: protected boolean preloadDeductions(Graph preloadIn) {
206: Graph d = fdeductions.getGraph();
207: BasicForwardRuleInfGraph preload = (BasicForwardRuleInfGraph) preloadIn;
208: // If the rule set is the same we can reuse those as well
209: if (preload.rules == rules) {
210: // Load raw deductions
211: for (Iterator i = preload.find(null, null, null); i
212: .hasNext();) {
213: d.add((Triple) i.next());
214: }
215: engine.setRuleStore(preload.engine.getRuleStore());
216: return true;
217: } else {
218: return false;
219: }
220: }
221:
222: /**
223: * Add a new deduction to the deductions graph.
224: */
225: public void addDeduction(Triple t) {
226: getDeductionsGraph().add(t);
227: }
228:
229: /**
230: * Extended find interface used in situations where the implementator
231: * may or may not be able to answer the complete query. It will
232: * attempt to answer the pattern but if its answers are not known
233: * to be complete then it will also pass the request on to the nested
234: * Finder to append more results.
235: * @param pattern a TriplePattern to be matched against the data
236: * @param continuation either a Finder or a normal Graph which
237: * will be asked for additional match results if the implementor
238: * may not have completely satisfied the query.
239: */
240: public ExtendedIterator findWithContinuation(TriplePattern pattern,
241: Finder continuation) {
242: checkOpen();
243: if (!isPrepared)
244: prepare();
245: ExtendedIterator result = null;
246: if (fdata == null) {
247: result = fdeductions.findWithContinuation(pattern,
248: continuation);
249: } else {
250: if (continuation == null) {
251: result = fdata.findWithContinuation(pattern,
252: fdeductions);
253: } else {
254: result = fdata.findWithContinuation(pattern, FinderUtil
255: .cascade(fdeductions, continuation));
256: }
257: }
258: return result.filterDrop(Functor.acceptFilter);
259: }
260:
261: /**
262: * Returns an iterator over Triples.
263: * This implementation assumes that the underlying findWithContinuation
264: * will have also consulted the raw data.
265: */
266: public ExtendedIterator graphBaseFind(Node subject, Node property,
267: Node object) {
268: return findWithContinuation(new TriplePattern(subject,
269: property, object), null);
270: }
271:
272: /**
273: * Basic pattern lookup interface.
274: * This implementation assumes that the underlying findWithContinuation
275: * will have also consulted the raw data.
276: * @param pattern a TriplePattern to be matched against the data
277: * @return a ExtendedIterator over all Triples in the data set
278: * that match the pattern
279: */
280: public ExtendedIterator find(TriplePattern pattern) {
281: return findWithContinuation(pattern, null);
282: }
283:
284: /**
285: * Add one triple to the data graph, run any rules triggered by
286: * the new data item, recursively adding any generated triples.
287: */
288: public synchronized void performAdd(Triple t) {
289: version++;
290: fdata.getGraph().add(t);
291: if (isPrepared) {
292: engine.add(t);
293: }
294: }
295:
296: /**
297: * Return the number of triples in the inferred graph
298: */
299: public int graphBaseSize() {
300: checkOpen();
301: if (!isPrepared) {
302: prepare();
303: }
304: int baseSize = fdata.getGraph().size();
305: int dedSize = fdeductions.getGraph().size();
306: // System.err.println( ">> BasicForwardRuleInfGraph::size = " + baseSize + "(base) + " + dedSize + "(deductions)" );
307: return baseSize + dedSize;
308: }
309:
310: /**
311: * Removes the triple t (if possible) from the set belonging to this graph.
312: */
313: public void performDelete(Triple t) {
314: version++;
315: if (fdata != null) {
316: Graph data = fdata.getGraph();
317: if (data != null) {
318: data.delete(t);
319: }
320: }
321: if (isPrepared) {
322: fdeductions.getGraph().delete(t);
323: }
324: }
325:
326: /**
327: * Free all resources, any further use of this Graph is an error.
328: */
329: public void close() {
330: if (!closed) {
331: engine = null;
332: fdeductions = null;
333: rules = null;
334: schemaGraph = null;
335: super .close();
336: }
337: }
338:
339: // =======================================================================
340: // Implementation of ForwardRuleInfGraphI interface which is used by
341: // the forward rule engine to invoke functions in this InfGraph
342:
343: /**
344: * Adds a new Backward rule as a rules of a forward rule process. Only some
345: * infgraphs support this.
346: */
347: public void addBRule(Rule brule) {
348: throw new ReasonerException(
349: "Forward reasoner does not support hybrid rules - "
350: + brule.toShortString());
351: }
352:
353: /**
354: * Deletes a new Backward rule as a rules of a forward rule process. Only some
355: * infgraphs support this.
356: */
357: public void deleteBRule(Rule brule) {
358: throw new ReasonerException(
359: "Forward reasoner does not support hybrid rules - "
360: + brule.toShortString());
361: }
362:
363: /**
364: * Return the Graph containing all the static deductions available so far.
365: */
366: public Graph getDeductionsGraph() {
367: prepare();
368: return fdeductions.getGraph();
369: }
370:
371: /**
372: * Create the graph used to hold the deductions. Can be overridden
373: * by subclasses that need special purpose graph implementations here.
374: * Assumes the graph underlying fdeductions can be reused if present.
375: */
376: protected Graph createDeductionsGraph() {
377: if (fdeductions != null) {
378: Graph dg = fdeductions.getGraph();
379: if (dg != null) {
380: // Reuse the old graph in order to preserve any listeners
381: dg.getBulkUpdateHandler().removeAll();
382: return dg;
383: }
384: }
385: return Factory.createGraphMem(style);
386: }
387:
388: /**
389: * Return the Graph containing all the static deductions available so far.
390: * Does not trigger a prepare action.
391: */
392: public Graph getCurrentDeductionsGraph() {
393: return fdeductions.getGraph();
394: }
395:
396: /**
397: * Search the combination of data and deductions graphs for the given triple pattern.
398: * This may different from the normal find operation in the base of hybrid reasoners
399: * where we are side-stepping the backward deduction step.
400: */
401: public ExtendedIterator findDataMatches(Node subject,
402: Node predicate, Node object) {
403: return find(subject, predicate, object);
404: }
405:
406: /**
407: * Log a dervivation record against the given triple.
408: */
409: public void logDerivation(Triple t, Object derivation) {
410: derivations.put(t, derivation);
411: }
412:
413: /**
414: * Assert a new triple in the deduction graph, bypassing any processing machinery.
415: */
416: public void silentAdd(Triple t) {
417: fdeductions.getGraph().add(t);
418: }
419:
420: //=======================================================================
421: // support for proof traces
422:
423: /**
424: * Set to true to enable derivation caching
425: */
426: public void setDerivationLogging(boolean recordDerivations) {
427: this .recordDerivations = recordDerivations;
428: engine.setDerivationLogging(recordDerivations);
429: if (recordDerivations) {
430: derivations = new OneToManyMap();
431: } else {
432: derivations = null;
433: }
434: }
435:
436: /**
437: * Return true if derivation logging is enabled.
438: */
439: public boolean shouldLogDerivations() {
440: return recordDerivations;
441: }
442:
443: /**
444: * Return the derivation of at triple.
445: * The derivation is a List of DerivationRecords
446: */
447: public Iterator getDerivation(Triple t) {
448: if (derivations == null) {
449: return new NullIterator();
450: } else {
451: return derivations.getAll(t);
452: }
453: }
454:
455: /**
456: * Set the state of the trace flag. If set to true then rule firings
457: * are logged out to the Log at "INFO" level.
458: */
459: public void setTraceOn(boolean state) {
460: traceOn = state;
461: }
462:
463: /**
464: * Return true if tracing should be acted on - i.e. if traceOn is true
465: * and we are past the bootstrap phase.
466: */
467: public boolean shouldTrace() {
468: return traceOn && engine.shouldTrace();
469: }
470:
471: /**
472: * Return the number of rules fired since this rule engine instance
473: * was created and initialized
474: */
475: public long getNRulesFired() {
476: return engine.getNRulesFired();
477: }
478:
479: public Reifier constructReifier() {
480: BasicFBReifier.GetReifier deductionsReifier = new BasicFBReifier.GetReifier() {
481: public Reifier getReifier() {
482: return getDeductionsGraph().getReifier();
483: }
484: };
485: return new BasicFBReifier(this , getRawGraph().getReifier(),
486: deductionsReifier, style);
487: }
488:
489: }
490:
491: /*
492: (c) Copyright 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
493: All rights reserved.
494:
495: Redistribution and use in source and binary forms, with or without
496: modification, are permitted provided that the following conditions
497: are met:
498:
499: 1. Redistributions of source code must retain the above copyright
500: notice, this list of conditions and the following disclaimer.
501:
502: 2. Redistributions in binary form must reproduce the above copyright
503: notice, this list of conditions and the following disclaimer in the
504: documentation and/or other materials provided with the distribution.
505:
506: 3. The name of the author may not be used to endorse or promote products
507: derived from this software without specific prior written permission.
508:
509: THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
510: IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
511: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
512: IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
513: INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
514: NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
515: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
516: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
517: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
518: THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
519: */
|