| package org.apache.rya.reasoning; |
| |
| /* |
| * 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. |
| */ |
| |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| import org.eclipse.rdf4j.model.IRI; |
| import org.eclipse.rdf4j.model.Resource; |
| import org.eclipse.rdf4j.model.Value; |
| |
| /** |
| * Abstract class for reasoning in the neighborhood of a particular resource. |
| * Contains general methods for collecting facts and derivations, and |
| * determining whether those facts are potentially new. |
| * |
| * As reasoning rules are applied, facts and inconsistencies are collected and |
| * stored in this object. (This allows some simple logic to reduce redundant |
| * fact generation.) The caller can use getFacts and getInconsistencies to |
| * retrieve them when needed. (This clears the buffered facts in the reasoner |
| * object.) |
| */ |
| public abstract class AbstractReasoner { |
| /** |
| * Reasoning is done with respect to this particular node. |
| */ |
| protected final Resource node; |
| |
| /** |
| * All schema (TBox/RBox) data is accessed through this. |
| */ |
| protected final Schema schema; |
| |
| /** |
| * The current iteration in the overall reasoning algorithm. |
| */ |
| protected final int currentIteration; |
| |
| /** |
| * A newly derived fact should have at least one source which is at least |
| * this recent. |
| */ |
| final int minIteration; |
| |
| /** |
| * If the global schema has ever been updated during reasoning iteration, |
| * this will be the iteration number of that update (otherwise 0). |
| */ |
| final int lastSchemaChange; |
| |
| // Newly derived information |
| Set<Fact> newFacts = new HashSet<>(); |
| Set<Derivation> inconsistencies = new HashSet<>(); |
| |
| /** |
| * Constructor. |
| * @param node Conduct reasoning about/around this node |
| * @param schema Global schema (class/property) information |
| * @param t Iteration # of new triples |
| * @param tSchema Iteration # of latest schema update. If the schema has |
| * not been changed, we can ignore many triples that we |
| * expect to already have used. |
| */ |
| public AbstractReasoner(Resource node, Schema schema, int t, int tSchema) { |
| this.node = node; |
| this.schema = schema; |
| this.currentIteration = t; |
| this.lastSchemaChange = tSchema; |
| if (tSchema < (t - 1)) { |
| this.minIteration = 0; |
| } |
| else { |
| this.minIteration = t - 1; |
| } |
| } |
| |
| /** |
| * Store inconsistency if it really is new. |
| * @return Whether it really needed to be stored |
| */ |
| protected boolean collectInconsistency(Derivation inconsistency) { |
| // If at least one of its sources can indeed be used for derivations, |
| // store this fact |
| for (Fact source : inconsistency.getSources()) { |
| if (frontier(source)) { |
| return inconsistencies.add(inconsistency); |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Store fact if it really is new. |
| * @return Whether it really needed to be stored |
| */ |
| protected boolean collect(Fact fact) { |
| // If this fact was just generated, at least one of its sources can |
| // indeed be used for derivations, and the sources don't include it |
| // itself, store this fact |
| if (fact.getIteration() == currentIteration && !fact.isCycle()) { |
| Derivation d = fact.getDerivation(); |
| for (Fact source : d.getSources()) { |
| if (frontier(source)) { |
| return newFacts.add(fact); |
| } |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Whether any new facts have been derived and not yet retrieved. |
| */ |
| public boolean hasNewFacts() { |
| return !newFacts.isEmpty(); |
| } |
| |
| /** |
| * Whether any inconsistencies have been derived and not retrieved. |
| */ |
| public boolean hasInconsistencies() { |
| return !inconsistencies.isEmpty(); |
| } |
| |
| /** |
| * Return the latest facts and set current new results to empty. |
| */ |
| public Set<Fact> getFacts() { |
| Set<Fact> results = newFacts; |
| newFacts = new HashSet<Fact>(); |
| return results; |
| } |
| |
| /** |
| * Return the latest inconsistencies and set inconsistencies to empty. |
| */ |
| public Set<Derivation> getInconsistencies() { |
| Set<Derivation> results = inconsistencies; |
| inconsistencies = new HashSet<Derivation>(); |
| return results; |
| } |
| |
| /** |
| * Create a Fact representing a triple inferred by this reasoner. |
| * @param s The subject {@link Resource}. |
| * @param p The predicate {@link IRI}. |
| * @param o The object {@link Value}. |
| * @param rule The specific rule rule that yielded the inference |
| * @param source One (might be the only) fact used in the derivation |
| */ |
| protected Fact triple(Resource s, IRI p, Value o, OwlRule rule, |
| Fact source) { |
| Fact fact = new Fact(s, p, o, this.currentIteration, |
| rule, this.node); |
| fact.addSource(source); |
| return fact; |
| } |
| |
| /** |
| * Create a Derivation representing an inconsistency found by this reasoner. |
| * @param rule The specific rule rule that yielded the inconsistency |
| * @param source One (might be the only) fact used in the derivation |
| */ |
| protected Derivation inconsistency(OwlRule rule, Fact source) { |
| Derivation d = new Derivation(this.currentIteration, rule, this.node); |
| d.addSource(source); |
| return d; |
| } |
| |
| /** |
| * Determine whether a fact is on the frontier of knowledge so far, meaning |
| * it is new enough to imply further unknown information. If false, we can |
| * expect inferences based on this fact to have already been made in the |
| * step during which it was initially derived. Any interesting fact coming |
| * from this reasoner should have at least one of its direct sources on |
| * the frontier. Considers only age, not semantics. |
| * |
| * Three cases that indicate this is on the frontier: |
| * 1) We are looking at all the data (minIteration==0, either because this |
| * is the first pass through the data or because we reset on updating |
| * the schema). |
| * 2) This fact was generated (by this reasoner) during this iteration, so |
| * it hasn't been seen before. |
| * 3) It was generated by another node's reasoner since minIteration, |
| * meaning it hasn't been seen yet by a reasoner for this node. |
| * |
| * In any case, inconsistencies are not used to derive anything, so they |
| * always return false. |
| */ |
| protected boolean frontier(Fact fact) { |
| int t = fact.getIteration(); |
| Resource dNode = null; |
| if (fact.isInference()) { |
| dNode = fact.getDerivation().getNode(); |
| } |
| return (minIteration == 0) || (t == currentIteration) |
| || (t >= minIteration && !this.node.equals(dNode)); |
| } |
| |
| /** |
| * Get some summary information for logging/debugging. |
| */ |
| public String getDiagnostics() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(newFacts.size()).append(" new facts buffered"); |
| sb.append(inconsistencies.size()).append(" new inconsistencies buffered"); |
| return sb.toString(); |
| } |
| |
| /** |
| * Get the number of inputs cached. |
| */ |
| public int getNumStored() { return 0; } |
| |
| /** |
| * Get the node this reasoner reasons around. |
| */ |
| public Resource getNode() { return node; } |
| } |