/*
* 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.*;
import org.apache.jena.graph.* ;
import org.apache.jena.reasoner.* ;
import org.apache.jena.reasoner.rulesys.impl.* ;
import org.apache.jena.util.OneToManyMap ;
import org.apache.jena.util.iterator.* ;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Inference graph for accessing the LP version of the backward chaining
* rule engine.
*/
public class LPBackwardRuleInfGraph extends BaseInfGraph implements BackwardRuleInfGraphI {
// =======================================================================
// variables
/** The LP rule engine controller which handles the processing for this graph */
protected LPBRuleEngine engine;
/** Table of derivation records, maps from triple to RuleDerivation */
protected OneToManyMap<Triple, Derivation> derivations;
/** An optional graph of separate schema assertions that should also be processed */
protected FGraph fschema;
/** Cache of deductions made from the rules */
protected FGraph fdeductions;
/** A finder that searches across the data, schema and axioms */
protected Finder dataFind;
/** Cache of temporary property values inferred through getTemp calls */
protected TempNodeCache tempNodecache;
static Logger logger = LoggerFactory.getLogger(LPBackwardRuleInfGraph.class);
// =======================================================================
// Core methods
/**
* Constructor. Create a new backward inference graph to process
* the given data. The parent reasoner supplies the ruleset and
* any additional schema graph.
*
* @param reasoner the parent reasoner
* @param ruleStore the indexed set of rules to use
* @param data the data graph to be processed
* @param schema optional precached schema (use null if not required)
*/
public LPBackwardRuleInfGraph(Reasoner reasoner, LPRuleStore ruleStore, Graph data, Graph schema) {
super(data, reasoner);
if (schema != null) {
fschema = new FGraph(schema);
}
engine = new LPBRuleEngine(this, ruleStore);
tempNodecache = new TempNodeCache(this);
}
/**
* Return the schema graph, if any, bound into this inference graph.
*/
@Override
public Graph getSchemaGraph() {
return fschema.getGraph();
}
/**
* 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;
fdeductions = new FGraph( Factory.createGraphMem() );
extractAxioms();
dataFind = fdata;
if (fdeductions != null) {
dataFind = FinderUtil.cascade(dataFind, fdeductions);
}
if (fschema != null) {
dataFind = FinderUtil.cascade(dataFind, fschema);
}
this.setPreparedState(true);
}
/**
* 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 synchronized void rebind(Graph data) {
engine.checkSafeToUpdate();
fdata = new FGraph(data);
this.setPreparedState(false);
}
/**
* 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 synchronized void rebind() {
version++;
engine.checkSafeToUpdate();
this.setPreparedState(false);
}
/**
* Flush out all cached results. Future queries have to start from scratch.
*/
@Override
public synchronized void reset() {
version++;
engine.checkSafeToUpdate();
engine.reset();
}
/**
* 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 synchronized ExtendedIterator<Triple> findWithContinuation(TriplePattern pattern, Finder continuation) {
checkOpen();
this.requirePrepared();
ExtendedIterator<Triple> result = engine.find(pattern).filterKeep( new UniqueFilter<Triple>());
if (continuation != null) {
result = result.andThen(continuation.find(pattern));
}
return result.filterDrop(Functor.acceptFilter);
}
/**
* Returns an iterator over Triples.
* This implementation assumes that the underlying findWithContinuation
* will have also consulted the raw data.
*/
@Override
public 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++;
engine.checkSafeToUpdate();
fdata.getGraph().add(t);
this.setPreparedState(false);
}
/**
* Removes the triple t (if possible) from the set belonging to this graph.
*/
@Override
public synchronized void performDelete(Triple t) {
version++;
engine.checkSafeToUpdate();
fdata.getGraph().delete(t);
this.setPreparedState(false);
}
/**
* Set a predicate to be tabled/memoized by the LP engine.
*/
public void setTabled(Node predicate) {
engine.tablePredicate(predicate);
if (isTraceOn()) {
logger.info("LP TABLE " + predicate);
}
}
// =======================================================================
// support for proof traces
/**
* Set to true to enable derivation caching
*/
@Override
public void setDerivationLogging(boolean recordDerivations) {
engine.setDerivationLogging(recordDerivations);
if (recordDerivations) {
derivations = new OneToManyMap<>();
} else {
derivations = null;
}
}
/**
* 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) {
engine.setTraceOn(state);
}
/**
* Return true if tracing is switched on
*/
public boolean isTraceOn() {
return engine.isTraceOn();
}
// =======================================================================
// Interface between infGraph and the goal processing machinery
/**
* Log a dervivation record against the given triple.
*/
@Override
public void logDerivation(Triple t, Derivation derivation) {
derivations.put(t, derivation);
}
/**
* Match a pattern just against the stored data (raw data, schema,
* axioms) but no derivation.
*/
@Override
public ExtendedIterator<Triple> findDataMatches(TriplePattern pattern) {
return dataFind.find(pattern);
}
/**
* Process a call to a builtin predicate
* @param clause the Functor representing the call
* @param env the BindingEnvironment for this call
* @param rule the rule which is invoking this call
* @return true if the predicate succeeds
*/
@Override
public boolean processBuiltin(ClauseEntry clause, Rule rule, BindingEnvironment env) {
throw new ReasonerException("Internal error in FBLP rule engine, incorrect invocation of building in rule " + rule);
}
/**
* Assert a new triple in the deduction graph, bypassing any processing machinery.
*/
@Override
public void silentAdd(Triple t) {
fdeductions.getGraph().add(t);
}
/**
* Retrieve or create a bNode representing an inferred property value.
* @param instance the base instance node to which the property applies
* @param prop the property node whose value is being inferred
* @param pclass the (optional, can be null) class for the inferred value.
* @return the bNode representing the property value
*/
@Override
public Node getTemp(Node instance, Node prop, Node pclass) {
return tempNodecache.getTemp(instance, prop, pclass);
}
// =======================================================================
// Rule engine extras
/**
* Find any axioms (rules with no body) in the rule set and
* add those to the auxilliary graph to be included in searches.
*/
protected void extractAxioms() {
Graph axioms = fdeductions.getGraph();
BBRuleContext contextForBuiltins = null;
for ( Rule rule : engine.getRuleStore().getAllRules() )
{
if ( rule.bodyLength() == 0 )
{
// An axiom
for ( int j = 0; j < rule.headLength(); j++ )
{
ClauseEntry axiom = rule.getHeadElement( j );
if ( axiom instanceof TriplePattern )
{
axioms.add( ( (TriplePattern) axiom ).asTriple() );
}
else if ( axiom instanceof Functor )
{
if ( contextForBuiltins == null )
{
contextForBuiltins = new BBRuleContext( this );
}
Functor f = (Functor) axiom;
Builtin implementation = f.getImplementor();
if ( implementation == null )
{
throw new ReasonerException( "Attempted to invoke undefined functor: " + f );
}
Node[] args = f.getArgs();
contextForBuiltins.setEnv( new BindingVector( args ) );
contextForBuiltins.setRule( rule );
implementation.headAction( args, args.length, contextForBuiltins );
}
}
}
}
}
}