KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > hp > hpl > jena > reasoner > rulesys > impl > LPBRuleEngine


1 /******************************************************************
2  * File: LPBRuleEngine.java
3  * Created by: Dave Reynolds
4  * Created on: 21-Jul-2003
5  *
6  * (c) Copyright 2003, 2004, 2005 Hewlett-Packard Development Company, LP
7  * [See end of file]
8  * $Id: LPBRuleEngine.java,v 1.9 2005/04/11 15:53:10 der Exp $
9  *****************************************************************/

10 package com.hp.hpl.jena.reasoner.rulesys.impl;
11
12 import com.hp.hpl.jena.graph.*;
13 import com.hp.hpl.jena.reasoner.*;
14 import com.hp.hpl.jena.reasoner.rulesys.*;
15 import com.hp.hpl.jena.util.iterator.*;
16
17 import org.apache.commons.logging.Log;
18 import org.apache.commons.logging.LogFactory;
19 import java.util.*;
20
21 /**
22  * LP version of the core backward chaining engine. For each parent inference
23  * graph (whether pure backward or hybrid) there should be one LPBRuleEngine
24  * instance. The shared instance holds any common result caching, rule store
25  * and global state data. However, all the processing is done by instances
26  * of the LPInterpreter - one per query.
27  *
28  * @author <a HREF="mailto:der@hplb.hpl.hp.com">Dave Reynolds</a>
29  * @version $Revision: 1.9 $ on $Date: 2005/04/11 15:53:10 $
30  */

31 public class LPBRuleEngine {
32     
33 // =======================================================================
34
// variables
35

36     /** Store which holds the raw and compiled rules */
37     protected LPRuleStore ruleStore;
38     
39     /** The parent inference graph to which this engine is attached */
40     protected BackwardRuleInfGraphI infGraph;
41     
42     /** True if debug information should be written out */
43     protected boolean traceOn = false;
44     
45     /** Set to true to flag that derivations should be logged */
46     protected boolean recordDerivations;
47         
48     /** List of engine instances which are still processing queries */
49     protected List activeInterpreters = new ArrayList();
50     
51     /** Table mapping tabled goals to generators for those goals.
52      * This is here so that partial goal state can be shared across multiple queries. */

53     protected HashMap tabledGoals = new HashMap();
54     
55     /** Set of generators waiting to be run */
56     protected LinkedList agenda = new LinkedList();
57 // protected List agenda = new ArrayList();
58
// protected Collection agenda = new HashSet();
59

60     /** Optional profile of number of time each rule is entered, set to non-null to profile */
61     protected HashMap profile;
62     
63     /** The number of generator cycles to wait before running a completion check.
64      * If set to 0 then checks will be done in the generator each time. */

65     public static final int CYCLES_BETWEEN_COMPLETION_CHECK = 3;
66     
67     static Log logger = LogFactory.getLog(LPBRuleEngine.class);
68     
69 // =======================================================================
70
// Constructors
71

72     /**
73      * Constructor.
74      * @param infGraph the parent inference graph which is using this engine
75      * @param rules the indexed set of rules to process
76      */

77     public LPBRuleEngine(BackwardRuleInfGraphI infGraph, LPRuleStore rules) {
78         this.infGraph = infGraph;
79         ruleStore = rules;
80     }
81     
82     /**
83      * Constructor. Creates an empty engine to which rules must be added.
84      * @param infGraph the parent inference graph which is using this engine
85      */

86     public LPBRuleEngine(BackwardRuleInfGraphI infGraph) {
87         this.infGraph = infGraph;
88         ruleStore = new LPRuleStore();
89     }
90     
91 // =======================================================================
92
// Control methods
93

94     /**
95      * Start a new interpreter running to answer a query.
96      * @param goal the query to be processed
97      * @return a closable iterator over the query results
98      */

99     public synchronized ExtendedIterator find(TriplePattern goal) {
100         LPInterpreter interpreter = new LPInterpreter(this, goal);
101         activeInterpreters.add(interpreter);
102         return WrappedIterator.create( new LPTopGoalIterator(interpreter));
103     }
104     
105     /**
106      * Clear all tabled results.
107      */

108     public synchronized void reset() {
109         checkSafeToUpdate();
110         tabledGoals = new HashMap();
111         agenda.clear();
112     }
113     
114     /**
115      * Add a single rule to the store.
116      * N.B. This will invalidate current partial results and the engine
117      * should be reset() before future queries.
118      */

119     public synchronized void addRule(Rule rule) {
120         checkSafeToUpdate();
121         ruleStore.addRule(rule);
122     }
123     
124     /**
125      * Remove a single rule from the store.
126      * N.B. This will invalidate current partial results and the engine
127      * should be reset() before future queries.
128      */

129     public synchronized void deleteRule(Rule rule) {
130         checkSafeToUpdate();
131         ruleStore.deleteRule(rule);
132     }
133     
134     /**
135      * Return an ordered list of all registered rules.
136      */

137     public synchronized List getAllRules() {
138         checkSafeToUpdate();
139         return ruleStore.getAllRules();
140     }
141     
142     /**
143      * Delete all the rules.
144      */

145     public synchronized void deleteAllRules() {
146         checkSafeToUpdate();
147         ruleStore.deleteAllRules();
148     }
149     
150     /**
151      * Stop the current work. Forcibly stop all current query instances over this engine.
152      */

153     public synchronized void halt() {
154         ArrayList copy = new ArrayList(activeInterpreters);
155         // Copy because closing the LPInterpreter will detach it from this engine which affects activeInterpreters
156
for (Iterator i = copy.iterator(); i.hasNext(); ) {
157             ((LPInterpreter)i.next()).close();
158         }
159     }
160        
161     /**
162      * Set the state of the trace flag. If set to true then rule firings
163      * are logged out to the Log at "INFO" level.
164      */

165     public void setTraceOn(boolean state) {
166         traceOn = state;
167     }
168     
169     /**
170      * Return true if traces of rule firings should be logged.
171      */

172     public boolean isTraceOn() {
173         return traceOn;
174     }
175        
176     /**
177      * Set to true to enable derivation caching
178      */

179     public void setDerivationLogging(boolean recordDerivations) {
180         this.recordDerivations = recordDerivations;
181     }
182     
183     /**
184      * Return true in derivations should be logged.
185      */

186     public boolean getDerivationLogging() {
187         return recordDerivations;
188     }
189
190     /** Return the rule store associated with the inference graph */
191     public LPRuleStore getRuleStore() {
192         return ruleStore;
193     }
194     
195     /** Return the parent infernce graph associated with this engine */
196     public BackwardRuleInfGraphI getInfGraph() {
197         return infGraph;
198     }
199     
200     /** Detatch the given engine from the list of active engines for this inf graph */
201     public synchronized void detach(LPInterpreter engine) {
202         activeInterpreters.remove(engine);
203     }
204     
205     /**
206      * Check that there are no currently processing queries.
207      * Could throw an exception here but often this can be caused by simply leaving
208      * an unclosed iterator. So instead we try to close the iterators and assume the
209      * rest of the context will be reset by the add call.
210      *
211      * <p>Should be called from within a synchronized block.
212      */

213     public void checkSafeToUpdate() {
214         if (!activeInterpreters.isEmpty()) {
215             ArrayList toClose = new ArrayList();
216             for (Iterator i = activeInterpreters.iterator(); i.hasNext(); ) {
217                 LPInterpreter interpreter = (LPInterpreter)i.next();
218                 if (interpreter.getContext() instanceof LPTopGoalIterator) {
219                     toClose.add(interpreter.getContext());
220                 }
221             }
222             for (Iterator i = toClose.iterator(); i.hasNext(); ) {
223                 ((LPTopGoalIterator)i.next()).close();
224             }
225         }
226     }
227     
228     
229 // =======================================================================
230
// Interface for tabled operations
231

232     /**
233      * Register an RDF predicate as one whose presence in a goal should force
234      * the goal to be tabled.
235      */

236     public synchronized void tablePredicate(Node predicate) {
237         ruleStore.tablePredicate(predicate);
238     }
239     
240     /**
241      * Return a generator for the given goal (assumes that the caller knows that
242      * the goal should be tabled).
243      * @param goal the goal whose results are to be generated
244      * @param clauses the precomputed set of code blocks used to implement the goal
245      */

246     public synchronized Generator generatorFor(TriplePattern goal, List clauses) {
247         Generator generator = (Generator) tabledGoals.get(goal);
248         if (generator == null) {
249             LPInterpreter interpreter = new LPInterpreter(this, goal, clauses, false);
250             activeInterpreters.add(interpreter);
251             generator = new Generator(interpreter, goal);
252             schedule(generator);
253             tabledGoals.put(goal, generator);
254         }
255         return generator;
256     }
257         
258     /**
259      * Return a generator for the given goal (assumes that the caller knows that
260      * the goal should be tabled).
261      * @param goal the goal whose results are to be generated
262      */

263     public synchronized Generator generatorFor(TriplePattern goal) {
264         Generator generator = (Generator) tabledGoals.get(goal);
265         if (generator == null) {
266             LPInterpreter interpreter = new LPInterpreter(this, goal, false);
267             activeInterpreters.add(interpreter);
268             generator = new Generator(interpreter, goal);
269             schedule(generator);
270             tabledGoals.put(goal, generator);
271         }
272         return generator;
273     }
274     
275     /**
276      * Register that a generator or specific generator state (Consumer choice point)
277      * is now ready to run.
278      */

279     public void schedule(LPAgendaEntry state) {
280         agenda.add(state);
281     }
282     
283     /**
284      * Run the scheduled generators until the given generator is ready to run.
285      */

286     public synchronized void pump(LPInterpreterContext gen) {
287 // System.out.println("Pump agenda on engine " + this + ", size = " + agenda.size());
288
Collection batch = null;
289         if (CYCLES_BETWEEN_COMPLETION_CHECK > 0) {
290             batch = new ArrayList(CYCLES_BETWEEN_COMPLETION_CHECK);
291         }
292         int count = 0;
293         while(!gen.isReady()) {
294             if (agenda.isEmpty()) {
295 // System.out.println("Cycled " + this + ", " + count);
296
return;
297             }
298 // Iterator ai = agenda.iterator();
299
// LPAgendaEntry next = (LPAgendaEntry) ai.next();
300
// ai.remove();
301
int chosen = agenda.size() - 1;
302             LPAgendaEntry next = (LPAgendaEntry) agenda.get(chosen);
303             agenda.remove(chosen);
304 // System.out.println(" pumping entry " + next);
305
next.pump();
306             count ++;
307             if (CYCLES_BETWEEN_COMPLETION_CHECK > 0) {
308                 batch.add(next.getGenerator());
309                 if (count % CYCLES_BETWEEN_COMPLETION_CHECK == 0) {
310                     Generator.checkForCompletions(batch);
311                     batch.clear();
312                 }
313             }
314         }
315         if (CYCLES_BETWEEN_COMPLETION_CHECK > 0 && !batch.isEmpty()) {
316             Generator.checkForCompletions(batch);
317         }
318         
319 // System.out.println("Cycled " + this + ", " + count);
320
}
321      
322     /**
323      * Check all known interpeter contexts to see if any are complete.
324      */

325     public void checkForCompletions() {
326         ArrayList contexts = new ArrayList(activeInterpreters.size());
327         for (Iterator i = activeInterpreters.iterator(); i.hasNext(); ) {
328             LPInterpreter interpreter = (LPInterpreter)i.next();
329             if (interpreter.getContext() instanceof Generator) {
330                 contexts.add(interpreter.getContext());
331             }
332         }
333         Generator.checkForCompletions(contexts);
334     }
335     
336 // =======================================================================
337
// Profiling support
338

339     /**
340      * Record a rule invocation in the profile count.
341      */

342     public void incrementProfile(RuleClauseCode clause) {
343         if (profile != null) {
344             String JavaDoc index = clause.toString();
345             Count count = (Count)profile.get(index);
346             if (count == null) {
347                 profile.put(index, new Count(clause).inc());
348             } else {
349                 count.inc();
350             }
351         }
352     }
353     
354     /**
355      * Reset the profile.
356      * @param enable it true then profiling will continue with a new empty profile table,
357      * if false profiling will stop all current data lost.
358      */

359     public void resetProfile(boolean enable) {
360         profile = enable ? new HashMap() : null;
361     }
362     
363     /**
364      * Print a profile of rules used since the last reset.
365      */

366     public void printProfile() {
367         if (profile == null) {
368             System.out.println("No profile collected");
369         } else {
370             ArrayList counts = new ArrayList();
371             counts.addAll(profile.values());
372             Collections.sort(counts);
373             System.out.println("LP engine rule profile");
374             for (Iterator i = counts.iterator(); i.hasNext(); ) {
375                 System.out.println(i.next());
376             }
377         }
378     }
379     
380     /**
381      * Record count of number of rule invocations, used in profile structure only.
382      */

383     static class Count implements Comparable JavaDoc {
384         protected int count = 0;
385         protected RuleClauseCode clause;
386
387         /** Constructor */
388         public Count(RuleClauseCode clause) {
389             this.clause = clause;
390         }
391         
392         /** return the count value */
393         public int getCount() {
394             return count;
395         }
396         
397         /** increment the count value, return the count object */
398         public Count inc() {
399             count++;
400             return this;
401         }
402         
403         /** Ordering */
404         public int compareTo(Object JavaDoc other) {
405             Count otherCount = (Count) other;
406             return (count < otherCount.count) ? -1 : ( (count == otherCount.count) ? 0 : +1);
407         }
408         
409         /** Printable form */
410         public String JavaDoc toString() {
411             return " " + count + "\t - " + clause;
412         }
413         
414     }
415 }
416
417
418
419 /*
420     (c) Copyright 2003, 2004, 2005 Hewlett-Packard Development Company, LP
421     All rights reserved.
422
423     Redistribution and use in source and binary forms, with or without
424     modification, are permitted provided that the following conditions
425     are met:
426
427     1. Redistributions of source code must retain the above copyright
428        notice, this list of conditions and the following disclaimer.
429
430     2. Redistributions in binary form must reproduce the above copyright
431        notice, this list of conditions and the following disclaimer in the
432        documentation and/or other materials provided with the distribution.
433
434     3. The name of the author may not be used to endorse or promote products
435        derived from this software without specific prior written permission.
436
437     THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
438     IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
439     OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
440     IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
441     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
442     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
443     DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
444     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
445     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
446     THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
447 */
Popular Tags