| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.jena.reasoner.rulesys; |
| |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.apache.jena.graph.Factory ; |
| import org.apache.jena.graph.Graph ; |
| import org.apache.jena.graph.Node ; |
| import org.apache.jena.graph.Triple ; |
| import org.apache.jena.reasoner.* ; |
| import org.apache.jena.reasoner.rulesys.impl.FRuleEngineI ; |
| import org.apache.jena.reasoner.rulesys.impl.FRuleEngineIFactory ; |
| import org.apache.jena.reasoner.rulesys.impl.SafeGraph ; |
| import org.apache.jena.util.OneToManyMap ; |
| import org.apache.jena.util.iterator.ExtendedIterator ; |
| import org.apache.jena.util.iterator.NullIterator ; |
| |
| /** |
| * An inference graph interface that runs a set of forward chaining |
| * rules to conclusion on each added triple and stores the entire |
| * result set. |
| * <p> |
| * This implementation has a horribly inefficient rule chainer built in. |
| * Once we have this working generalize this to an interface than |
| * can call out to a rule engine and build a real rule engine (e.g. Rete style). </p> |
| */ |
| |
| public class BasicForwardRuleInfGraph extends BaseInfGraph implements ForwardRuleInfGraphI { |
| |
| /** Table of derivation records, maps from triple to RuleDerivation */ |
| protected OneToManyMap<Triple, Derivation> derivations; |
| |
| /** The set of deduced triples, this is in addition to base triples in the fdata graph */ |
| protected FGraph fdeductions; |
| |
| /** A safe wrapped version of the deductions graph used for reporting getDeductions */ |
| protected Graph safeDeductions; |
| |
| /** Reference to any schema graph data bound into the parent reasoner */ |
| protected Graph schemaGraph; |
| |
| /** The forward rule engine being used */ |
| protected FRuleEngineI engine; |
| |
| /** The original rule set as supplied */ |
| private List<Rule> rules; |
| |
| /** Flag, if true then find results will be filtered to remove functors and illegal RDF */ |
| public boolean filterFunctors = true; |
| |
| /** Flag which, if true, enables tracing of rule actions to logger.info */ |
| protected boolean traceOn = false; |
| |
| // private static Logger logger = LoggerFactory.getLogger(BasicForwardRuleInfGraph.class); |
| |
| //======================================================================= |
| // Core methods |
| |
| /** |
| * Constructor. Creates a new inference graph to which a (compiled) rule set |
| * and a data graph can be attached. This separation of binding is useful to allow |
| * any configuration parameters (such as logging) to be set before the data is added. |
| * Note that until the data is added using {@link #rebind rebind} then any operations |
| * like add, remove, find will result in errors. |
| * |
| * @param reasoner the parent reasoner |
| * @param schema the (optional) schema data which is being processed |
| */ |
| public BasicForwardRuleInfGraph(Reasoner reasoner, Graph schema) { |
| super(null, reasoner); |
| instantiateRuleEngine(null); |
| this.schemaGraph = schema; |
| } |
| |
| /** |
| * Constructor. Creates a new inference graph based on the given rule set. |
| * No data graph is attached at this stage. This is to allow |
| * any configuration parameters (such as logging) to be set before the data is added. |
| * Note that until the data is added using {@link #rebind rebind} then any operations |
| * like add, remove, find will result in errors. |
| * |
| * @param reasoner the parent reasoner |
| * @param rules the list of rules to use this time |
| * @param schema the (optional) schema or preload data which is being processed |
| */ |
| public BasicForwardRuleInfGraph(Reasoner reasoner, List<Rule> rules, Graph schema) |
| { |
| super( null, reasoner ); |
| instantiateRuleEngine( rules ); |
| this.rules = rules; |
| this.schemaGraph = schema; |
| } |
| |
| /** |
| * Constructor. Creates a new inference graph based on the given rule set |
| * then processes the initial data graph. No precomputed deductions are loaded. |
| * |
| * @param reasoner the parent reasoner |
| * @param rules the list of rules to use this time |
| * @param schema the (optional) schema or preload data which is being processed |
| * @param data the data graph to be processed |
| */ |
| public BasicForwardRuleInfGraph(Reasoner reasoner, List<Rule> rules, Graph schema, Graph data) { |
| this(reasoner, rules, schema); |
| rebind(data); |
| } |
| |
| /** |
| * Instantiate the forward rule engine to use. |
| * Subclasses can override this to switch to, say, a RETE imlementation. |
| * @param rules the rule set or null if there are not rules bound in yet. |
| */ |
| protected void instantiateRuleEngine(List<Rule> rules) { |
| engine = FRuleEngineIFactory.getInstance().createFRuleEngineI(this, rules, false); |
| } |
| |
| /** |
| * Attach a compiled rule set to this inference graph. |
| * @param ruleStore a compiled set of rules (i.e. the result of an FRuleEngine.compile). |
| */ |
| public void setRuleStore(Object ruleStore) { |
| engine.setRuleStore(ruleStore); |
| } |
| |
| /** |
| * Replace the underlying data graph for this inference graph and start any |
| * inferences over again. This is primarily using in setting up ontology imports |
| * processing to allow an imports multiunion graph to be inserted between the |
| * inference graph and the raw data, before processing. |
| * @param data the new raw data graph |
| */ |
| @Override |
| public void rebind(Graph data) { |
| fdata = new FGraph( data ); |
| rebind(); |
| } |
| |
| /** |
| * Cause the inference graph to reconsult the underlying graph to take |
| * into account changes. Normally changes are made through the InfGraph's add and |
| * remove calls are will be handled appropriately. However, in some cases changes |
| * are made "behind the InfGraph's back" and this forces a full reconsult of |
| * the changed data. |
| */ |
| @Override |
| public void rebind() { |
| version++; |
| this.setPreparedState(false); |
| } |
| |
| /** |
| * Return the schema graph, if any, bound into this inference graph. |
| */ |
| @Override |
| public Graph getSchemaGraph() { |
| return schemaGraph; |
| } |
| |
| /** |
| * Perform any initial processing and caching. This call is optional. Most |
| * engines either have negligable set up work or will perform an implicit |
| * "prepare" if necessary. The call is provided for those occasions where |
| * substantial preparation work is possible (e.g. running a forward chaining |
| * rule system) and where an application might wish greater control over when |
| * this prepration is done. |
| */ |
| @Override |
| public synchronized void prepare() { |
| if (this.isPrepared()) return; |
| this.setPreparedState(true); |
| |
| // initilize the deductions graph |
| fdeductions = new FGraph( createDeductionsGraph() ); |
| boolean rulesLoaded = false; |
| if (schemaGraph != null) { |
| rulesLoaded = preloadDeductions(schemaGraph); |
| } |
| if (rulesLoaded) { |
| engine.fastInit(fdata); |
| } else { |
| engine.init(true, fdata); |
| } |
| } |
| |
| /** |
| * Adds a set of precomputed triples to the deductions store. These do not, themselves, |
| * fire any rules but provide additional axioms that might enable future rule |
| * firing when real data is added. Used to implement bindSchema processing |
| * in the parent Reasoner. |
| * @return return true if the rule set has also been loaded |
| */ |
| protected boolean preloadDeductions(Graph preloadIn) { |
| Graph d = fdeductions.getGraph(); |
| BasicForwardRuleInfGraph preload = (BasicForwardRuleInfGraph) preloadIn; |
| // If the rule set is the same we can reuse those as well |
| if (preload.rules == rules) { |
| // Load raw deductions |
| for (Iterator<Triple> i = preload.find(null, null, null); i.hasNext(); ) { |
| d.add(i.next()); |
| } |
| engine.setRuleStore(preload.engine.getRuleStore()); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| /** |
| * Add a new deduction to the deductions graph. |
| */ |
| @Override |
| public void addDeduction(Triple t) { |
| getDeductionsGraph().add(t); |
| } |
| |
| /** |
| * Set to true to cause functor-valued literals to be dropped from rule output. |
| * Default is true. |
| */ |
| @Override |
| public void setFunctorFiltering(boolean param) { |
| filterFunctors = param; |
| } |
| |
| /** |
| * Extended find interface used in situations where the implementator |
| * may or may not be able to answer the complete query. It will |
| * attempt to answer the pattern but if its answers are not known |
| * to be complete then it will also pass the request on to the nested |
| * Finder to append more results. |
| * @param pattern a TriplePattern to be matched against the data |
| * @param continuation either a Finder or a normal Graph which |
| * will be asked for additional match results if the implementor |
| * may not have completely satisfied the query. |
| */ |
| @Override |
| public ExtendedIterator<Triple> findWithContinuation(TriplePattern pattern, Finder continuation) { |
| return findWithContinuation(pattern, continuation, true); |
| } |
| |
| /** |
| * Internals of findWithContinuation implementation which allows control |
| * over functor filtering. |
| */ |
| private ExtendedIterator<Triple> findWithContinuation(TriplePattern pattern, Finder continuation, boolean filter) { |
| checkOpen(); |
| this.requirePrepared(); |
| ExtendedIterator<Triple> result = null; |
| if (fdata == null) { |
| result = fdeductions.findWithContinuation(pattern, continuation); |
| } else { |
| if (continuation == null) { |
| result = fdata.findWithContinuation(pattern, fdeductions); |
| } else { |
| result = fdata.findWithContinuation(pattern, FinderUtil.cascade(fdeductions, continuation) ); |
| } |
| } |
| if (filter && filterFunctors) { |
| return result.filterDrop(Functor.acceptFilter); |
| } else { |
| return result; |
| } |
| } |
| |
| /** |
| * Returns an iterator over Triples. |
| * This implementation assumes that the underlying findWithContinuation |
| * will have also consulted the raw data. |
| */ |
| @Override |
| protected ExtendedIterator<Triple> graphBaseFind(Node subject, Node property, Node object) { |
| return findWithContinuation(new TriplePattern(subject, property, object), null); |
| } |
| |
| /** |
| * Basic pattern lookup interface. |
| * This implementation assumes that the underlying findWithContinuation |
| * will have also consulted the raw data. |
| * @param pattern a TriplePattern to be matched against the data |
| * @return a ExtendedIterator over all Triples in the data set |
| * that match the pattern |
| */ |
| @Override |
| public ExtendedIterator<Triple> find(TriplePattern pattern) { |
| return findWithContinuation(pattern, null); |
| } |
| |
| |
| /** |
| * Add one triple to the data graph, run any rules triggered by |
| * the new data item, recursively adding any generated triples. |
| */ |
| @Override |
| public synchronized void performAdd(Triple t) { |
| version++; |
| fdata.getGraph().add(t); |
| if (this.isPrepared()) { |
| engine.add(t); |
| } |
| } |
| |
| /** |
| * Return the number of triples in the inferred graph |
| */ |
| @Override |
| public int graphBaseSize() { |
| checkOpen(); |
| this.requirePrepared(); |
| int baseSize = fdata.getGraph().size(); |
| int dedSize = fdeductions.getGraph().size(); |
| // System.err.println( ">> BasicForwardRuleInfGraph::size = " + baseSize + "(base) + " + dedSize + "(deductions)" ); |
| return baseSize + dedSize; |
| } |
| |
| /** |
| * Removes the triple t (if possible) from the set belonging to this graph. |
| */ |
| @Override |
| public void performDelete(Triple t) { |
| version++; |
| if (fdata != null) { |
| Graph data = fdata.getGraph(); |
| if (data != null) { |
| data.delete(t); |
| } |
| } |
| if (!this.isPrepared()) { |
| fdeductions.getGraph().delete(t); |
| } |
| } |
| |
| /** |
| * Free all resources, any further use of this Graph is an error. |
| */ |
| @Override |
| public void close() { |
| if (!closed) { |
| engine = null; |
| fdeductions = null; |
| rules = null; |
| schemaGraph = null; |
| super.close(); |
| } |
| } |
| |
| // ======================================================================= |
| // Implementation of ForwardRuleInfGraphI interface which is used by |
| // the forward rule engine to invoke functions in this InfGraph |
| |
| /** |
| * Adds a new Backward rule as a rules of a forward rule process. Only some |
| * infgraphs support this. |
| */ |
| @Override |
| public void addBRule(Rule brule) { |
| throw new ReasonerException("Forward reasoner does not support hybrid rules - " + brule.toShortString()); |
| } |
| |
| /** |
| * Deletes a new Backward rule as a rules of a forward rule process. Only some |
| * infgraphs support this. |
| */ |
| @Override |
| public void deleteBRule(Rule brule) { |
| throw new ReasonerException("Forward reasoner does not support hybrid rules - " + brule.toShortString()); |
| } |
| |
| /** |
| * Return the Graph containing all the static deductions available so far. |
| * Will force a prepare. |
| */ |
| @Override |
| public Graph getDeductionsGraph() { |
| prepare(); |
| return safeDeductions; |
| } |
| |
| /** |
| * Create the graph used to hold the deductions. Can be overridden |
| * by subclasses that need special purpose graph implementations here. |
| * Assumes the graph underlying fdeductions and associated SafeGraph |
| * wrapper can be reused if present thus enabling preservation of |
| * listeners. |
| */ |
| protected Graph createDeductionsGraph() { |
| if (fdeductions != null) { |
| Graph dg = fdeductions.getGraph(); |
| if (dg != null) { |
| // Reuse the old graph in order to preserve any listeners |
| safeDeductions.clear(); |
| return dg; |
| } |
| } |
| Graph dg = Factory.createGraphMem( ); |
| safeDeductions = new SafeGraph( dg ); |
| return dg; |
| } |
| |
| /** |
| * Return the Graph containing all the static deductions available so far. |
| * Does not trigger a prepare action. Returns a SafeWrapper and so |
| * can be used for update (thus triggering listeners) but not |
| * for access to generalized triples |
| */ |
| @Override |
| public Graph getCurrentDeductionsGraph() { |
| return safeDeductions; |
| // return fdeductions.getGraph(); |
| } |
| |
| /** |
| * Search the combination of data and deductions graphs for the given triple pattern. |
| * This may different from the normal find operation in the base of hybrid reasoners |
| * where we are side-stepping the backward deduction step. |
| */ |
| @Override |
| public ExtendedIterator<Triple> findDataMatches(Node subject, Node predicate, Node object) { |
| return findWithContinuation(new TriplePattern(subject, predicate, object), null, false); |
| } |
| |
| |
| /** |
| * Log a dervivation record against the given triple. |
| */ |
| @Override |
| public void logDerivation(Triple t, Derivation derivation) { |
| derivations.put(t, derivation); |
| } |
| |
| /** |
| * Assert a new triple in the deduction graph, bypassing any processing machinery. |
| */ |
| @Override |
| public void silentAdd(Triple t) { |
| fdeductions.getGraph().add(t); |
| } |
| |
| //======================================================================= |
| // support for proof traces |
| |
| /** |
| * Set to true to enable derivation caching |
| */ |
| @Override |
| public void setDerivationLogging(boolean recordDerivations) { |
| this.recordDerivations = recordDerivations; |
| engine.setDerivationLogging(recordDerivations); |
| if (recordDerivations) { |
| derivations = new OneToManyMap<>(); |
| } else { |
| derivations = null; |
| } |
| } |
| |
| /** |
| * Return true if derivation logging is enabled. |
| */ |
| @Override |
| public boolean shouldLogDerivations() { |
| return recordDerivations; |
| } |
| |
| /** |
| * Return the derivation of at triple. |
| * The derivation is a List of DerivationRecords |
| */ |
| @Override |
| public Iterator<Derivation> getDerivation(Triple t) { |
| if (derivations == null) { |
| return new NullIterator<>(); |
| } else { |
| return derivations.getAll(t); |
| } |
| } |
| |
| /** |
| * Set the state of the trace flag. If set to true then rule firings |
| * are logged out to the Log at "INFO" level. |
| */ |
| public void setTraceOn(boolean state) { |
| traceOn = state; |
| } |
| |
| /** |
| * Return true if tracing should be acted on - i.e. if traceOn is true |
| * and we are past the bootstrap phase. |
| */ |
| @Override |
| public boolean shouldTrace() { |
| return traceOn && engine.shouldTrace(); |
| } |
| |
| /** |
| * Return the number of rules fired since this rule engine instance |
| * was created and initialized |
| */ |
| public long getNRulesFired() { |
| return engine.getNRulesFired(); |
| } |
| |
| } |