diff --git a/reasoner/src/main/java/eu/knowledge/engine/reasoner/AntSide.java b/reasoner/src/main/java/eu/knowledge/engine/reasoner/AntSide.java index 9ece3ed60..4d59b694c 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/AntSide.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/AntSide.java @@ -3,6 +3,7 @@ import java.util.Map; import java.util.Set; +import eu.knowledge.engine.reasoner.BaseRule.CombiMatch; import eu.knowledge.engine.reasoner.api.TripleVarBindingSet; import eu.knowledge.engine.reasoner.rulenode.RuleNode; @@ -12,10 +13,14 @@ */ public interface AntSide { + public void setAntecedentCombiMatches(Set someCombiMatches); + + public Set getAntecedentCombiMatches(); + public void addAntecedentNeighbour(RuleNode neighbour, Set matches); public Map> getAntecedentNeighbours(); - public boolean addResultBindingSetInput(RuleNode aNeighbor, TripleVarBindingSet bs); + public boolean addResultBindingSetInput(RuleNode aNeighbor, Map someBindingSets); } diff --git a/reasoner/src/main/java/eu/knowledge/engine/reasoner/BaseRule.java b/reasoner/src/main/java/eu/knowledge/engine/reasoner/BaseRule.java index 07bec683d..edaea4230 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/BaseRule.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/BaseRule.java @@ -183,7 +183,9 @@ public CompletableFuture handle(BindingSet aBindingSet) { return null; } }); - Map> matches = getMatches(this, new HashSet<>(Arrays.asList(r)), false, aMatchConfig); + Set combiMatches = getMatches(this, new HashSet<>(Arrays.asList(r)), false, aMatchConfig); + + Map> matches = convertToMap(combiMatches); if (matches.containsKey(r)) return matches.get(r); @@ -194,7 +196,8 @@ public CompletableFuture handle(BindingSet aBindingSet) { public Set antecedentMatches(Set aConsequent, EnumSet aMatchConfig) { if (!this.antecedent.isEmpty()) { Rule r = new Rule(new HashSet<>(), aConsequent); - Map> matches = getMatches(this, new HashSet<>(Arrays.asList(r)), true, aMatchConfig); + Set combiMatches = getMatches(this, new HashSet<>(Arrays.asList(r)), true, aMatchConfig); + Map> matches = convertToMap(combiMatches); if (matches.containsKey(r)) return matches.get(r); } @@ -388,7 +391,7 @@ public static Map> getMatchesPerTriplePerRule(Set * the target rule * @return A set of matches that all contribute to some full matche. */ - public static Map> getMatches(BaseRule aTargetRule, Set someCandidateRules, + public static Set getMatches(BaseRule aTargetRule, Set someCandidateRules, boolean antecedentOfTarget, EnumSet config) { Set targetGP = antecedentOfTarget ? aTargetRule.getAntecedent() : aTargetRule.getConsequent(); @@ -409,7 +412,7 @@ public static Map> getMatches(BaseRule aTargetRule, Set(); + return new HashSet<>(); printCombiMatchesPerTriple(aTargetRule, combiMatchesPerTriple); @@ -526,7 +529,7 @@ public static Map> getMatches(BaseRule aTargetRule, Set(allMatches); } private static void printAllMatches(List allMatches) { @@ -597,7 +600,7 @@ private static CombiMatch mergeCombiMatches(CombiMatch candidateCombiMatch, Comb CombiMatch newCombiMatch = new CombiMatch(aBiggestCombiMatch); Set newBiggestMatch = new HashSet<>(biggestMatch); - // we merge it with one of the avaialble matches (does that work?) + // we merge it with one of the available matches (does that work?) for (Match m : biggestMatch) { newMatch = m.merge(candidateMatch); if (newMatch != null) { @@ -635,7 +638,7 @@ private static boolean doesSameCandidateTripleMapToDifferentTriple(Match candida for (Match biggestMatch : biggestMatches) { var biggestMatchMP = biggestMatch.getMatchingPatterns(); for (Map.Entry entry : biggestMatchMP.entrySet()) { - if (entry.getValue().equals(candidateMatchMP.getValue())) { + if (!entry.getValue().equals(candidateMatchMP.getValue())) { return true; } } @@ -644,7 +647,7 @@ private static boolean doesSameCandidateTripleMapToDifferentTriple(Match candida return false; } - private static Map> convertToMap(List combiMatches) { + private static Map> convertToMap(Set combiMatches) { Map> matchesPerRule = new HashMap<>(); for (CombiMatch combiMatch : combiMatches) { for (Map.Entry> ruleMatch : combiMatch.entrySet()) { @@ -670,13 +673,17 @@ private static Map> convertToMap(List combiMatc * matched to the same triple pattern. This is the case in transitivity * scenario's and we there support multiple match objects. */ - private static class CombiMatch extends HashMap> { + public static class CombiMatch extends HashMap> { private static final long serialVersionUID = 1L; public CombiMatch() { super(); } + public CombiMatch(Map> someRules) { + super(someRules); + } + /** * @param aMatch a combi match object to check whether it is a sub combi match * of this combi match. diff --git a/reasoner/src/main/java/eu/knowledge/engine/reasoner/ConsSide.java b/reasoner/src/main/java/eu/knowledge/engine/reasoner/ConsSide.java index 4b64b4d20..9159553ad 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/ConsSide.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/ConsSide.java @@ -3,6 +3,7 @@ import java.util.Map; import java.util.Set; +import eu.knowledge.engine.reasoner.BaseRule.CombiMatch; import eu.knowledge.engine.reasoner.api.TripleVarBindingSet; import eu.knowledge.engine.reasoner.rulenode.RuleNode; @@ -12,9 +13,13 @@ */ public interface ConsSide { + public void setConsequentCombiMatches(Set someMatches); + + public Set getConsequentCombiMatches(); + public void addConsequentNeighbour(RuleNode neighbour, Set matches); public Map> getConsequentNeighbours(); - - public boolean addFilterBindingSetInput(RuleNode aNeighbor, TripleVarBindingSet bs); + + public boolean addFilterBindingSetInput(RuleNode aNeighbor, Map someBindingSets); } diff --git a/reasoner/src/main/java/eu/knowledge/engine/reasoner/ReasonerPlan.java b/reasoner/src/main/java/eu/knowledge/engine/reasoner/ReasonerPlan.java index d4fcb64fe..fa6f7e99f 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/ReasonerPlan.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/ReasonerPlan.java @@ -6,12 +6,14 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import eu.knowledge.engine.reasoner.BaseRule.CombiMatch; import eu.knowledge.engine.reasoner.BaseRule.MatchFlag; import eu.knowledge.engine.reasoner.api.Binding; import eu.knowledge.engine.reasoner.api.BindingSet; @@ -57,6 +59,97 @@ public ReasonerPlan(RuleStore aStore, ProactiveRule aStartRule, EnumSet { + if (!(rule instanceof ProactiveRule)) { + assert currentRuleNode instanceof AntSide; + var newNode = createOrGetReasonerNode(rule, aRule); + assert newNode instanceof ConsSide; + ((AntSide) currentRuleNode).addAntecedentNeighbour(newNode, matches); + + var inverseMatches = Match.invertAll(matches); + ((ConsSide) newNode).addConsequentNeighbour(currentRuleNode, inverseMatches); + } else { + LOG.trace("Skipped proactive rule: {}", rule); + } + }); + + // store the combi matches when there are any antecedents. + if (!currentRuleNode.getRule().getAntecedent().isEmpty()) + ((AntSide) currentRuleNode) + .setAntecedentCombiMatches(this.store.getAntecedentCombiMatches(currentRuleNode.getRule())); + } else { + // interested in both consequent and antecedent neighbors + + this.store.getConsequentNeighbors(aRule, this.matchConfig).forEach((rule, matches) -> { + if (!(rule instanceof ProactiveRule)) { + assert currentRuleNode instanceof ConsSide; + var newNode = createOrGetReasonerNode(rule, aRule); + ((ConsSide) currentRuleNode).addConsequentNeighbour(newNode, matches); + + var inverseMatches = Match.invertAll(matches); + ((AntSide) newNode).addAntecedentNeighbour(currentRuleNode, inverseMatches); + } else { + LOG.trace("Skipped proactive rule: {}", rule); + } + }); + + // store the combi matches when there are any antecedents. + if (!currentRuleNode.getRule().getConsequent().isEmpty()) + ((ConsSide) currentRuleNode) + .setConsequentCombiMatches(this.store.getConsequentCombiMatches(currentRuleNode.getRule())); + + // antecedent neighbors to propagate bindings further via backward chaining + + // determine whether our parent matches us partially + boolean ourAntecedentFullyMatchesParentConsequent = false; + + Map> antecedentNeighbors = this.store.getAntecedentNeighbors(aRule, this.matchConfig); + if (aParent != null && antecedentNeighbors.containsKey(aParent)) { + ourAntecedentFullyMatchesParentConsequent = antecedentFullyMatchesConsequent(aRule, aParent, + antecedentNeighbors.get(aParent)); + } + + if (!ourAntecedentFullyMatchesParentConsequent) { + + antecedentNeighbors.forEach((rule, matches) -> { + if (!(rule instanceof ProactiveRule)) { + assert currentRuleNode instanceof AntSide; + var newNode = createOrGetReasonerNode(rule, aRule); + assert newNode instanceof ConsSide; + ((AntSide) currentRuleNode).addAntecedentNeighbour(newNode, matches); + + var inverseMatches = Match.invertAll(matches); + ((ConsSide) newNode).addConsequentNeighbour(currentRuleNode, inverseMatches); + } else { + LOG.trace("Skipped proactive rule: {}", rule); + } + }); + + if (!currentRuleNode.getRule().getAntecedent().isEmpty()) + ((AntSide) currentRuleNode) + .setAntecedentCombiMatches(this.store.getAntecedentCombiMatches(currentRuleNode.getRule())); + } + } + + return currentRuleNode; + } + /** * Enable (default) or disable the {@link TaskBoard}. When it is disabled, all * tasks will be executed as they occur in the algorithm, and an EMPTY task @@ -132,6 +225,7 @@ public TaskBoard execute(BindingSet bindingSet) { ((AntSide) current).getAntecedentNeighbours().forEach((n, matches) -> { var translated = toBeFilterPropagated.translate(n.getRule().getConsequent(), Match.invertAll(matches)); + boolean itChanged = ((ConsSide) n).addFilterBindingSetInput(current, translated); if (itChanged) { changed.add(n); @@ -216,84 +310,6 @@ public boolean isBackward() { return !this.start.getAntecedent().isEmpty() && this.start.getConsequent().isEmpty(); } - private RuleNode createOrGetReasonerNode(BaseRule aRule, BaseRule aParent) { - - final RuleNode reasonerNode; - if (this.ruleToRuleNode.containsKey(aRule)) - return this.ruleToRuleNode.get(aRule); - else { - reasonerNode = this.newNode(aRule); - } - - // build the reasoner node graph - this.ruleToRuleNode.put(aRule, reasonerNode); - - if (isBackward()) { - // for now we only are interested in antecedent neighbors. - // TODO for looping we DO want to consider consequent neighbors as well. - - this.store.getAntecedentNeighbors(aRule, this.matchConfig).forEach((rule, matches) -> { - if (!(rule instanceof ProactiveRule)) { - assert reasonerNode instanceof AntSide; - var newNode = createOrGetReasonerNode(rule, aRule); - assert newNode instanceof ConsSide; - ((AntSide) reasonerNode).addAntecedentNeighbour(newNode, matches); - - var inverseMatches = Match.invertAll(matches); - // TODO: Validate with Barry if we can use the same `matches` object here - ((ConsSide) newNode).addConsequentNeighbour(reasonerNode, inverseMatches); - } else { - LOG.trace("Skipped proactive rule: {}", rule); - } - }); - } else { - // interested in both consequent and antecedent neighbors - this.store.getConsequentNeighbors(aRule, this.matchConfig).forEach((rule, matches) -> { - if (!(rule instanceof ProactiveRule)) { - assert reasonerNode instanceof ConsSide; - var newNode = createOrGetReasonerNode(rule, aRule); - ((ConsSide) reasonerNode).addConsequentNeighbour(newNode, matches); - - var inverseMatches = Match.invertAll(matches); - // TODO: Validate with Barry if we can use the same `matches` object here - ((AntSide) newNode).addAntecedentNeighbour(reasonerNode, inverseMatches); - } else { - LOG.trace("Skipped proactive rule: {}", rule); - } - }); - - // antecedent neighbors to propagate bindings further via backward chaining - - // determine whether our parent matches us partially - boolean ourAntecedentFullyMatchesParentConsequent = false; - - Map> antecedentNeighbors = this.store.getAntecedentNeighbors(aRule, this.matchConfig); - if (aParent != null && antecedentNeighbors.containsKey(aParent)) { - ourAntecedentFullyMatchesParentConsequent = antecedentFullyMatchesConsequent(aRule, aParent, - antecedentNeighbors.get(aParent)); - } - - if (!ourAntecedentFullyMatchesParentConsequent) { - antecedentNeighbors.forEach((rule, matches) -> { - if (!(rule instanceof ProactiveRule)) { - assert reasonerNode instanceof AntSide; - var newNode = createOrGetReasonerNode(rule, aRule); - assert newNode instanceof ConsSide; - ((AntSide) reasonerNode).addAntecedentNeighbour(newNode, matches); - - // TODO: Validate with Barry if we can use the same `matches` object here - var inverseMatches = Match.invertAll(matches); - ((ConsSide) newNode).addConsequentNeighbour(reasonerNode, inverseMatches); - } else { - LOG.trace("Skipped proactive rule: {}", rule); - } - }); - } - } - - return reasonerNode; - } - private void scheduleOrDoTask(RuleNode current, TaskBoard taskBoard) { if (this.useTaskBoard) { taskBoard.addTask(current); 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 391dcad5d..039cb5e25 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 @@ -1,6 +1,7 @@ package eu.knowledge.engine.reasoner.api; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -18,7 +19,7 @@ public class TripleVarBindingSet { private static final int PROGRESS_MILESTONE_SIZE = 1000; - private static final int LARGE_BS_SIZE = 30000; + private static final int LARGE_BS_SIZE = 300000; private Set graphPattern; private Set bindings; private Set tripleVarsCache; @@ -156,10 +157,30 @@ public String toString() { * Simply the union between the two bindingsets. Does nothing complex for now. * * @param aGraphBindingSet - * @return + * @return a NEW (!) binding set with the merged binding sets. + * @apiNote Note that this method does not modify the original (this) binding + * set. */ public TripleVarBindingSet merge(TripleVarBindingSet aGraphBindingSet) { + TripleVarBindingSet gbs = combine(aGraphBindingSet); + gbs.addAll(aGraphBindingSet.getBindings()); + gbs.addAll(this.bindings); + + return gbs; + } + + /** + * Special merge that only combines the current bindings with the given + * bindings. It does not add the bindings separately. + * + * @param aGraphBindingSet + * @return a NEW (!) binding set with the merged binding sets. + * @apiNote Note that this method does not modify the original (this) binding + * set. + */ + public TripleVarBindingSet combine(TripleVarBindingSet aGraphBindingSet) { + LOG.trace("Merging {} bindings with our {} bindings.", aGraphBindingSet.getBindings().size(), this.getBindings().size()); @@ -172,13 +193,9 @@ public TripleVarBindingSet merge(TripleVarBindingSet aGraphBindingSet) { aGraphBindingSet.getBindings().size(), this.getBindings().size(), totalCount); if (this.bindings.isEmpty()) { - for (TripleVarBinding tvb2 : aGraphBindingSet.getBindings()) { - gbs.add(tvb2); - } + gbs.addAll(aGraphBindingSet.getBindings()); } else { // Cartesian product is the base case - gbs.addAll(aGraphBindingSet.getBindings()); - gbs.addAll(this.bindings); AtomicLong progress = new AtomicLong(0); final int milestoneSize = PROGRESS_MILESTONE_SIZE; @@ -213,7 +230,7 @@ public boolean isEmpty() { } /** - * Translate this bindingset using the given match. The variablenames will be + * Translate this bindingset using the given matches. The variable names will be * changed and variables not relevant in the match will be removed. * * The format of the mapping is expected to be translate , @@ -221,21 +238,31 @@ public boolean isEmpty() { * * It also filters bindings that are incompatible with the match. * - * @param match - * @return + * The resulting TripleVarBindingSets are stored per match to allow follow-up + * computation to be more efficient. + * + * @param someMatches The matches to use for this translation. + * @return A mapping from each match to a triplevarbindingset. */ - public TripleVarBindingSet translate(Set graphPattern, Set match) { - LOG.trace("Translating binding set with '{}' bindings and '{}' matches.", this.bindings.size(), match.size()); + public Map translate(Set aGraphPattern, Set someMatches) { + LOG.trace("Translating binding set with '{}' bindings and '{}' matches.", this.bindings.size(), + someMatches.size()); long start = System.currentTimeMillis(); - - TripleVarBindingSet newOne = new TripleVarBindingSet(graphPattern); + Map bsPerMatch = new HashMap<>(); TripleVarBinding toB; for (TripleVarBinding fromB : this.bindings) { - for (Match entry : match) { + for (Match aMatch : someMatches) { + + TripleVarBindingSet matchBS = bsPerMatch.get(aMatch); + if (matchBS == null) { + matchBS = new TripleVarBindingSet(aGraphPattern); + bsPerMatch.put(aMatch, matchBS); + } + boolean skip = false; toB = new TripleVarBinding(); - for (Map.Entry keyValue : entry.getMatchingPatterns().entrySet()) { + for (Map.Entry keyValue : aMatch.getMatchingPatterns().entrySet()) { TriplePattern fromTriple = keyValue.getKey(); TriplePattern toTriple = keyValue.getValue(); Map mapping = fromTriple.findMatches(toTriple); @@ -267,20 +294,19 @@ public TripleVarBindingSet translate(Set graphPattern, Set if (toB.containsVar((Var) toTVar.node) && !toB.getVarValue((Var) toTVar.node).equals(fromTNode.node)) { skip = true; - } else if (!toB.containsVar((Var) toTVar.node)) { + } else toB.put(toTVar, fromTNode.node); - } } } } if (!skip) - newOne.add(toB); + matchBS.add(toB); } } LOG.trace("Translated binding set with '{}' bindings and '{}' matches in '{}ms'.", this.bindings.size(), - match.size(), System.currentTimeMillis() - start); - return newOne; + someMatches.size(), System.currentTimeMillis() - start); + return bsPerMatch; } @@ -293,7 +319,7 @@ public void addAll(Set permutatedTVBs) { * the bindings in the given {@code bindingSet}. * * @param bindingSet - * @return + * @return A new binding set that only contains the compatible bindings. */ public TripleVarBindingSet keepCompatible(TripleVarBindingSet bindingSet) { diff --git a/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulenode/AntRuleNode.java b/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulenode/AntRuleNode.java index 9575fbf9c..9caf4a89e 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulenode/AntRuleNode.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulenode/AntRuleNode.java @@ -4,9 +4,11 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import eu.knowledge.engine.reasoner.AntSide; import eu.knowledge.engine.reasoner.BaseRule; +import eu.knowledge.engine.reasoner.BaseRule.CombiMatch; import eu.knowledge.engine.reasoner.Match; import eu.knowledge.engine.reasoner.api.TripleVarBindingSet; import eu.knowledge.engine.reasoner.rulestore.RuleStore; @@ -34,6 +36,21 @@ public AntRuleNode(BaseRule aRule) { */ private Map> antecedentNeighbours = new HashMap<>(); + private Set antecedentCombiMatches; + + @Override + public void setAntecedentCombiMatches(Set someCombiMatches) { + this.antecedentCombiMatches = someCombiMatches; + + // also set the combi matches to the rule store. + this.resultBindingSetInput.setCombiMatches(this.antecedentCombiMatches); + } + + @Override + public Set getAntecedentCombiMatches() { + return this.antecedentCombiMatches; + } + @Override public void addAntecedentNeighbour(RuleNode neighbour, Set matches) { this.antecedentNeighbours.put(neighbour, matches); @@ -50,11 +67,14 @@ public Set getAllNeighbours() { } @Override - public boolean addResultBindingSetInput(RuleNode aNeighbor, TripleVarBindingSet aBindingSet) { + public boolean addResultBindingSetInput(RuleNode aNeighbor, Map someBindingSets) { assert (antecedentNeighbours.keySet().contains(aNeighbor)); - TripleVarBindingSet filteredBS = aBindingSet; + Map filteredBS = someBindingSets; if (this.filterBindingSetOutput != null && !this.hasProactiveParent(aNeighbor)) { - filteredBS = aBindingSet.keepCompatible(this.filterBindingSetOutput); + + for (Map.Entry matchBS : filteredBS.entrySet()) { + filteredBS.put(matchBS.getKey(), matchBS.getValue().keepCompatible(this.filterBindingSetOutput)); + } } var changed = this.resultBindingSetInput.add(aNeighbor, filteredBS); 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 9a0cabbde..dd5d5c983 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 @@ -5,11 +5,15 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; +import java.util.stream.Collectors; import org.apache.jena.graph.Node; import eu.knowledge.engine.reasoner.BaseRule; +import eu.knowledge.engine.reasoner.BaseRule.CombiMatch; +import eu.knowledge.engine.reasoner.Match; import eu.knowledge.engine.reasoner.api.TripleNode; import eu.knowledge.engine.reasoner.api.TriplePattern; import eu.knowledge.engine.reasoner.api.TripleVarBinding; @@ -26,7 +30,13 @@ public class BindingSetStore { private final Set neighbors; - private final Map neighborBindingSet = new HashMap<>(); + private final Map> neighborBindingSet = new HashMap<>(); + + /** + * This combi matches set need to be initialized after the + * constructor has been called. + */ + private Set combiMatches = null; private final Set graphPattern; /** @@ -43,19 +53,20 @@ public BindingSetStore(Set aGraphPattern, Set someNeigh * Register the given {@code aBindingSet} to this BindingSetStore as coming from * neighbor {@code aNeighbor} * - * @param aNeighbor the neighbor who brought this bindingset. - * @param aBindingSet the bindingset brought by this neighbor. + * @param aNeighbor the neighbor who brought this bindingset. + * @param someBindingSets the bindingset brought by this neighbor. * @return whether adding this bindingset changed anything. I.e. this particular * neighbor did not yet give a bindingset or this bindingset is * different from the previous one. */ - public boolean add(RuleNode aNeighbor, TripleVarBindingSet aBindingSet) { - assert aBindingSet != null; + public boolean add(RuleNode aNeighbor, Map someBindingSets) { + assert someBindingSets != null; assert neighbors.contains(aNeighbor); - TripleVarBindingSet previousBindingSet = this.neighborBindingSet.put(aNeighbor, aBindingSet); + Map previousBindingSet = this.neighborBindingSet.put(aNeighbor.getRule(), + someBindingSets); - boolean changed = previousBindingSet == null || !previousBindingSet.equals(aBindingSet); + boolean changed = previousBindingSet == null || !previousBindingSet.equals(someBindingSets); if (changed) this.cache = null; @@ -64,17 +75,78 @@ public boolean add(RuleNode aNeighbor, TripleVarBindingSet aBindingSet) { } public boolean haveAllNeighborsContributed() { - return this.neighborBindingSet.keySet().containsAll(neighbors); + return this.neighborBindingSet.keySet() + .containsAll(neighbors.stream().map(x -> x.getRule()).collect(Collectors.toSet())); } // TODO: It feels ugly to do this. public boolean haveAllNeighborsContributedExcept(Set nodes) { - var allNeighboursWithException = new HashSet<>(); - allNeighboursWithException.addAll(neighbors); - allNeighboursWithException.removeAll(nodes); + var allNeighboursWithException = new HashSet(); + allNeighboursWithException.addAll(neighbors.stream().map(x -> x.getRule()).collect(Collectors.toSet())); + allNeighboursWithException.removeAll(nodes.stream().map(x -> x.getRule()).collect(Collectors.toSet())); return this.neighborBindingSet.keySet().containsAll(allNeighboursWithException); } + /** + * Translates 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. In the previous (old) + * version, the binding sets were all being merge in every possible way to make + * sure all possible bindings were present (also for cases like transitivity). + * But by using the combi matches this should not be necessary anymore! + * + * @param aGraphPattern The graph pattern of the binding set that is created. + * @param someCombiMatches The CombiMatches of the node. + * @param someNeighborBS The already translated binding sets from all + * neighbors of a node per match. + * @return A TripleVarBindingSet that consists of only valid (according to the + * combimatches) bindings. + */ + private TripleVarBindingSet translateWithCombiMatches(Set aGraphPattern, + Set someCombiMatches, Map> someNeighborBS) { + var combinedTVBS = new TripleVarBindingSet(aGraphPattern); + + for (CombiMatch cMatch : someCombiMatches) { + // keep separate binding set per combi match + var cMatchTVBS = new TripleVarBindingSet(aGraphPattern); + + for (Entry> cEntry : cMatch.entrySet()) { + + BaseRule aNeighborRule = cEntry.getKey(); + + Map matchToBS = someNeighborBS.get(aNeighborRule); + + if (matchToBS != null) { + for (Match cSingleMatch : cEntry.getValue()) { + TripleVarBindingSet tvbs = matchToBS.get(cSingleMatch); + + if (tvbs != null) + cMatchTVBS.addAll(cMatchTVBS.combine(tvbs).getBindings()); + } + } + } + + // addAll instead of merge, because different combi matches do not need to be + // combined. + combinedTVBS.addAll(cMatchTVBS.getBindings()); + } + + return combinedTVBS; + + } + + /** + * Sets the combi matches for this binding set store. Combi matches contain a + * combination of matches with neighboring rules that together form a single + * match for the rule node of which this bindingset store is parts. + * + * @param someCombiMatches The combi matches for this store. + */ + public void setCombiMatches(Set someCombiMatches) { + this.combiMatches = someCombiMatches; + } + /** * @return the bindingset with the combined bindingset of all neighbors. */ @@ -83,20 +155,27 @@ public TripleVarBindingSet get() { if (this.cache != null) { return this.cache; } - // TODO: Can a similar assertion be made? (Changed the class so that the - // result is also gettable when all neighbours except a specific one - // contributed) - // assert haveAllNeighborsContributed(); - - TripleVarBindingSet combinedBS = new TripleVarBindingSet(graphPattern); - for (TripleVarBindingSet bs : this.neighborBindingSet.values()) { - combinedBS = combinedBS.merge(bs); + + // unfortunately, due to the asymmetry introduced with combi matches (i.e. combi + // matches are only available at the antecedent/consequent neighbours, but not + // 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); + else { + TripleVarBindingSet combinedBS = new TripleVarBindingSet(graphPattern); + + for (TripleVarBindingSet bs : this.neighborBindingSet.values().stream().map(x -> x.values()) + .flatMap(x -> x.stream()).collect(Collectors.toSet())) { + combinedBS = combinedBS.merge(bs); + } + + // NOTE: we merge the bindings with themselves here (when the bindings + // 'leave' the store), but it may be better to do it when they enter the + // store, or when they get translated/matched. + this.cache = combinedBS.merge(combinedBS); } - // NOTE: we merge the bindings with themselves here (when the bindings - // 'leave' the store), but it may be better to do it when they enter the - // store, or when they get translated/matched. - this.cache = combinedBS.merge(combinedBS); return this.cache; } @@ -116,21 +195,22 @@ private String getName(BaseRule r) { public void printDebuggingTable() { StringBuilder table = new StringBuilder(); - List allNeighbors = new ArrayList<>(neighbors); + List allNeighbors = new ArrayList<>( + neighbors.stream().map(x -> x.getRule()).collect(Collectors.toSet())); // header row int count = 0; table.append("| Graph Pattern |"); count++; - for (RuleNode neighbor : allNeighbors) { + for (BaseRule neighbor : allNeighbors) { if (this.neighborBindingSet.containsKey(neighbor)) { - for (int i = 0; i < this.neighborBindingSet.get(neighbor).getBindings().size(); i++) { - table.append(this.getName(neighbor.getRule()) + "-" + i).append(" | "); + for (int i = 0; i < combine(this.neighborBindingSet.get(neighbor)).getBindings().size(); i++) { + table.append(this.getName(neighbor) + "-" + i).append(" | "); count++; } } else { - table.append(this.getName(neighbor.getRule())).append(" | "); + table.append(this.getName(neighbor)).append(" | "); count++; } } @@ -150,9 +230,9 @@ public void printDebuggingTable() { table.append(" | ").append(tp.toString()).append(" | "); // bindings - for (RuleNode neigh : allNeighbors) { + for (BaseRule neigh : allNeighbors) { - TripleVarBindingSet tvbs = this.neighborBindingSet.get(neigh); + TripleVarBindingSet tvbs = combine(this.neighborBindingSet.get(neigh)); if (tvbs != null) { for (TripleVarBinding tvb : tvbs.getBindings()) { @@ -201,4 +281,11 @@ private String formatNode(Node n) { return n.isVariable() ? before + TriplePattern.trunc(n) + after : TriplePattern.trunc(n); } + private TripleVarBindingSet combine(Map someBindingSets) { + var resultBS = new TripleVarBindingSet(this.graphPattern); + for (TripleVarBindingSet bs : someBindingSets.values()) { + resultBS.addAll(bs.getBindings()); + } + return resultBS; + } } diff --git a/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulenode/ConsRuleNode.java b/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulenode/ConsRuleNode.java index e8303a0c5..329902e1c 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulenode/ConsRuleNode.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulenode/ConsRuleNode.java @@ -4,8 +4,10 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import eu.knowledge.engine.reasoner.BaseRule; +import eu.knowledge.engine.reasoner.BaseRule.CombiMatch; import eu.knowledge.engine.reasoner.ConsSide; import eu.knowledge.engine.reasoner.Match; import eu.knowledge.engine.reasoner.api.TripleVarBindingSet; @@ -31,6 +33,21 @@ public ConsRuleNode(BaseRule aRule) { */ private Map> consequentNeighbours = new HashMap<>(); + private Set consequentCombiMatches; + + @Override + public void setConsequentCombiMatches(Set someMatches) { + this.consequentCombiMatches = someMatches; + + // also set the combi matches to the binding set store. + this.filterBindingSetInput.setCombiMatches(someMatches); + } + + @Override + public Set getConsequentCombiMatches() { + return this.consequentCombiMatches; + } + @Override public void addConsequentNeighbour(RuleNode neighbour, Set matches) { this.consequentNeighbours.put(neighbour, matches); @@ -47,9 +64,9 @@ public Set getAllNeighbours() { } @Override - public boolean addFilterBindingSetInput(RuleNode aNeighbor, TripleVarBindingSet bs) { + public boolean addFilterBindingSetInput(RuleNode aNeighbor, Map someBindingSets) { assert this.consequentNeighbours.containsKey(aNeighbor); - return this.filterBindingSetInput.add(aNeighbor, bs); + return this.filterBindingSetInput.add(aNeighbor, someBindingSets); } @Override diff --git a/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulenode/FullRuleNode.java b/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulenode/FullRuleNode.java index f1c2a6840..a19ac6501 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulenode/FullRuleNode.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulenode/FullRuleNode.java @@ -11,12 +11,12 @@ import java.util.concurrent.Future; import java.util.stream.Collectors; -import org.apache.jena.atlas.logging.Log; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import eu.knowledge.engine.reasoner.AntSide; import eu.knowledge.engine.reasoner.BaseRule; +import eu.knowledge.engine.reasoner.BaseRule.CombiMatch; import eu.knowledge.engine.reasoner.ConsSide; import eu.knowledge.engine.reasoner.Match; import eu.knowledge.engine.reasoner.Rule; @@ -44,6 +44,31 @@ public FullRuleNode(BaseRule aRule) { private Map> antecedentNeighbours = new HashMap<>(); private Map> consequentNeighbours = new HashMap<>(); + private Set antecedentCombiMatches; + private Set consequentCombiMatches; + + public void setAntecedentCombiMatches(Set someMatches) { + this.antecedentCombiMatches = someMatches; + + // also set the combi matches to the binding set store + this.resultBindingSetInput.setCombiMatches(someMatches); + } + + public Set getAntecedentCombiMatches() { + return this.antecedentCombiMatches; + } + + public void setConsequentCombiMatches(Set someMatches) { + this.consequentCombiMatches = someMatches; + + // also set the combi matches to the binding set store + this.filterBindingSetInput.setCombiMatches(someMatches); + } + + public Set getConsequentCombiMatches() { + return this.consequentCombiMatches; + } + @Override public void addConsequentNeighbour(RuleNode neighbour, Set matches) { this.consequentNeighbours.put(neighbour, matches); @@ -75,16 +100,20 @@ public Set getAllNeighbours() { } @Override - public boolean addFilterBindingSetInput(RuleNode aNeighbor, TripleVarBindingSet bs) { - return this.filterBindingSetInput.add(aNeighbor, bs); + public boolean addFilterBindingSetInput(RuleNode aNeighbor, Map someBindingSets) { + assert this.consequentNeighbours.containsKey(aNeighbor); + return this.filterBindingSetInput.add(aNeighbor, someBindingSets); } @Override - public boolean addResultBindingSetInput(RuleNode aNeighbor, TripleVarBindingSet bs) { + public boolean addResultBindingSetInput(RuleNode aNeighbor, Map someBindingSets) { - TripleVarBindingSet filteredBS = bs; + Map filteredBS = someBindingSets; if (this.filterBindingSetOutput != null && !this.hasProactiveParent(aNeighbor)) { - filteredBS = bs.keepCompatible(this.filterBindingSetOutput); + + for (Map.Entry matchBS : filteredBS.entrySet()) { + filteredBS.put(matchBS.getKey(), matchBS.getValue().keepCompatible(this.filterBindingSetOutput)); + } } var changed = this.resultBindingSetInput.add(aNeighbor, filteredBS); diff --git a/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulestore/MatchNode.java b/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulestore/MatchNode.java index bcf8e317f..e1002f634 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulestore/MatchNode.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulestore/MatchNode.java @@ -1,11 +1,12 @@ package eu.knowledge.engine.reasoner.rulestore; -import java.util.EnumSet; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Set; import eu.knowledge.engine.reasoner.BaseRule; +import eu.knowledge.engine.reasoner.BaseRule.CombiMatch; import eu.knowledge.engine.reasoner.BaseRule.MatchFlag; import eu.knowledge.engine.reasoner.Match; @@ -30,21 +31,47 @@ public class MatchNode { /** * All other rules in the {@link MatchNode#store} whose consequents match this - * rule's antecedent according to the {@link MatchStrategy#NORMAL_LEVEL} + * rule's antecedent according to the configured {@link MatchFlag} */ private Map> antecedentNeighbors; + /** + * The combi matches to antecedent neighbors. + * + * These might not get initialized, because we cannot initialize these + * symmetrically like the antencedentNeighbors mapping can be initialized. + * + * If this set remains null, it is not applicable. If it is empty, it is + * delibarately initalized with an empty list, because there are no combi + * matches on the antecedent side of this node.If it is non-empty, there are + * combi matches to one or more nodes on the consequent side of this node. + */ + private Set antecedentCombiMatches; + /** * All other rules in the {@link MatchNode#store} whose antecedents match this - * rule's consequent according to the {@link MatchStrategy#NORMAL_LEVEL} + * rule's consequent according to the {@link MatchFlag} */ private Map> consequentNeighbors; + /** + * The combi matches to consequent neighbors. + * + * Thesemightnot get initialize,because we cannot initialize these symmetrically + * like the consequentNeighbors mapping can be initialized. + * + * If this set remains null, it is not applicable. If it is empty, it is + * delibarately initalized with an empty list, because there are no combi + * matches on the consequent side of this node. If it is non-empty, there are + * combi matches to one or more nodes on the consequent side of this node. + * + */ + private Set consequentCombiMatches; + public MatchNode(BaseRule aRule) { this.rule = aRule; this.antecedentNeighbors = new HashMap<>(); this.consequentNeighbors = new HashMap<>(); - } public BaseRule getRule() { @@ -67,6 +94,34 @@ public void setAntecedentNeighbor(BaseRule aRule, Set someMatches) { } } + /** + * Set the combi matches for the consequent side of this node. + * + * @param someCombiMatches The combi matches for this node on the consequent + * side. + */ + public void setConsequentMatches(Set someCombiMatches) { + this.consequentCombiMatches = someCombiMatches; + } + + /** + * Set the combi matches for the antecedent side of this node. + * + * @param someCombiMatches The combi matches for this node on the antecedent + * side. + */ + public void setAntecedentCombiMatches(Set someCombiMatches) { + this.antecedentCombiMatches = someCombiMatches; + } + + public Set getConsequentCombiMatches() { + return this.consequentCombiMatches; + } + + public Set getAntecedentCombiMatches() { + return this.antecedentCombiMatches; + } + public Map> getConsequentNeighbors() { return this.consequentNeighbors; } @@ -83,7 +138,9 @@ public RuleStore getStore() { } public void reset() { + this.antecedentCombiMatches.clear(); this.antecedentNeighbors.clear(); + this.consequentCombiMatches.clear(); this.consequentNeighbors.clear(); } diff --git a/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulestore/RuleStore.java b/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulestore/RuleStore.java index 123116de0..ca02f18f4 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulestore/RuleStore.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulestore/RuleStore.java @@ -18,6 +18,7 @@ import org.slf4j.LoggerFactory; import eu.knowledge.engine.reasoner.BaseRule; +import eu.knowledge.engine.reasoner.BaseRule.CombiMatch; import eu.knowledge.engine.reasoner.BaseRule.MatchFlag; import eu.knowledge.engine.reasoner.Match; import eu.knowledge.engine.reasoner.ProactiveRule; @@ -41,18 +42,18 @@ public class RuleStore { /** * All the rules in this store. */ - private Map ruleToRuleNode; + private Map ruleToMatchNode; /** * Instantiate an empty rule store. */ public RuleStore() { - ruleToRuleNode = new HashMap<>(); + ruleToMatchNode = new HashMap<>(); } public void addRule(BaseRule aRule) { - MatchNode aRuleNode = new MatchNode(aRule); - this.ruleToRuleNode.put(aRule, aRuleNode); + MatchNode aMatchNode = new MatchNode(aRule); + this.ruleToMatchNode.put(aRule, aMatchNode); } /** @@ -70,7 +71,7 @@ public void addRules(Set someRules) { */ public Set getRules() { - return this.ruleToRuleNode.keySet(); + return this.ruleToMatchNode.keySet(); } /** @@ -89,28 +90,55 @@ public Map> getAntecedentNeighbors(BaseRule aRule) { * this rule's antecedent. */ public Map> getAntecedentNeighbors(BaseRule aRule, EnumSet aConfig) { - MatchNode aRuleNode = this.ruleToRuleNode.get(aRule); + MatchNode aMatchNode = this.ruleToMatchNode.get(aRule); - assert aRuleNode != null; + assert aMatchNode != null; - Map> newMapping = BaseRule.getMatches(aRule, this.getRules(), true, aConfig); + // calculate matches + Set combiMatches = BaseRule.getMatches(aRule, this.getRules(), true, aConfig); + + // store combi matches + aMatchNode.setAntecedentCombiMatches(combiMatches); + + // store normal matches + Map> newMapping = convertToMapping(combiMatches); for (Map.Entry> entry : newMapping.entrySet()) { - aRuleNode.setAntecedentNeighbor(entry.getKey(), Match.invertAll(entry.getValue())); - this.ruleToRuleNode.get(entry.getKey()).setConsequentNeighbor(aRule, entry.getValue()); + aMatchNode.setAntecedentNeighbor(entry.getKey(), Match.invertAll(entry.getValue())); + this.ruleToMatchNode.get(entry.getKey()).setConsequentNeighbor(aRule, entry.getValue()); } return newMapping; } - public void addToNewMapping(Map> newMapping, Map.Entry entryToAdd) { - // check if rule already has entry in newMapping - if (!newMapping.containsKey(entryToAdd.getKey())) { - newMapping.put(entryToAdd.getKey(), new HashSet<>()); + public Set getAntecedentCombiMatches(BaseRule aRule) { + assert aRule != null; + MatchNode mn = this.ruleToMatchNode.get(aRule); + return mn.getAntecedentCombiMatches(); + } + + public Set getConsequentCombiMatches(BaseRule aRule) { + assert aRule != null; + MatchNode mn = this.ruleToMatchNode.get(aRule); + return mn.getConsequentCombiMatches(); + } + + private Map> convertToMapping(Set someMatches) { + var mapping = new HashMap>(); + + for (CombiMatch cm : someMatches) { + for (Map.Entry> entry : cm.entrySet()) { + // get rule match set + var ruleMatchSet = mapping.get(entry.getKey()); + if (ruleMatchSet == null) { + ruleMatchSet = new HashSet(); + mapping.put(entry.getKey(), ruleMatchSet); + } + ruleMatchSet.addAll(entry.getValue()); + } } - Set matches = newMapping.get(entryToAdd.getKey()); - matches.add(entryToAdd.getValue()); + return mapping; } /** @@ -118,7 +146,6 @@ public void addToNewMapping(Map> newMapping, Map.Entry> getConsequentNeighbors(BaseRule aRule) { return this.getConsequentNeighbors(aRule, EnumSet.noneOf(MatchFlag.class)); - } /** @@ -132,15 +159,20 @@ public Map> getConsequentNeighbors(BaseRule aRule) { * this rule's consequent. */ public Map> getConsequentNeighbors(BaseRule aRule, EnumSet aConfig) { - MatchNode aRuleNode = this.ruleToRuleNode.get(aRule); + MatchNode aMatchNode = this.ruleToMatchNode.get(aRule); + + assert aMatchNode != null; - assert aRuleNode != null; + // calculate matches + Set combiMatches = BaseRule.getMatches(aRule, this.getRules(), false, aConfig); - Map> newMapping = BaseRule.getMatches(aRule, this.getRules(), false, aConfig); + // store combi matches + Map> newMapping = convertToMapping(combiMatches); + // store normal matches for (Map.Entry> entry : newMapping.entrySet()) { - aRuleNode.setConsequentNeighbor(entry.getKey(), Match.invertAll(entry.getValue())); - this.ruleToRuleNode.get(entry.getKey()).setAntecedentNeighbor(aRule, entry.getValue()); + aMatchNode.setConsequentNeighbor(entry.getKey(), Match.invertAll(entry.getValue())); + this.ruleToMatchNode.get(entry.getKey()).setAntecedentNeighbor(aRule, entry.getValue()); } return newMapping; @@ -151,7 +183,7 @@ public Map> getConsequentNeighbors(BaseRule aRule, EnumSet< * the neighbor of who. */ public void reset() { - for (MatchNode r : this.ruleToRuleNode.values()) { + for (MatchNode r : this.ruleToMatchNode.values()) { r.reset(); } } @@ -178,7 +210,7 @@ public String getGraphVizCode(ReasonerPlan aPlan, boolean urlOnly) { sb.append("strict digraph {\n"); Map ruleToName = new HashMap<>(); - for (MatchNode r : ruleToRuleNode.values()) { + for (MatchNode r : ruleToMatchNode.values()) { String currentName = ruleToName.get(r.getRule()); if (currentName == null) { diff --git a/reasoner/src/test/java/eu/knowledge/engine/reasoner/BackwardTest.java b/reasoner/src/test/java/eu/knowledge/engine/reasoner/BackwardTest.java index 550123fff..910ffc613 100644 --- a/reasoner/src/test/java/eu/knowledge/engine/reasoner/BackwardTest.java +++ b/reasoner/src/test/java/eu/knowledge/engine/reasoner/BackwardTest.java @@ -12,15 +12,11 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import org.apache.jena.graph.Node; -import org.apache.jena.graph.Triple; import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.rdf.model.Statement; import org.apache.jena.rdf.model.StmtIterator; import org.apache.jena.sparql.graph.PrefixMappingZero; import org.apache.jena.sparql.lang.arq.ParseException; -import org.apache.jena.sparql.sse.SSE; import org.apache.jena.sparql.util.FmtUtils; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -29,11 +25,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import eu.knowledge.engine.reasoner.ProactiveRule; -import eu.knowledge.engine.reasoner.ReasonerPlan; -import eu.knowledge.engine.reasoner.Rule; -import eu.knowledge.engine.reasoner.TaskBoard; -import eu.knowledge.engine.reasoner.TransformBindingSetHandler; import eu.knowledge.engine.reasoner.api.Binding; import eu.knowledge.engine.reasoner.api.BindingSet; import eu.knowledge.engine.reasoner.api.TriplePattern; diff --git a/reasoner/src/test/java/eu/knowledge/engine/reasoner/BindingTest.java b/reasoner/src/test/java/eu/knowledge/engine/reasoner/BindingTest.java index 444930684..5ba7537dc 100644 --- a/reasoner/src/test/java/eu/knowledge/engine/reasoner/BindingTest.java +++ b/reasoner/src/test/java/eu/knowledge/engine/reasoner/BindingTest.java @@ -61,7 +61,7 @@ public void testFruitfulVsActualMatches() { BaseRule r = new BaseRule("test", toTriplePattern(gp2), new HashSet<>()); - Set matches = r.antecedentMatches(toTriplePattern(gp1), EnumSet.of(MatchFlag.ONLY_BIGGEST)); + Set matches = r.antecedentMatches(toTriplePattern(gp1), EnumSet.of(MatchFlag.ONLY_BIGGEST, MatchFlag.ONE_TO_ONE)); LOG.info("matches size: {}", matches.size()); diff --git a/reasoner/src/test/java/eu/knowledge/engine/reasoner/JenaRDFSRulesTest.java b/reasoner/src/test/java/eu/knowledge/engine/reasoner/JenaRDFSRulesTest.java index f0b8424f6..59d6c22c0 100644 --- a/reasoner/src/test/java/eu/knowledge/engine/reasoner/JenaRDFSRulesTest.java +++ b/reasoner/src/test/java/eu/knowledge/engine/reasoner/JenaRDFSRulesTest.java @@ -47,7 +47,7 @@ public class JenaRDFSRulesTest { * @throws ExecutionException * @throws ParseException */ - @Disabled + @Test public void test() throws InterruptedException, ExecutionException, ParseException { diff --git a/reasoner/src/test/java/eu/knowledge/engine/reasoner/SimpleTransitivityTest.java b/reasoner/src/test/java/eu/knowledge/engine/reasoner/SimpleTransitivityTest.java new file mode 100644 index 000000000..48bc87f85 --- /dev/null +++ b/reasoner/src/test/java/eu/knowledge/engine/reasoner/SimpleTransitivityTest.java @@ -0,0 +1,84 @@ +package eu.knowledge.engine.reasoner; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ExecutionException; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; + +import eu.knowledge.engine.reasoner.BaseRule.MatchFlag; +import eu.knowledge.engine.reasoner.api.Binding; +import eu.knowledge.engine.reasoner.api.BindingSet; +import eu.knowledge.engine.reasoner.api.TriplePattern; +import eu.knowledge.engine.reasoner.rulestore.RuleStore; +import eu.knowledge.engine.reasoner.util.DataBindingSetHandler; +import eu.knowledge.engine.reasoner.util.Table; + +@TestInstance(Lifecycle.PER_CLASS) +public class SimpleTransitivityTest { + + private RuleStore store; + + @BeforeAll + public void init() { + store = new RuleStore(); + + // transitivity rule + Set antecedent = new HashSet<>(); + antecedent.add(new TriplePattern("?x ?y")); + antecedent.add(new TriplePattern("?y ?z")); + + Set consequent = new HashSet<>(); + consequent.add(new TriplePattern("?x ?z")); + + Rule transitivity = new Rule(antecedent, consequent); + + store.addRule(transitivity); + + // data rule + DataBindingSetHandler aBindingSetHandler = new DataBindingSetHandler( + new Table(new String[] { "a", "b" }, new String[] { + //@formatter:off + ",", + ",", + //@formatter:on + })); + + Rule rule = new Rule(new HashSet<>(), new HashSet<>(Arrays.asList(new TriplePattern("?a ?b"))), + aBindingSetHandler); + + store.addRule(rule); + } + + @Test + public void test() throws InterruptedException, ExecutionException { + Set aGoal = new HashSet<>(); + aGoal.add(new TriplePattern("?x ?y")); + ProactiveRule startRule = new ProactiveRule(aGoal, new HashSet<>()); + store.addRule(startRule); + + ReasonerPlan plan = new ReasonerPlan(store, startRule, EnumSet.of(MatchFlag.ONLY_BIGGEST)); + + store.printGraphVizCode(plan); + + TaskBoard tb; + BindingSet bindingSet = new BindingSet(); + bindingSet.add(new Binding()); + + while ((tb = plan.execute(bindingSet)).hasTasks()) { + tb.executeScheduledTasks().get(); + } + + BindingSet result = plan.getResults(); + + // it should be , and , and (derived) ,. + assertEquals(3, result.size()); + } +} diff --git a/reasoner/src/test/java/eu/knowledge/engine/reasoner/VerySimpleBackwardTest.java b/reasoner/src/test/java/eu/knowledge/engine/reasoner/VerySimpleBackwardTest.java index e78d69769..9f3e7e5a3 100644 --- a/reasoner/src/test/java/eu/knowledge/engine/reasoner/VerySimpleBackwardTest.java +++ b/reasoner/src/test/java/eu/knowledge/engine/reasoner/VerySimpleBackwardTest.java @@ -3,6 +3,7 @@ import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; +import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -16,6 +17,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import eu.knowledge.engine.reasoner.BaseRule.MatchFlag; import eu.knowledge.engine.reasoner.ProactiveRule; import eu.knowledge.engine.reasoner.ReasonerPlan; import eu.knowledge.engine.reasoner.Rule; @@ -75,14 +77,13 @@ public void init() throws URISyntaxException { // Initialize store = new RuleStore(); bindingSetHandler = new ProxyDataBindingSetHandler(new Table(new String[] { - //@formatter:off + // @formatter:off "a", "b" - //@formatter:on + // @formatter:on }, new String[] { - //@formatter:off - ",22", - ",21", - //@formatter:on + // @formatter:off + ",22", ",21", + // @formatter:on })); store.addRule(new Rule( new HashSet<>( @@ -103,7 +104,8 @@ public void init() throws URISyntaxException { @Test public void doReasoning() throws InterruptedException, ExecutionException { // Start reasoning - ReasonerPlan plan = new ReasonerPlan(store, startRule); + ReasonerPlan plan = new ReasonerPlan(store, startRule, + EnumSet.of(MatchFlag.ONLY_BIGGEST, MatchFlag.FULLY_COVERED)); BindingSet bs = new BindingSet(); Binding binding2 = new Binding(); diff --git a/reasoner/src/test/java/eu/knowledge/engine/reasoner/api/MatchTest.java b/reasoner/src/test/java/eu/knowledge/engine/reasoner/api/MatchTest.java index fd5179847..e16ac5318 100644 --- a/reasoner/src/test/java/eu/knowledge/engine/reasoner/api/MatchTest.java +++ b/reasoner/src/test/java/eu/knowledge/engine/reasoner/api/MatchTest.java @@ -165,7 +165,8 @@ public void testGPMatcher7() { BaseRule r = new ProactiveRule(obj, new HashSet<>()); - Set findMatchesWithConsequent = r.antecedentMatches(obj, EnumSet.of(MatchFlag.ONLY_BIGGEST, MatchFlag.FULLY_COVERED)); + Set findMatchesWithConsequent = r.antecedentMatches(obj, + EnumSet.of(MatchFlag.ONLY_BIGGEST, MatchFlag.FULLY_COVERED)); System.out.println("Size: " + findMatchesWithConsequent.size()); for (Match m : findMatchesWithConsequent) { @@ -340,7 +341,8 @@ public void testGPMatcher12() { }); Set findMatchesWithAntecedent = r.antecedentMatches( - new HashSet<>(Arrays.asList(/* t1, */ t5, t9, t8, t7, t6, t4, t3)), EnumSet.of(MatchFlag.ONLY_BIGGEST, MatchFlag.FULLY_COVERED)); + new HashSet<>(Arrays.asList(/* t1, */ t5, t9, t8, t7, t6, t4, t3)), + EnumSet.of(MatchFlag.ONLY_BIGGEST, MatchFlag.FULLY_COVERED, MatchFlag.ONE_TO_ONE)); System.out.println("Size: " + findMatchesWithAntecedent.size()); // System.out.println(findMatchesWithConsequent); @@ -428,7 +430,7 @@ public void testGPMatcherCardinalityTest() { Rule r = new Rule(new HashSet<>(), obj); Set findMatchesWithConsequent = r.consequentMatches(new HashSet<>(Arrays.asList(graphPattern)), - EnumSet.noneOf(MatchFlag.class)); + EnumSet.of(MatchFlag.ONE_TO_ONE)); System.out.println("graph pattern size " + gpSize + " gives matches size " + findMatchesWithConsequent.size() + "-" + getNumberOfMatches(gpSize)); @@ -484,14 +486,14 @@ public void testBugTranslate() { var tvbs = new TripleVarBindingSet(obj, bs); - var nBs = tvbs.translate(rhs, findMatchesWithConsequent); + var nBs = tvbs.translate(rhs, findMatchesWithConsequent).values().iterator().next(); System.out.println(nBs); assertTrue(nBs.isEmpty()); } @Test - public void testOtherBurgTranslate() { + public void testOtherBugTranslate() { TriplePattern t1 = new TriplePattern("?p ?t"); TriplePattern t2 = new TriplePattern("?p ?q"); Set obj = new HashSet<>(Arrays.asList(t1, t2)); @@ -544,12 +546,16 @@ public void testTranslateEmptyBindingSet() { var match = new Match(t1, t2, map); - TripleVarBindingSet tvbs2 = tvbs1.translate(new HashSet<>(Arrays.asList(t2)), + Map translated = tvbs1.translate(new HashSet<>(Arrays.asList(t2)), new HashSet<>(Arrays.asList(match))); - System.out.println("BindingSet: " + tvbs2); + for (TripleVarBindingSet aBS : translated.values()) { + tvbs1 = tvbs1.merge(aBS); + } + + System.out.println("BindingSet: " + tvbs1); - assertTrue(tvbs2.isEmpty()); + assertTrue(tvbs1.isEmpty()); } @Test @@ -594,7 +600,8 @@ public CompletableFuture handle(BindingSet aBindingSet) { System.out.println("NrOfMatches with " + obj.size() + " triple patterns: " + getNumberOfMatches(obj.size())); - Set findMatchesWithAntecedent = r.antecedentMatches(obj, EnumSet.of(MatchFlag.ONLY_BIGGEST, MatchFlag.FULLY_COVERED)); + Set findMatchesWithAntecedent = r.antecedentMatches(obj, + EnumSet.of(MatchFlag.ONLY_BIGGEST, MatchFlag.FULLY_COVERED)); System.out.println("Size: " + findMatchesWithAntecedent.size()); @@ -692,7 +699,7 @@ public void testNewGPMatcher1() { System.out.println(matches); - assertEquals(matches.size(), 1); + assertEquals(matches.size(), 2); } @Test @@ -708,7 +715,8 @@ public void testNewGPMatcherTransitivity() { BaseRule r2 = new Rule(new HashSet<>(), tp2); - var matches = BaseRule.getMatches(r1, new HashSet<>(Arrays.asList(r2)), true, EnumSet.noneOf(MatchFlag.class)); + var matches = BaseRule.getMatches(r1, new HashSet<>(Arrays.asList(r2)), true, + EnumSet.of(MatchFlag.ONLY_BIGGEST)); System.out.println(matches); assertEquals(1, matches.size()); @@ -732,7 +740,7 @@ public void testNewGPMatcher2() { var matches = BaseRule.getMatches(r1, new HashSet<>(Arrays.asList(r2)), true, EnumSet.noneOf(MatchFlag.class)); System.out.println(matches); - assertEquals(1, matches.size()); + assertEquals(7, matches.size()); } } diff --git a/reasoner/src/test/java/eu/knowledge/engine/reasoner/api/ReasoningPlanTest.java b/reasoner/src/test/java/eu/knowledge/engine/reasoner/api/ReasoningPlanTest.java index 99f977064..21cff1cf2 100644 --- a/reasoner/src/test/java/eu/knowledge/engine/reasoner/api/ReasoningPlanTest.java +++ b/reasoner/src/test/java/eu/knowledge/engine/reasoner/api/ReasoningPlanTest.java @@ -15,6 +15,7 @@ import eu.knowledge.engine.reasoner.ProactiveRule; import eu.knowledge.engine.reasoner.ReasonerPlan; import eu.knowledge.engine.reasoner.Rule; +import eu.knowledge.engine.reasoner.TaskBoard; import eu.knowledge.engine.reasoner.api.Binding; import eu.knowledge.engine.reasoner.api.BindingSet; import eu.knowledge.engine.reasoner.api.TriplePattern; @@ -26,7 +27,6 @@ public class ReasoningPlanTest { private static final String TEST_RULES = "/reasoningplantest.rls"; - @Disabled("Until optimize() method is availble.") @Test public void test() throws IOException, InterruptedException, ExecutionException { @@ -38,27 +38,24 @@ public void test() throws IOException, InterruptedException, ExecutionException Rule first = someRules.get(0); first.backwardForwardBindingSetHandler = new DataBindingSetHandler(new Table(new String[] { - //@formatter:off + // @formatter:off "a", "b", "c" - //@formatter:on + // @formatter:on }, new String[] { - //@formatter:off - ",,", - ",,", - //@formatter:on + // @formatter:off + ",,", ",,", + // @formatter:on })); Rule second = someRules.get(1); second.backwardForwardBindingSetHandler = new DataBindingSetHandler(new Table(new String[] { - //@formatter:off + // @formatter:off "x", "y", "z" - //@formatter:on + // @formatter:on }, new String[] { - //@formatter:off - ",,", - ",,", - ",,", - //@formatter:on + // @formatter:off + ",,", ",,", ",,", + // @formatter:on })); store.addRules(new HashSet<>(someRules)); @@ -90,9 +87,12 @@ public void test() throws IOException, InterruptedException, ExecutionException aBindingSet.add(b1); aBindingSet.add(b2); - plan.execute(aBindingSet); + TaskBoard tb; + while ((tb = plan.execute(aBindingSet)).hasTasks()) { + tb.executeScheduledTasks(); + } - BindingSet bs = null; //plan.getStartNode().getIncomingAntecedentBindingSet().toBindingSet(); + BindingSet bs = plan.getResults(); System.out.println(bs); assertEquals(2, bs.size()); diff --git a/reasoner/src/test/java/eu/knowledge/engine/reasoner/reasoningnode/TestBindingSetStore.java b/reasoner/src/test/java/eu/knowledge/engine/reasoner/reasoningnode/TestBindingSetStore.java index 2daa83684..4b67a3329 100644 --- a/reasoner/src/test/java/eu/knowledge/engine/reasoner/reasoningnode/TestBindingSetStore.java +++ b/reasoner/src/test/java/eu/knowledge/engine/reasoner/reasoningnode/TestBindingSetStore.java @@ -4,81 +4,98 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; import org.junit.jupiter.api.Test; +import eu.knowledge.engine.reasoner.BaseRule; +import eu.knowledge.engine.reasoner.Match; import eu.knowledge.engine.reasoner.Rule; import eu.knowledge.engine.reasoner.api.BindingSet; import eu.knowledge.engine.reasoner.api.TriplePattern; +import eu.knowledge.engine.reasoner.api.TripleVarBindingSet; import eu.knowledge.engine.reasoner.api.Util; import eu.knowledge.engine.reasoner.rulenode.ActiveConsRuleNode; import eu.knowledge.engine.reasoner.rulenode.BindingSetStore; import eu.knowledge.engine.reasoner.rulenode.RuleNode; public class TestBindingSetStore { - @Test - public void testAddingEmptyBindingSetTwice() { - Set graphPattern = new HashSet<>(); - var tp = new TriplePattern("?s :prop ?o"); - graphPattern.add(tp); - - Set ruleNodes = new HashSet<>(); - var neighbour = new ActiveConsRuleNode(new Rule(new HashSet<>(), new HashSet<>(Arrays.asList(tp)))); - ruleNodes.add(neighbour); - - BindingSetStore bss = new BindingSetStore(graphPattern, ruleNodes); - - assertFalse(bss.haveAllNeighborsContributed()); - var changed = bss.add(neighbour, new BindingSet().toTripleVarBindingSet(graphPattern)); - assertTrue(changed); - changed = bss.add(neighbour, new BindingSet().toTripleVarBindingSet(graphPattern)); - assertFalse(changed); - assertTrue(bss.haveAllNeighborsContributed()); - } - - @Test - public void testAddingNonEmptyBindingSetTwice() { - Set graphPattern = new HashSet<>(); - var tp = new TriplePattern("?s :prop ?o"); - graphPattern.add(tp); - - Set ruleNodes = new HashSet<>(); - var neighbour = new ActiveConsRuleNode(new Rule(new HashSet<>(), new HashSet<>(Arrays.asList(tp)))); - ruleNodes.add(neighbour); - - BindingSetStore bss = new BindingSetStore(graphPattern, ruleNodes); - - assertFalse(bss.haveAllNeighborsContributed()); - var bs = Util.toBindingSet("s=,o=7"); - var changed = bss.add(neighbour, bs.toTripleVarBindingSet(graphPattern)); - assertTrue(changed); - bs = Util.toBindingSet("s=,o=7"); - changed = bss.add(neighbour, bs.toTripleVarBindingSet(graphPattern)); - assertFalse(changed); - assertTrue(bss.haveAllNeighborsContributed()); - } - - @Test - public void testAddingDifferentNonEmptyBindingSets() { - Set graphPattern = new HashSet<>(); - var tp = new TriplePattern("?s :prop ?o"); - graphPattern.add(tp); - - Set ruleNodes = new HashSet<>(); - var neighbour = new ActiveConsRuleNode(new Rule(new HashSet<>(), new HashSet<>(Arrays.asList(tp)))); - ruleNodes.add(neighbour); - - BindingSetStore bss = new BindingSetStore(graphPattern, ruleNodes); - - assertFalse(bss.haveAllNeighborsContributed()); - var bs = Util.toBindingSet("s=,o=7"); - var changed = bss.add(neighbour, bs.toTripleVarBindingSet(graphPattern)); - assertTrue(changed); - bs = Util.toBindingSet("s=,o=8"); - changed = bss.add(neighbour, bs.toTripleVarBindingSet(graphPattern)); - assertTrue(changed); - assertTrue(bss.haveAllNeighborsContributed()); - } + @Test + public void testAddingEmptyBindingSetTwice() { + Set graphPattern = new HashSet<>(); + var tp = new TriplePattern("?s :prop ?o"); + graphPattern.add(tp); + + Set ruleNodes = new HashSet<>(); + var neighbour = new ActiveConsRuleNode(new Rule(new HashSet<>(), new HashSet<>(Arrays.asList(tp)))); + ruleNodes.add(neighbour); + + BindingSetStore bss = new BindingSetStore(graphPattern, ruleNodes); + + assertFalse(bss.haveAllNeighborsContributed()); + TripleVarBindingSet tripleVarBindingSet = new BindingSet().toTripleVarBindingSet(graphPattern); + + var changed = bss.add(neighbour, convertToDummyMap(tripleVarBindingSet)); + assertTrue(changed); + + TripleVarBindingSet tripleVarBindingSet2 = new BindingSet().toTripleVarBindingSet(graphPattern); + + changed = bss.add(neighbour, convertToDummyMap(tripleVarBindingSet2)); + assertFalse(changed); + + assertTrue(bss.haveAllNeighborsContributed()); + } + + private Map convertToDummyMap(TripleVarBindingSet aTVBS) { + var map = new HashMap(); + map.put(new Match(new TriplePattern("?s ?p ?o"), new TriplePattern("?a ?b ?c"), new HashMap<>()), aTVBS); + return map; + } + + @Test + public void testAddingNonEmptyBindingSetTwice() { + Set graphPattern = new HashSet<>(); + var tp = new TriplePattern("?s :prop ?o"); + graphPattern.add(tp); + + Set ruleNodes = new HashSet<>(); + var neighbour = new ActiveConsRuleNode(new Rule(new HashSet<>(), new HashSet<>(Arrays.asList(tp)))); + ruleNodes.add(neighbour); + + BindingSetStore bss = new BindingSetStore(graphPattern, ruleNodes); + + assertFalse(bss.haveAllNeighborsContributed()); + var bs = Util.toBindingSet("s=,o=7"); + var changed = bss.add(neighbour, convertToDummyMap(bs.toTripleVarBindingSet(graphPattern))); + assertTrue(changed); + bs = Util.toBindingSet("s=,o=7"); + changed = bss.add(neighbour, convertToDummyMap(bs.toTripleVarBindingSet(graphPattern))); + assertFalse(changed); + assertTrue(bss.haveAllNeighborsContributed()); + } + + @Test + public void testAddingDifferentNonEmptyBindingSets() { + Set graphPattern = new HashSet<>(); + var tp = new TriplePattern("?s :prop ?o"); + graphPattern.add(tp); + + Set ruleNodes = new HashSet<>(); + var neighbour = new ActiveConsRuleNode(new Rule(new HashSet<>(), new HashSet<>(Arrays.asList(tp)))); + ruleNodes.add(neighbour); + + BindingSetStore bss = new BindingSetStore(graphPattern, ruleNodes); + + assertFalse(bss.haveAllNeighborsContributed()); + var bs = Util.toBindingSet("s=,o=7"); + var changed = bss.add(neighbour, convertToDummyMap(bs.toTripleVarBindingSet(graphPattern))); + assertTrue(changed); + bs = Util.toBindingSet("s=,o=8"); + changed = bss.add(neighbour, convertToDummyMap(bs.toTripleVarBindingSet(graphPattern))); + assertTrue(changed); + assertTrue(bss.haveAllNeighborsContributed()); + } } diff --git a/smart-connector/src/test/java/eu/knowledge/engine/smartconnector/api/TestAskAnswerLargeBindingSets.java b/smart-connector/src/test/java/eu/knowledge/engine/smartconnector/api/TestAskAnswerLargeBindingSets.java new file mode 100644 index 000000000..e4d543b22 --- /dev/null +++ b/smart-connector/src/test/java/eu/knowledge/engine/smartconnector/api/TestAskAnswerLargeBindingSets.java @@ -0,0 +1,167 @@ +package eu.knowledge.engine.smartconnector.api; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.net.URI; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; + +import org.apache.jena.shared.PrefixMapping; +import org.apache.jena.sparql.graph.PrefixMappingMem; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import eu.knowledge.engine.smartconnector.util.KnowledgeBaseImpl; +import eu.knowledge.engine.smartconnector.util.KnowledgeNetwork; + +public class TestAskAnswerLargeBindingSets { + + private static final Logger LOG = LoggerFactory.getLogger(TestAskAnswerLargeBindingSets.class); + + private static KnowledgeBaseImpl kb1; + private static KnowledgeBaseImpl kb2; + private static KnowledgeBaseImpl kb3; + + private static BindingSet kb1BS; + private static BindingSet kb2BS; + + private static int NR_OF_BINDINGS = 1500; + + private static char[] chars = new char[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g' }; + + @BeforeAll + public static void setup() throws InterruptedException, BrokenBarrierException, TimeoutException { + + kb1BS = new BindingSet(); + + Binding b; + for (int i = 0; i < NR_OF_BINDINGS; i++) { + b = new Binding(); + for (char c : chars) + b.put(Character.toString(c), ""); + kb1BS.add(b); + } + + kb2BS = new BindingSet(); + + for (int i = NR_OF_BINDINGS; i < (2 * NR_OF_BINDINGS); i++) { + b = new Binding(); + for (char c : chars) + b.put(Character.toString(c), ""); + kb2BS.add(b); + } + + } + + @Test + public void testAskAnswer() throws InterruptedException { + + PrefixMappingMem prefixes = new PrefixMappingMem(); + prefixes.setNsPrefixes(PrefixMapping.Standard); + prefixes.setNsPrefix("ex", "https://www.tno.nl/example/"); + + var kn = new KnowledgeNetwork(); + kb1 = new KnowledgeBaseImpl("kb1"); + kn.addKB(kb1); + kb2 = new KnowledgeBaseImpl("kb2"); + kn.addKB(kb2); + kb3 = new KnowledgeBaseImpl("kb3"); + kn.addKB(kb3); + + GraphPattern gp1 = new GraphPattern(prefixes, """ + ?a ?b . + ?a ?c . + ?a ?d . + ?a ?e . + ?e ?f . + ?e ?g . + """); + + AnswerKnowledgeInteraction aKI = new AnswerKnowledgeInteraction(new CommunicativeAct(), gp1); + kb1.register(aKI, (AnswerHandler) (anAKI, anAnswerExchangeInfo) -> { + assertTrue( + anAnswerExchangeInfo.getIncomingBindings().isEmpty() + || anAnswerExchangeInfo.getIncomingBindings().iterator().next().getVariables().isEmpty(), + "Should not have bindings in this binding set."); + + return kb1BS; + }); + + AnswerKnowledgeInteraction aKI3 = new AnswerKnowledgeInteraction(new CommunicativeAct(), gp1); + kb3.register(aKI3, (AnswerHandler) (anAKI, anAnswerExchangeInfo) -> { + assertTrue( + anAnswerExchangeInfo.getIncomingBindings().isEmpty() + || anAnswerExchangeInfo.getIncomingBindings().iterator().next().getVariables().isEmpty(), + "Should not have bindings in this binding set."); + + return kb2BS; + }); + + AskKnowledgeInteraction askKI = new AskKnowledgeInteraction(new CommunicativeAct(), gp1); + kb2.register(askKI); + LOG.info("Waiting until everyone is up-to-date!"); + kn.sync(); + LOG.info("Everyone is up-to-date!"); + + // start testing! + BindingSet bindings = null; + AskResult result = null; + try { + LOG.trace("Before ask."); + + try { + result = kb2.ask(askKI, new BindingSet()).get(10, TimeUnit.SECONDS); + } catch (TimeoutException te) { + fail("This ASK should take 10 seconds maximally."); + } + + bindings = result.getBindings(); + LOG.trace("After ask."); + + Set kbIds = result.getExchangeInfoPerKnowledgeBase().stream().map(AskExchangeInfo::getKnowledgeBaseId) + .collect(Collectors.toSet()); + + assertEquals(new HashSet(Arrays.asList(kb1.getKnowledgeBaseId(), kb3.getKnowledgeBaseId())), kbIds, + "The result should come from kb1, kb3 and not: " + kbIds); + + assertEquals(2 * NR_OF_BINDINGS, bindings.size()); + + } catch (InterruptedException | ExecutionException e) { + fail(); + } + } + + @AfterAll + public static void cleanup() { + LOG.info("Clean up: {}", TestAskAnswerLargeBindingSets.class.getSimpleName()); + if (kb1 != null) { + kb1.stop(); + } else { + fail("KB1 should not be null!"); + } + + if (kb2 != null) { + + kb2.stop(); + } else { + fail("KB2 should not be null!"); + } + + if (kb3 != null) { + kb3.stop(); + } else { + fail("KB3 should not be null!"); + } + } +} diff --git a/smart-connector/src/test/java/eu/knowledge/engine/smartconnector/api/TestGraphPatternMatch.java b/smart-connector/src/test/java/eu/knowledge/engine/smartconnector/api/TestGraphPatternMatch.java index 3a6177428..7a3a0c684 100644 --- a/smart-connector/src/test/java/eu/knowledge/engine/smartconnector/api/TestGraphPatternMatch.java +++ b/smart-connector/src/test/java/eu/knowledge/engine/smartconnector/api/TestGraphPatternMatch.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -19,6 +20,7 @@ import eu.knowledge.engine.reasoner.Rule; import eu.knowledge.engine.reasoner.SinkBindingSetHandler; import eu.knowledge.engine.reasoner.TransformBindingSetHandler; +import eu.knowledge.engine.reasoner.BaseRule.CombiMatch; import eu.knowledge.engine.smartconnector.impl.Util; public class TestGraphPatternMatch { @@ -176,10 +178,28 @@ public CompletableFuture handle( }); // assertTrue(GraphPatternMatcher.areIsomorphic(gp1, gp2)); - Map> isomorphism = BaseRule.getMatches(anteRule, Collections.singleton(consRule), true, - MatchStrategy.ENTRY_LEVEL.toConfig(true)); + Map> isomorphism = convertToMapping(BaseRule.getMatches(anteRule, + Collections.singleton(consRule), true, MatchStrategy.ENTRY_LEVEL.toConfig(true))); return isomorphism.containsKey(consRule) ? isomorphism.get(consRule) : new HashSet(); } + // copied from RuleStore#convertToMapping + private Map> convertToMapping(Set someMatches) { + var mapping = new HashMap>(); + + for (CombiMatch cm : someMatches) { + for (Map.Entry> entry : cm.entrySet()) { + // get rule match set + var ruleMatchSet = mapping.get(entry.getKey()); + if (ruleMatchSet == null) { + ruleMatchSet = new HashSet(); + mapping.put(entry.getKey(), ruleMatchSet); + } + ruleMatchSet.addAll(entry.getValue()); + } + } + return mapping; + } + } diff --git a/smart-connector/src/test/java/eu/knowledge/engine/smartconnector/misc/WireMockFirstConfigurationTest.java b/smart-connector/src/test/java/eu/knowledge/engine/smartconnector/misc/WireMockFirstConfigurationTest.java index 13713940f..fe091da6e 100644 --- a/smart-connector/src/test/java/eu/knowledge/engine/smartconnector/misc/WireMockFirstConfigurationTest.java +++ b/smart-connector/src/test/java/eu/knowledge/engine/smartconnector/misc/WireMockFirstConfigurationTest.java @@ -55,7 +55,7 @@ public static void before(WireMockRuntimeInfo wmRuntimeInfo) { * @throws InterruptedException */ @Test - @Timeout(value = 3, unit = TimeUnit.SECONDS) + @Timeout(value = 4, unit = TimeUnit.SECONDS) public void testConfigHttpConnectTimeout() throws Exception { stubFor(post("/ker/").willReturn(status(201).withBody("{}"))); System.setProperty(SmartConnectorConfig.CONF_KEY_KE_HTTP_TIMEOUT, "1");