diff --git a/reasoner/src/main/java/eu/knowledge/engine/reasoner/api/TripleVarBinding.java b/reasoner/src/main/java/eu/knowledge/engine/reasoner/api/TripleVarBinding.java index f6d03c9ff..18c5f6f8a 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/api/TripleVarBinding.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/api/TripleVarBinding.java @@ -125,6 +125,25 @@ public boolean isConflicting(TripleVarBinding tvb) { return false; } + /** + * True if two Bindings have a different value for at least one variable. Note + * that it looks not at variable instances. + */ + public boolean isConflicting2(TripleVarBinding tvb, Set overlappingVars) { + + Node l, val; + for (Var e : overlappingVars) { + l = tvb.getVarValue(e); + + val = this.getVarValue(e); + + if (val != null && l != null && !val.sameValueAs(l)) { + return true; + } + } + return false; + } + /** * We assume all occurrences of a var have the same literal, we just return the * first one found. @@ -253,4 +272,8 @@ public boolean containsTriplePattern(TriplePattern value) { return false; } + public Set getVars() { + return this.variableTripleVarMapping.keySet(); + } + } diff --git a/reasoner/src/main/java/eu/knowledge/engine/reasoner/api/TripleVarBindingSet.java b/reasoner/src/main/java/eu/knowledge/engine/reasoner/api/TripleVarBindingSet.java index f671d6a43..90f4700fe 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/api/TripleVarBindingSet.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/api/TripleVarBindingSet.java @@ -23,18 +23,24 @@ public class TripleVarBindingSet { private Set graphPattern; private Set bindings; private Set tripleVarsCache; + private Set varsCache; private static final Logger LOG = LoggerFactory.getLogger(TripleVarBindingSet.class); public TripleVarBindingSet(Set aGraphPattern) { + this(aGraphPattern, 16); + } + + public TripleVarBindingSet(Set aGraphPattern, int initialCapacity) { + this.graphPattern = aGraphPattern; - bindings = ConcurrentHashMap.newKeySet(); + bindings = ConcurrentHashMap.newKeySet(initialCapacity); } public TripleVarBindingSet(Set aGraphPattern, BindingSet aBindingSet) { - this(aGraphPattern); + this(aGraphPattern, aBindingSet.size()); for (Binding b : aBindingSet) { this.add(new TripleVarBinding(this.graphPattern, b)); @@ -171,6 +177,15 @@ public TripleVarBindingSet merge(TripleVarBindingSet aBindingSet) { return gbs; } + public Set getVariables() { + if (this.varsCache == null) { + this.varsCache = new HashSet(); + for (TriplePattern tp : this.graphPattern) + this.varsCache.addAll(tp.getVariables()); + } + return this.varsCache; + } + /** * Special merge that only combines the current bindings with the given * bindings. It only adds bindings that are a combination of two input bindings @@ -187,17 +202,26 @@ public TripleVarBindingSet combine(TripleVarBindingSet aBindingSet) { LOG.trace("Merging {} bindings with our {} bindings.", aBindingSet.getBindings().size(), this.getBindings().size()); - TripleVarBindingSet gbs = new TripleVarBindingSet(this.graphPattern); - final int otherBindingSetSize = aBindingSet.getBindings().size(); final long totalCount = (long) otherBindingSetSize * (long) this.getBindings().size(); if (totalCount > LARGE_BS_SIZE) LOG.warn("Merging 2 large BindingSets ({} * {} = {}). This can take some time.", aBindingSet.getBindings().size(), this.getBindings().size(), totalCount); + TripleVarBindingSet gbs = new TripleVarBindingSet(this.graphPattern, (int) totalCount); if (this.bindings.isEmpty()) { gbs.addAll(aBindingSet.getBindings()); } else { + + Set overlappingVars = new HashSet(); + if (this.bindings.size() > 0 && aBindingSet.getBindings().size() > 0) { + Set vars1 = this.getVariables(); + Set vars2 = aBindingSet.getVariables(); + overlappingVars.addAll(vars1); + overlappingVars.retainAll(vars2); + LOG.trace("Overlapping vars found: {} - {} = {}", vars1, vars2, overlappingVars); + } + // Cartesian product is the base case AtomicLong progress = new AtomicLong(0); @@ -207,7 +231,7 @@ public TripleVarBindingSet combine(TripleVarBindingSet aBindingSet) { this.bindings.stream().parallel().forEach(tvb1 -> { for (TripleVarBinding otherB : aBindingSet.getBindings()) { // always add a merged version of the two bindings, except when they conflict. - if (!tvb1.isConflicting(otherB)) { + if (!tvb1.isConflicting2(otherB, overlappingVars)) { gbs.add(tvb1.merge(otherB)); } } @@ -221,7 +245,7 @@ public TripleVarBindingSet combine(TripleVarBindingSet aBindingSet) { } if (totalCount > LARGE_BS_SIZE) - LOG.debug("Merging large BindingSets done!"); + LOG.trace("Merging large BindingSets done!"); return gbs; } diff --git a/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulenode/BindingSetStore.java b/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulenode/BindingSetStore.java index ceaa76b1b..b03a6bf2b 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulenode/BindingSetStore.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulenode/BindingSetStore.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.concurrent.atomic.AtomicLong; import java.util.Set; import java.util.stream.Collectors; @@ -92,7 +93,7 @@ public boolean haveAllNeighborsContributedExcept(Set nodes) { } /** - * Translates the given TripleVarBindingSets from neighbors to a single + * Combine the given TripleVarBindingSets from neighbors to a single * TripleVarBindingSet using the given combi matches. Because the combi matches * are very specific to how they should be formed, using this information should * speed up the binding set merging process considerably. @@ -104,15 +105,16 @@ public boolean haveAllNeighborsContributedExcept(Set nodes) { * @return A TripleVarBindingSet that consists of only valid (according to the * combimatches) bindings. */ - private TripleVarBindingSet translateWithCombiMatches(Set aGraphPattern, + private TripleVarBindingSet combineWithCombiMatches(Set aGraphPattern, Set someCombiMatches, Map> someNeighborBS) { var combinedTVBS = new TripleVarBindingSet(aGraphPattern); - int i = 0; + AtomicLong i = new AtomicLong(0); int size = someCombiMatches.size(); - for (CombiMatch cMatch : someCombiMatches) { - i = i + 1; - LOG.trace("Creating binding set for combi match: {}/{}", i, size); + + someCombiMatches.stream().forEach(cMatch -> { + i.incrementAndGet(); + LOG.trace("Creating binding set for combi match: {}/{}", i.get(), size); // keep separate binding set per combi match var cMatchTVBS = new TripleVarBindingSet(aGraphPattern); @@ -128,7 +130,7 @@ private TripleVarBindingSet translateWithCombiMatches(Set aGraphP TripleVarBindingSet tvbs = matchToBS.get(cSingleMatch); if (tvbs != null) - cMatchTVBS.addAll(cMatchTVBS.combine(tvbs).getBindings()); + cMatchTVBS = cMatchTVBS.combine(tvbs); } } } @@ -136,7 +138,7 @@ private TripleVarBindingSet translateWithCombiMatches(Set aGraphP // addAll instead of merge, because different combi matches do not need to be // combined. combinedTVBS.addAll(cMatchTVBS.getBindings()); - } + }); return combinedTVBS; @@ -167,7 +169,7 @@ public TripleVarBindingSet get() { // at the consequent/antecedent neighbors depending on backward/forward // reasoning) we use the older (slower) method when there are no combi matches. if (this.combiMatches != null) - this.cache = this.translateWithCombiMatches(this.graphPattern, this.combiMatches, this.neighborBindingSet); + this.cache = this.combineWithCombiMatches(this.graphPattern, this.combiMatches, this.neighborBindingSet); else { LOG.trace("Ignoring combi matches for binding set construction."); TripleVarBindingSet combinedBS = new TripleVarBindingSet(graphPattern);