From 30858de80d4bf515a762e461fbe83a667f090ffc Mon Sep 17 00:00:00 2001 From: Barry Nouwt Date: Tue, 10 Sep 2024 18:39:44 +0200 Subject: [PATCH 1/2] MatchStrategy is now part of knowledge-engine module instead of reasoner How do we configure individual KIs? And which other configuration options are there isMeta() and includeMetaKIs(). Currently, the meta interactions are broken because they use the normal reasoner which is WAY TOO SLOW! --- .../knowledge/engine/reasoner/BaseRule.java | 50 +------- .../engine/reasoner/ReasonerPlan.java | 25 ++-- .../engine/reasoner/rulestore/MatchNode.java | 112 +++--------------- .../engine/reasoner/rulestore/RuleStore.java | 26 ++-- .../engine/reasoner/BindingTest.java | 5 +- .../engine/reasoner/api/MatchTest.java | 52 ++++---- .../api/KnowledgeInteraction.java | 2 - .../impl/InteractionProcessorImpl.java | 5 +- .../impl/ReasonerProcessor.java | 66 +++++++++-- .../api/TestDynamicSemanticComposition.java | 5 +- 10 files changed, 140 insertions(+), 208 deletions(-) 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 fa9a4bf0..85a69850 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/BaseRule.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/BaseRule.java @@ -82,10 +82,6 @@ public static enum MatchFlag { public static final EnumSet ALL_OPTS = EnumSet.allOf(MatchFlag.class); } - public static enum MatchStrategy { - SUPREME_LEVEL, ULTRA_LEVEL, ADVANCED_LEVEL, NORMAL_LEVEL, ENTRY_LEVEL - } - public static class TrivialBindingSetHandler implements TransformBindingSetHandler { private Set consequent; @@ -169,7 +165,7 @@ public Set getConsequent() { return this.consequent; } - public Set consequentMatches(Set anAntecedent, MatchStrategy aMatchStrategy) { + public Set consequentMatches(Set anAntecedent, EnumSet aMatchConfig) { if (!this.consequent.isEmpty()) { Rule r = new Rule(anAntecedent, new SinkBindingSetHandler() { @@ -179,8 +175,7 @@ public CompletableFuture handle(BindingSet aBindingSet) { return null; } }); - Map> matches = getMatches(this, new HashSet<>(Arrays.asList(r)), false, - aMatchStrategy); + Map> matches = getMatches(this, new HashSet<>(Arrays.asList(r)), false, aMatchConfig); if (matches.containsKey(r)) return matches.get(r); @@ -188,10 +183,10 @@ public CompletableFuture handle(BindingSet aBindingSet) { return new HashSet<>(); } - public Set antecedentMatches(Set aConsequent, MatchStrategy aMatchStrategy) { + 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, aMatchStrategy); + Map> matches = getMatches(this, new HashSet<>(Arrays.asList(r)), true, aMatchConfig); if (matches.containsKey(r)) return matches.get(r); } @@ -382,9 +377,7 @@ public static Map> getMatchesPerTriplePerRule(Set * @return A set of matches that all contribute to some full matche. */ public static Map> getMatches(BaseRule aTargetRule, Set someCandidateRules, - boolean antecedentOfTarget, MatchStrategy aStrategy) { - - EnumSet config = fromStrategyToConfig(aStrategy, antecedentOfTarget); + boolean antecedentOfTarget, EnumSet config) { Set targetGP = antecedentOfTarget ? aTargetRule.getAntecedent() : aTargetRule.getConsequent(); @@ -545,39 +538,6 @@ private static boolean isSubCombiMatch(CombiMatch aSmallerMatch, List fromStrategyToConfig(MatchStrategy aStrategy, boolean antecedentOfTarget) { - EnumSet config; - switch (aStrategy) { - case ENTRY_LEVEL: - config = EnumSet.of(MatchFlag.FULLY_COVERED, MatchFlag.ONE_TO_ONE, MatchFlag.ONLY_BIGGEST, - MatchFlag.SINGLE_RULE); - break; - case NORMAL_LEVEL: - config = EnumSet.of(MatchFlag.ONE_TO_ONE, MatchFlag.ONLY_BIGGEST); - - // disable fully covered when matching consequents. - if (antecedentOfTarget) - config.add(MatchFlag.FULLY_COVERED); - break; - case ADVANCED_LEVEL: - config = EnumSet.of(MatchFlag.ONLY_BIGGEST); - - // disable fully covered when matching consequents. - if (antecedentOfTarget) - config.add(MatchFlag.FULLY_COVERED); - break; - case ULTRA_LEVEL: - config = EnumSet.of(MatchFlag.ONLY_BIGGEST); - break; - case SUPREME_LEVEL: - config = EnumSet.noneOf(MatchFlag.class); - break; - default: - config = EnumSet.noneOf(MatchFlag.class); - } - return config; - } - private static void printCombiMatchesPerTriple(Map> combiMatchesPerTriple) { StringBuilder sb = new StringBuilder(); 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 537234c1..c8b79ab6 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/ReasonerPlan.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/ReasonerPlan.java @@ -2,6 +2,7 @@ import java.util.ArrayDeque; import java.util.Deque; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -11,7 +12,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import eu.knowledge.engine.reasoner.BaseRule.MatchStrategy; +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; @@ -38,7 +39,7 @@ public class ReasonerPlan { private final ProactiveRule start; private final Map ruleToRuleNode; private boolean done; - private MatchStrategy strategy = MatchStrategy.ULTRA_LEVEL; + private EnumSet matchConfig = EnumSet.noneOf(MatchFlag.class); private boolean useTaskBoard = true; public ReasonerPlan(RuleStore aStore, ProactiveRule aStartRule) { @@ -48,11 +49,11 @@ public ReasonerPlan(RuleStore aStore, ProactiveRule aStartRule) { createOrGetReasonerNode(this.start, null); } - public ReasonerPlan(RuleStore aStore, ProactiveRule aStartRule, MatchStrategy aStrategy) { + public ReasonerPlan(RuleStore aStore, ProactiveRule aStartRule, EnumSet aConfig) { this.store = aStore; this.start = aStartRule; this.ruleToRuleNode = new HashMap<>(); - this.strategy = aStrategy; + this.matchConfig = aConfig; createOrGetReasonerNode(this.start, null); } @@ -231,7 +232,7 @@ private RuleNode createOrGetReasonerNode(BaseRule aRule, BaseRule aParent) { // 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.strategy).forEach((rule, matches) -> { + this.store.getAntecedentNeighbors(aRule, this.matchConfig).forEach((rule, matches) -> { if (!(rule instanceof ProactiveRule)) { assert reasonerNode instanceof AntSide; var newNode = createOrGetReasonerNode(rule, aRule); @@ -247,7 +248,7 @@ private RuleNode createOrGetReasonerNode(BaseRule aRule, BaseRule aParent) { }); } else { // interested in both consequent and antecedent neighbors - this.store.getConsequentNeighbors(aRule, this.strategy).forEach((rule, matches) -> { + this.store.getConsequentNeighbors(aRule, this.matchConfig).forEach((rule, matches) -> { if (!(rule instanceof ProactiveRule)) { assert reasonerNode instanceof ConsSide; var newNode = createOrGetReasonerNode(rule, aRule); @@ -266,13 +267,13 @@ private RuleNode createOrGetReasonerNode(BaseRule aRule, BaseRule aParent) { // determine whether our parent matches us partially boolean ourAntecedentFullyMatchesParentConsequent = false; - if (aParent != null && this.store.getAntecedentNeighbors(aRule, this.strategy).containsKey(aParent)) { + if (aParent != null && this.store.getAntecedentNeighbors(aRule, this.matchConfig).containsKey(aParent)) { ourAntecedentFullyMatchesParentConsequent = antecedentFullyMatchesConsequent(aRule, aParent, - this.store.getAntecedentNeighbors(aRule, this.strategy).get(aParent), this.strategy); + this.store.getAntecedentNeighbors(aRule, this.matchConfig).get(aParent)); } if (!ourAntecedentFullyMatchesParentConsequent) { - this.store.getAntecedentNeighbors(aRule, this.strategy).forEach((rule, matches) -> { + this.store.getAntecedentNeighbors(aRule, this.matchConfig).forEach((rule, matches) -> { if (!(rule instanceof ProactiveRule)) { assert reasonerNode instanceof AntSide; var newNode = createOrGetReasonerNode(rule, aRule); @@ -317,7 +318,7 @@ private void scheduleOrDoTask(RuleNode current, TaskBoard taskBoard) { * @return */ private boolean antecedentFullyMatchesConsequent(BaseRule antecedentRule, BaseRule consequentRule, - Set someMatches, MatchStrategy aMatchStrategy) { + Set someMatches) { var antecedent = antecedentRule.getAntecedent(); var consequent = consequentRule.getConsequent(); @@ -348,8 +349,8 @@ private boolean antecedentFullyMatchesConsequent(BaseRule antecedentRule, BaseRu return false; } - public MatchStrategy getMatchStrategy() { - return this.strategy; + public EnumSet getMatchConfig() { + return this.matchConfig; } public RuleStore getStore() { 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 7166ac05..bcf8e317 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.Map; import java.util.Set; import eu.knowledge.engine.reasoner.BaseRule; -import eu.knowledge.engine.reasoner.BaseRule.MatchStrategy; +import eu.knowledge.engine.reasoner.BaseRule.MatchFlag; import eu.knowledge.engine.reasoner.Match; /** @@ -31,46 +32,19 @@ 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} */ - private Map> antecedentNeighborsNormal; - - /** - * All other rules in the {@link MatchNode#store} whose consequents match this - * rule's antecedent according to the {@link MatchStrategy#ADVANCED_LEVEL} - */ - private Map> antecedentNeighborsAdvanced; - - /** - * All other rules in the {@link MatchNode#store} whose consequents match this - * rule's antecedent according to the {@link MatchStrategy#ULTRA_LEVEL} - */ - private Map> antecedentNeighborsUltra; + private Map> antecedentNeighbors; /** * All other rules in the {@link MatchNode#store} whose antecedents match this * rule's consequent according to the {@link MatchStrategy#NORMAL_LEVEL} */ - private Map> consequentNeighborsNormal; - - /** - * All other rules in the {@link MatchNode#store} whose antecedents match this - * rule's consequent according to the {@link MatchStrategy#ADVANCED_LEVEL} - */ - private Map> consequentNeighborsAdvanced; - - /** - * All other rules in the {@link MatchNode#store} whose antecedents match this - * rule's consequent according to the {@link MatchStrategy#ULTRA_LEVEL} - */ - private Map> consequentNeighborsUltra; + private Map> consequentNeighbors; public MatchNode(BaseRule aRule) { this.rule = aRule; - this.antecedentNeighborsNormal = new HashMap<>(); - this.antecedentNeighborsAdvanced = new HashMap<>(); - this.antecedentNeighborsUltra = new HashMap<>(); - this.consequentNeighborsNormal = new HashMap<>(); - this.consequentNeighborsAdvanced = new HashMap<>(); - this.consequentNeighborsUltra = new HashMap<>(); + this.antecedentNeighbors = new HashMap<>(); + this.consequentNeighbors = new HashMap<>(); + } public BaseRule getRule() { @@ -78,43 +52,15 @@ public BaseRule getRule() { } public void setConsequentNeighbor(BaseRule aRule, Set someMatches) { - this.setConsequentNeighbor(aRule, someMatches, MatchStrategy.ULTRA_LEVEL); - } - - public void setAntecedentNeighbor(BaseRule aRule, Set someMatches) { - this.setAntecedentNeighbor(aRule, someMatches, MatchStrategy.ULTRA_LEVEL); - } - - public void setConsequentNeighbor(BaseRule aRule, Set someMatches, MatchStrategy aStrategy) { - Map> neighbors; - if (aStrategy.equals(MatchStrategy.NORMAL_LEVEL) || aStrategy.equals(MatchStrategy.ENTRY_LEVEL)) { - neighbors = this.consequentNeighborsNormal; - } else if (aStrategy.equals(MatchStrategy.ADVANCED_LEVEL)) { - neighbors = this.consequentNeighborsAdvanced; - } else if (aStrategy.equals(MatchStrategy.ULTRA_LEVEL)) { - neighbors = this.consequentNeighborsUltra; - } else { - assert false; - neighbors = new HashMap<>(); - } + Map> neighbors = this.consequentNeighbors; if (!neighbors.containsKey(aRule)) { neighbors.put(aRule, someMatches); } } - public void setAntecedentNeighbor(BaseRule aRule, Set someMatches, MatchStrategy aStrategy) { - Map> neighbors; - if (aStrategy.equals(MatchStrategy.NORMAL_LEVEL) || aStrategy.equals(MatchStrategy.ENTRY_LEVEL)) { - neighbors = this.antecedentNeighborsNormal; - } else if (aStrategy.equals(MatchStrategy.ADVANCED_LEVEL)) { - neighbors = this.antecedentNeighborsAdvanced; - } else if (aStrategy.equals(MatchStrategy.ULTRA_LEVEL)) { - neighbors = this.antecedentNeighborsUltra; - } else { - assert false; - neighbors = new HashMap<>(); - } + public void setAntecedentNeighbor(BaseRule aRule, Set someMatches) { + Map> neighbors = this.antecedentNeighbors; if (!neighbors.containsKey(aRule)) { neighbors.put(aRule, someMatches); @@ -122,37 +68,11 @@ public void setAntecedentNeighbor(BaseRule aRule, Set someMatches, MatchS } public Map> getConsequentNeighbors() { - return this.getConsequentNeighbors(MatchStrategy.ULTRA_LEVEL); + return this.consequentNeighbors; } public Map> getAntecedentNeighbors() { - return this.getAntecedentNeighbors(MatchStrategy.ULTRA_LEVEL); - } - - public Map> getConsequentNeighbors(MatchStrategy aStrategy) { - if (aStrategy.equals(MatchStrategy.NORMAL_LEVEL)) { - return this.consequentNeighborsNormal; - } else if (aStrategy.equals(MatchStrategy.ADVANCED_LEVEL)) { - return this.consequentNeighborsAdvanced; - } else if (aStrategy.equals(MatchStrategy.ULTRA_LEVEL)) { - return this.consequentNeighborsUltra; - } else { - assert false; - return null; - } - } - - public Map> getAntecedentNeighbors(MatchStrategy aStrategy) { - if (aStrategy.equals(MatchStrategy.NORMAL_LEVEL)) { - return this.antecedentNeighborsNormal; - } else if (aStrategy.equals(MatchStrategy.ADVANCED_LEVEL)) { - return this.antecedentNeighborsAdvanced; - } else if (aStrategy.equals(MatchStrategy.ULTRA_LEVEL)) { - return this.antecedentNeighborsUltra; - } else { - assert false; - return null; - } + return this.antecedentNeighbors; } /** @@ -163,12 +83,8 @@ public RuleStore getStore() { } public void reset() { - this.antecedentNeighborsNormal.clear(); - this.antecedentNeighborsAdvanced.clear(); - this.antecedentNeighborsUltra.clear(); - this.consequentNeighborsNormal.clear(); - this.consequentNeighborsAdvanced.clear(); - this.consequentNeighborsUltra.clear(); + this.antecedentNeighbors.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 f0fc5442..547233e6 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 @@ -6,6 +6,7 @@ import java.net.URI; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -16,7 +17,7 @@ import org.slf4j.LoggerFactory; import eu.knowledge.engine.reasoner.BaseRule; -import eu.knowledge.engine.reasoner.BaseRule.MatchStrategy; +import eu.knowledge.engine.reasoner.BaseRule.MatchFlag; import eu.knowledge.engine.reasoner.Match; import eu.knowledge.engine.reasoner.ProactiveRule; import eu.knowledge.engine.reasoner.ReasonerPlan; @@ -75,7 +76,7 @@ public Set getRules() { * @see #getAntecedentNeighbors(BaseRule, MatchStrategy) */ public Map> getAntecedentNeighbors(BaseRule aRule) { - return this.getAntecedentNeighbors(aRule, MatchStrategy.NORMAL_LEVEL); + return this.getAntecedentNeighbors(aRule, EnumSet.noneOf(MatchFlag.class)); } /** @@ -86,16 +87,16 @@ public Map> getAntecedentNeighbors(BaseRule aRule) { * @return A mapping from a neighbor rulenode and the way its consequent matches * this rule's antecedent. */ - public Map> getAntecedentNeighbors(BaseRule aRule, MatchStrategy aStrategy) { + public Map> getAntecedentNeighbors(BaseRule aRule, EnumSet aConfig) { MatchNode aRuleNode = this.ruleToRuleNode.get(aRule); assert aRuleNode != null; - Map> newMapping = BaseRule.getMatches(aRule, this.getRules(), true, aStrategy); + Map> newMapping = BaseRule.getMatches(aRule, this.getRules(), true, aConfig); for (Map.Entry> entry : newMapping.entrySet()) { - aRuleNode.setAntecedentNeighbor(entry.getKey(), Match.invertAll(entry.getValue()), aStrategy); - this.ruleToRuleNode.get(entry.getKey()).setConsequentNeighbor(aRule, entry.getValue(), aStrategy); + aRuleNode.setAntecedentNeighbor(entry.getKey(), Match.invertAll(entry.getValue())); + this.ruleToRuleNode.get(entry.getKey()).setConsequentNeighbor(aRule, entry.getValue()); } return newMapping; @@ -115,7 +116,7 @@ public void addToNewMapping(Map> newMapping, Map.Entry> getConsequentNeighbors(BaseRule aRule) { - return this.getConsequentNeighbors(aRule, MatchStrategy.NORMAL_LEVEL); + return this.getConsequentNeighbors(aRule, EnumSet.noneOf(MatchFlag.class)); } @@ -129,16 +130,16 @@ public Map> getConsequentNeighbors(BaseRule aRule) { * @return A mapping from a neighbor rulenode and the way its antecedent matches * this rule's consequent. */ - public Map> getConsequentNeighbors(BaseRule aRule, MatchStrategy aStrategy) { + public Map> getConsequentNeighbors(BaseRule aRule, EnumSet aConfig) { MatchNode aRuleNode = this.ruleToRuleNode.get(aRule); assert aRuleNode != null; - Map> newMapping = BaseRule.getMatches(aRule, this.getRules(), false, aStrategy); + Map> newMapping = BaseRule.getMatches(aRule, this.getRules(), false, aConfig); for (Map.Entry> entry : newMapping.entrySet()) { - aRuleNode.setConsequentNeighbor(entry.getKey(), Match.invertAll(entry.getValue()), aStrategy); - this.ruleToRuleNode.get(entry.getKey()).setAntecedentNeighbor(aRule, entry.getValue(), aStrategy); + aRuleNode.setConsequentNeighbor(entry.getKey(), Match.invertAll(entry.getValue())); + this.ruleToRuleNode.get(entry.getKey()).setAntecedentNeighbor(aRule, entry.getValue()); } return newMapping; @@ -201,8 +202,7 @@ public void printGraphVizCode(ReasonerPlan aPlan) { } - Map> antecedentNeighbors = this.getAntecedentNeighbors(r.getRule(), - aPlan != null ? aPlan.getMatchStrategy() : MatchStrategy.NORMAL_LEVEL); + Map> antecedentNeighbors = this.getAntecedentNeighbors(r.getRule()); Set anteNeigh = antecedentNeighbors.keySet(); String neighName; for (BaseRule neighR : anteNeigh) { 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 6373452c..44493068 100644 --- a/reasoner/src/test/java/eu/knowledge/engine/reasoner/BindingTest.java +++ b/reasoner/src/test/java/eu/knowledge/engine/reasoner/BindingTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import java.util.EnumSet; import java.util.HashSet; import java.util.Set; @@ -10,7 +11,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import eu.knowledge.engine.reasoner.BaseRule.MatchStrategy; +import eu.knowledge.engine.reasoner.BaseRule.MatchFlag; import eu.knowledge.engine.reasoner.api.Binding; import eu.knowledge.engine.reasoner.api.TriplePattern; @@ -60,7 +61,7 @@ public void testFruitfulVsActualMatches() { BaseRule r = new BaseRule("test", toTriplePattern(gp2), new HashSet<>()); - Set matches = r.antecedentMatches(toTriplePattern(gp1), MatchStrategy.ULTRA_LEVEL); + Set matches = r.antecedentMatches(toTriplePattern(gp1), EnumSet.of(MatchFlag.ONLY_BIGGEST)); LOG.info("matches size: {}", matches.size()); 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 df6c293d..fd517984 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 @@ -7,6 +7,7 @@ import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -20,7 +21,7 @@ import org.junit.jupiter.api.TestInstance.Lifecycle; import eu.knowledge.engine.reasoner.BaseRule; -import eu.knowledge.engine.reasoner.BaseRule.MatchStrategy; +import eu.knowledge.engine.reasoner.BaseRule.MatchFlag; import eu.knowledge.engine.reasoner.Match; import eu.knowledge.engine.reasoner.ProactiveRule; import eu.knowledge.engine.reasoner.Rule; @@ -54,7 +55,7 @@ public void testGPMatcher() { Rule r = new Rule(new HashSet<>(), rhs); - Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.ULTRA_LEVEL); + Set findMatchesWithConsequent = r.consequentMatches(obj, EnumSet.of(MatchFlag.ONLY_BIGGEST)); System.out.println(findMatchesWithConsequent); } @@ -69,7 +70,7 @@ public void testGPMatcher2() { Rule r = new Rule(new HashSet<>(), rhs); - Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.ULTRA_LEVEL); + Set findMatchesWithConsequent = r.consequentMatches(obj, EnumSet.of(MatchFlag.ONLY_BIGGEST)); System.out.println(findMatchesWithConsequent); } @@ -86,7 +87,7 @@ public void testGPMatcher3() { Rule r = new Rule(new HashSet<>(), rhs); - Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.ULTRA_LEVEL); + Set findMatchesWithConsequent = r.consequentMatches(obj, EnumSet.of(MatchFlag.ONLY_BIGGEST)); System.out.println(findMatchesWithConsequent); } @@ -100,7 +101,7 @@ public void testGPMatcher4() { Rule r = new Rule(new HashSet<>(), rhs); - Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.ULTRA_LEVEL); + Set findMatchesWithConsequent = r.consequentMatches(obj, EnumSet.of(MatchFlag.ONLY_BIGGEST)); // there should be a match, but its mapping should be empty nothing needs to // happen to translate one to the other. @@ -117,7 +118,7 @@ public void testGPMatcher5() { Rule r = new Rule(new HashSet<>(), rhs); - Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.ULTRA_LEVEL); + Set findMatchesWithConsequent = r.consequentMatches(obj, EnumSet.of(MatchFlag.ONLY_BIGGEST)); // there should be a match and its mapping should be empty because nothing needs // to happen to translate one to the other. @@ -137,7 +138,7 @@ public void testGPMatcher6() { Rule r = new Rule(new HashSet<>(), rhs); - Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.ULTRA_LEVEL); + Set findMatchesWithConsequent = r.consequentMatches(obj, EnumSet.of(MatchFlag.ONLY_BIGGEST)); System.out.println(findMatchesWithConsequent); } @@ -164,7 +165,7 @@ public void testGPMatcher7() { BaseRule r = new ProactiveRule(obj, new HashSet<>()); - Set findMatchesWithConsequent = r.antecedentMatches(obj, MatchStrategy.NORMAL_LEVEL); + Set findMatchesWithConsequent = r.antecedentMatches(obj, EnumSet.of(MatchFlag.ONLY_BIGGEST, MatchFlag.FULLY_COVERED)); System.out.println("Size: " + findMatchesWithConsequent.size()); for (Match m : findMatchesWithConsequent) { @@ -186,7 +187,7 @@ public void testGPMatcher8() { Rule r = new Rule(new HashSet<>(), tp2); - Set findMatchesWithConsequent = r.consequentMatches(tp1, MatchStrategy.ULTRA_LEVEL); + Set findMatchesWithConsequent = r.consequentMatches(tp1, EnumSet.of(MatchFlag.ONLY_BIGGEST)); System.out.println(findMatchesWithConsequent); } @@ -204,7 +205,7 @@ public void testGPMatcher9() { Rule r = new Rule(new HashSet<>(), tp2); - Set findMatchesWithConsequent = r.consequentMatches(tp1, MatchStrategy.ADVANCED_LEVEL); + Set findMatchesWithConsequent = r.consequentMatches(tp1, EnumSet.of(MatchFlag.ONLY_BIGGEST)); System.out.println(findMatchesWithConsequent); } @@ -268,8 +269,10 @@ public void testGPMatcher10OrderingWithinGraphPatternsShouldNotMatter() { Rule r = new Rule(new HashSet<>(), obj); Rule r2 = new Rule(new HashSet<>(), obj2); - Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.NORMAL_LEVEL); - Set findMatchesWithConsequent2 = r2.consequentMatches(obj2, MatchStrategy.NORMAL_LEVEL); + Set findMatchesWithConsequent = r.consequentMatches(obj, + EnumSet.of(MatchFlag.ONE_TO_ONE, MatchFlag.ONLY_BIGGEST)); + Set findMatchesWithConsequent2 = r2.consequentMatches(obj2, + EnumSet.of(MatchFlag.ONE_TO_ONE, MatchFlag.ONLY_BIGGEST)); System.out.println("Size 1: " + findMatchesWithConsequent.size()); System.out.println(findMatchesWithConsequent); @@ -301,8 +304,10 @@ public void testGPMatcher11VariableNamesMatter() { Rule r = new Rule(new HashSet<>(), obj); Rule r2 = new Rule(new HashSet<>(), obj2); - Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.NORMAL_LEVEL); - Set findMatchesWithConsequent2 = r2.consequentMatches(obj2, MatchStrategy.NORMAL_LEVEL); + Set findMatchesWithConsequent = r.consequentMatches(obj, + EnumSet.of(MatchFlag.ONE_TO_ONE, MatchFlag.ONLY_BIGGEST)); + Set findMatchesWithConsequent2 = r2.consequentMatches(obj2, + EnumSet.of(MatchFlag.ONE_TO_ONE, MatchFlag.ONLY_BIGGEST)); System.out.println("Size 1: " + findMatchesWithConsequent.size()); System.out.println(findMatchesWithConsequent); @@ -335,7 +340,7 @@ public void testGPMatcher12() { }); Set findMatchesWithAntecedent = r.antecedentMatches( - new HashSet<>(Arrays.asList(/* t1, */ t5, t9, t8, t7, t6, t4, t3)), MatchStrategy.ADVANCED_LEVEL); + new HashSet<>(Arrays.asList(/* t1, */ t5, t9, t8, t7, t6, t4, t3)), EnumSet.of(MatchFlag.ONLY_BIGGEST, MatchFlag.FULLY_COVERED)); System.out.println("Size: " + findMatchesWithAntecedent.size()); // System.out.println(findMatchesWithConsequent); @@ -423,7 +428,7 @@ public void testGPMatcherCardinalityTest() { Rule r = new Rule(new HashSet<>(), obj); Set findMatchesWithConsequent = r.consequentMatches(new HashSet<>(Arrays.asList(graphPattern)), - MatchStrategy.SUPREME_LEVEL); + EnumSet.noneOf(MatchFlag.class)); System.out.println("graph pattern size " + gpSize + " gives matches size " + findMatchesWithConsequent.size() + "-" + getNumberOfMatches(gpSize)); @@ -473,7 +478,7 @@ public void testBugTranslate() { Rule r = new Rule(new HashSet<>(), rhs); - Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.ULTRA_LEVEL); + Set findMatchesWithConsequent = r.consequentMatches(obj, EnumSet.noneOf(MatchFlag.class)); BindingSet bs = Util.toBindingSet("s=,p=

,o="); @@ -496,7 +501,7 @@ public void testOtherBurgTranslate() { Rule r = new Rule(new HashSet<>(), rhs); - Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.ULTRA_LEVEL); + Set findMatchesWithConsequent = r.consequentMatches(obj, EnumSet.noneOf(MatchFlag.class)); BindingSet bs = Util.toBindingSet("p=,q=\"22.0\"^^"); @@ -589,7 +594,7 @@ public CompletableFuture handle(BindingSet aBindingSet) { System.out.println("NrOfMatches with " + obj.size() + " triple patterns: " + getNumberOfMatches(obj.size())); - Set findMatchesWithAntecedent = r.antecedentMatches(obj, MatchStrategy.ADVANCED_LEVEL); + Set findMatchesWithAntecedent = r.antecedentMatches(obj, EnumSet.of(MatchFlag.ONLY_BIGGEST, MatchFlag.FULLY_COVERED)); System.out.println("Size: " + findMatchesWithAntecedent.size()); @@ -661,7 +666,7 @@ public void testPloutosGPMatcher2() { System.out.println("NrOfMatches with " + obj.size() + " triple patterns: " + getNumberOfMatches(obj.size())); var findMatchesWithConsequent = BaseRule.getMatches(r1, new HashSet<>(Arrays.asList(r2)), true, - MatchStrategy.NORMAL_LEVEL); + EnumSet.of(MatchFlag.ONE_TO_ONE, MatchFlag.ONLY_BIGGEST, MatchFlag.FULLY_COVERED)); System.out.println("Size: " + findMatchesWithConsequent.size()); assertEquals(1, findMatchesWithConsequent.size()); @@ -682,7 +687,8 @@ public void testNewGPMatcher1() { BaseRule r2 = new Rule(new HashSet<>(), tp2); - var matches = BaseRule.getMatches(r1, new HashSet<>(Arrays.asList(r2)), true, MatchStrategy.NORMAL_LEVEL); + var matches = BaseRule.getMatches(r1, new HashSet<>(Arrays.asList(r2)), true, + EnumSet.of(MatchFlag.ONE_TO_ONE, MatchFlag.ONLY_BIGGEST)); System.out.println(matches); @@ -702,7 +708,7 @@ public void testNewGPMatcherTransitivity() { BaseRule r2 = new Rule(new HashSet<>(), tp2); - var matches = BaseRule.getMatches(r1, new HashSet<>(Arrays.asList(r2)), true, MatchStrategy.ULTRA_LEVEL); + var matches = BaseRule.getMatches(r1, new HashSet<>(Arrays.asList(r2)), true, EnumSet.noneOf(MatchFlag.class)); System.out.println(matches); assertEquals(1, matches.size()); @@ -723,7 +729,7 @@ public void testNewGPMatcher2() { BaseRule r2 = new Rule(new HashSet<>(), tp2); - var matches = BaseRule.getMatches(r1, new HashSet<>(Arrays.asList(r2)), true, MatchStrategy.ULTRA_LEVEL); + var matches = BaseRule.getMatches(r1, new HashSet<>(Arrays.asList(r2)), true, EnumSet.noneOf(MatchFlag.class)); System.out.println(matches); assertEquals(1, matches.size()); diff --git a/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/KnowledgeInteraction.java b/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/KnowledgeInteraction.java index eb324c86..86e6cdf9 100644 --- a/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/KnowledgeInteraction.java +++ b/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/KnowledgeInteraction.java @@ -1,7 +1,5 @@ package eu.knowledge.engine.smartconnector.api; -import org.apache.jena.ext.xerces.impl.xpath.regex.RegularExpression; - /** * A {@link KnowledgeInteraction} represents an agreement about the exchange of * knowledge between the {@link SmartConnectorImpl} and the diff --git a/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/InteractionProcessorImpl.java b/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/InteractionProcessorImpl.java index 2acfa44a..be08b3c0 100644 --- a/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/InteractionProcessorImpl.java +++ b/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/InteractionProcessorImpl.java @@ -31,7 +31,6 @@ import org.slf4j.Logger; import eu.knowledge.engine.reasoner.Rule; -import eu.knowledge.engine.reasoner.BaseRule.MatchStrategy; import eu.knowledge.engine.smartconnector.api.AnswerExchangeInfo; import eu.knowledge.engine.smartconnector.api.AnswerKnowledgeInteraction; import eu.knowledge.engine.smartconnector.api.AskPlan; @@ -135,7 +134,7 @@ public AskPlan planAskFromKnowledgeBase(MyKnowledgeInteractionInfo anAKI, Recipi } else { processor = new ReasonerProcessor(otherKnowledgeInteractions, messageRouter, this.additionalDomainKnowledge); - ((ReasonerProcessor) processor).setDefaultReasoningStrategy(MatchStrategy.ENTRY_LEVEL); + ((ReasonerProcessor) processor).setMatchStrategy(ReasonerProcessor.MatchStrategy.ENTRY_LEVEL); } // give the caller something to chew on while it waits. This method starts the @@ -305,7 +304,7 @@ public PostPlan planPostFromKnowledgeBase(MyKnowledgeInteractionInfo aPKI, Recip } else { processor = new ReasonerProcessor(otherKnowledgeInteractions, this.messageRouter, this.additionalDomainKnowledge); - ((ReasonerProcessor) processor).setDefaultReasoningStrategy(MatchStrategy.ENTRY_LEVEL); + ((ReasonerProcessor) processor).setMatchStrategy(ReasonerProcessor.MatchStrategy.ENTRY_LEVEL); } // give the caller something to chew on while it waits. This method starts the // interaction process as far as it can until it is blocked because it waits for diff --git a/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/ReasonerProcessor.java b/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/ReasonerProcessor.java index 4dc05bb6..1d481fdb 100644 --- a/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/ReasonerProcessor.java +++ b/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/ReasonerProcessor.java @@ -3,6 +3,7 @@ import java.io.IOException; import java.time.Instant; import java.util.Collections; +import java.util.EnumSet; import java.util.HashSet; import java.util.Iterator; import java.util.Map; @@ -22,7 +23,7 @@ import org.slf4j.helpers.MessageFormatter; import eu.knowledge.engine.reasoner.BaseRule; -import eu.knowledge.engine.reasoner.BaseRule.MatchStrategy; +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; @@ -66,13 +67,59 @@ public class ReasonerProcessor extends SingleInteractionProcessor { private static final Logger LOG = LoggerFactory.getLogger(ReasonerProcessor.class); + // the match strategy determines how many matches will be found between graph + // patterns. This is a very costly operation (especially with bigger (10+) + // triples per graph pattern. For some use cases it suffices to use ENTRY_LEVEL + // matching which is much faster, while others require SUPREME_LEVEL, which is + // extremely slow and often throws out of memory exceptions. + public static enum MatchStrategy { + SUPREME_LEVEL, ULTRA_LEVEL, ADVANCED_LEVEL, NORMAL_LEVEL, ENTRY_LEVEL; + + private EnumSet toConfig(boolean antecedentOfTarget) { + EnumSet config; + switch (this) { + case ENTRY_LEVEL: + config = EnumSet.of(MatchFlag.ONE_TO_ONE, MatchFlag.ONLY_BIGGEST, MatchFlag.SINGLE_RULE); + + // disable fully covered when matching consequents. + if (antecedentOfTarget) + config.add(MatchFlag.FULLY_COVERED); + break; + case NORMAL_LEVEL: + config = EnumSet.of(MatchFlag.ONE_TO_ONE, MatchFlag.ONLY_BIGGEST); + + // disable fully covered when matching consequents. + if (antecedentOfTarget) + config.add(MatchFlag.FULLY_COVERED); + break; + case ADVANCED_LEVEL: + config = EnumSet.of(MatchFlag.ONLY_BIGGEST); + + // disable fully covered when matching consequents. + if (antecedentOfTarget) + config.add(MatchFlag.FULLY_COVERED); + break; + case ULTRA_LEVEL: + config = EnumSet.of(MatchFlag.ONLY_BIGGEST); + break; + case SUPREME_LEVEL: + config = EnumSet.noneOf(MatchFlag.class); + break; + default: + config = EnumSet.noneOf(MatchFlag.class); + } + return config; + } + } + private RuleStore store; private MyKnowledgeInteractionInfo myKnowledgeInteraction; private final Set askExchangeInfos; private final Set postExchangeInfos; private Set additionalDomainKnowledge; private ReasonerPlan reasonerPlan; - private MatchStrategy defaultStrategy = MatchStrategy.NORMAL_LEVEL; + + private MatchStrategy matchStrategy = MatchStrategy.NORMAL_LEVEL; /** * These two bindingset handler are a bit dodgy. We need them to make the post @@ -146,8 +193,10 @@ public void planAskInteraction(MyKnowledgeInteractionInfo aAKI) { ProactiveRule aRule = new ProactiveRule(ruleName, translateGraphPatternTo(aki.getPattern()), new HashSet<>()); this.store.addRule(aRule); - this.reasonerPlan = new ReasonerPlan(this.store, aRule, - ki.includeMetaKIs() ? MatchStrategy.ENTRY_LEVEL : this.defaultStrategy); + + EnumSet config = this.matchStrategy.toConfig(true); + + this.reasonerPlan = new ReasonerPlan(this.store, aRule, config); } else { LOG.warn("Type should be Ask, not {}", this.myKnowledgeInteraction.getType()); this.finalBindingSetFuture.complete(new eu.knowledge.engine.reasoner.api.BindingSet()); @@ -212,8 +261,9 @@ public void planPostInteraction(MyKnowledgeInteractionInfo aPKI) { ProactiveRule aRule = new ProactiveRule(ruleName, new HashSet<>(), new HashSet<>(translatedGraphPattern)); store.addRule(aRule); - this.reasonerPlan = new ReasonerPlan(this.store, aRule, - pki.includeMetaKIs() ? MatchStrategy.ENTRY_LEVEL : this.defaultStrategy); + EnumSet config = this.matchStrategy.toConfig(false); + + this.reasonerPlan = new ReasonerPlan(this.store, aRule, config); } else { LOG.warn("Type should be Post, not {}", this.myKnowledgeInteraction.getType()); @@ -607,7 +657,7 @@ public ReasonerPlan getReasonerPlan() { return this.reasonerPlan; } - public void setDefaultReasoningStrategy(MatchStrategy aStrategy) { - this.defaultStrategy = aStrategy; + public void setMatchStrategy(MatchStrategy aStrategy) { + this.matchStrategy = aStrategy; } } diff --git a/smart-connector/src/test/java/eu/knowledge/engine/smartconnector/api/TestDynamicSemanticComposition.java b/smart-connector/src/test/java/eu/knowledge/engine/smartconnector/api/TestDynamicSemanticComposition.java index 81fad1de..4b096693 100644 --- a/smart-connector/src/test/java/eu/knowledge/engine/smartconnector/api/TestDynamicSemanticComposition.java +++ b/smart-connector/src/test/java/eu/knowledge/engine/smartconnector/api/TestDynamicSemanticComposition.java @@ -7,6 +7,7 @@ import java.net.URISyntaxException; import java.util.ArrayList; +import java.util.EnumSet; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -27,7 +28,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import eu.knowledge.engine.reasoner.BaseRule.MatchStrategy; +import eu.knowledge.engine.reasoner.BaseRule.MatchFlag; import eu.knowledge.engine.reasoner.Match; import eu.knowledge.engine.reasoner.ReasonerPlan; import eu.knowledge.engine.reasoner.Rule; @@ -202,7 +203,7 @@ private void updateNetwork(Set> gaps) { Rule r = new Rule(translateGraphPatternTo(ki.getArgument()), translateGraphPatternTo(ki.getResult())); - Set matches = r.consequentMatches(gap, MatchStrategy.ADVANCED_LEVEL); + Set matches = r.consequentMatches(gap, EnumSet.of(MatchFlag.ONLY_BIGGEST)); if (!matches.isEmpty()) { kn.addKB(kb); From fe3282fdf809c7ad12a024f615eb5c9a714e2e52 Mon Sep 17 00:00:00 2001 From: Barry Nouwt Date: Wed, 11 Sep 2024 15:57:35 +0200 Subject: [PATCH 2/2] Made the matching strategy available through the Java API. Made sure Meta KIs always use ENTRY_LEVEL match strategy. --- .../knowledge/engine/reasoner/BaseRule.java | 4 +- .../engine/reasoner/rulestore/RuleStore.java | 4 +- .../api/AnswerKnowledgeInteraction.java | 35 +++---- .../api/AskKnowledgeInteraction.java | 29 +++--- .../api/KnowledgeInteraction.java | 63 ++++++++---- .../smartconnector/api/MatchStrategy.java | 49 +++++++++ .../api/PostKnowledgeInteraction.java | 45 +++++---- .../api/ReactKnowledgeInteraction.java | 42 ++++---- .../impl/InteractionProcessorImpl.java | 5 +- .../impl/MetaKnowledgeBaseImpl.java | 99 +++++++++++-------- .../impl/ReasonerProcessor.java | 58 +++-------- .../smartconnector/util/KnowledgeNetwork.java | 6 +- .../api/TestComplexGraphPatternMatching.java | 3 +- 13 files changed, 250 insertions(+), 192 deletions(-) create mode 100644 smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/MatchStrategy.java 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 85a69850..d3108c42 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/BaseRule.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/BaseRule.java @@ -410,8 +410,8 @@ public static Map> getMatches(BaseRule aTargetRule, Set value = triplePatternMatchesIter.next().getValue(); - LOG.trace("{}/{} ({}): biggest: {}, smaller: {}", 1, combiMatchesPerTriple.size(), value.size(), - biggestMatches.size(), smallerMatches.size()); + LOG.trace("{}/{} ({}): biggest: {}, smaller: {} ({})", 1, combiMatchesPerTriple.size(), value.size(), + biggestMatches.size(), smallerMatches.size(), config); biggestMatches.addAll(value); } 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 547233e6..31994313 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 @@ -201,8 +201,8 @@ public void printGraphVizCode(ReasonerPlan aPlan) { ruleToName.put(r.getRule(), currentName); } - - Map> antecedentNeighbors = this.getAntecedentNeighbors(r.getRule()); + + Map> antecedentNeighbors = r.getAntecedentNeighbors(); Set anteNeigh = antecedentNeighbors.keySet(); String neighName; for (BaseRule neighR : anteNeigh) { diff --git a/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/AnswerKnowledgeInteraction.java b/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/AnswerKnowledgeInteraction.java index 6282a316..0992ae1d 100644 --- a/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/AnswerKnowledgeInteraction.java +++ b/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/AnswerKnowledgeInteraction.java @@ -16,36 +16,33 @@ public final class AnswerKnowledgeInteraction extends KnowledgeInteraction { */ private final GraphPattern pattern; - /** - * Create an {@link AnswerKnowledgeInteraction}. - * - * @param act The {@link CommunicativeAct} of this - * {@link KnowledgeInteraction}. It can be read as the 'goal' or - * 'purpose' of the data exchange and whether it has side-effects - * or not. - * @param pattern The {@link GraphPattern} expresses the 'shape' of knowledge - * that this {@link KnowledgeInteraction} can provide. - */ public AnswerKnowledgeInteraction(CommunicativeAct act, GraphPattern pattern) { - this(act, pattern, null, false, false); + this(act, pattern, null, false, false, null); } public AnswerKnowledgeInteraction(CommunicativeAct act, GraphPattern pattern, String name) { - this(act, pattern, name, false, false); + this(act, pattern, name, false, false, null); } - public AnswerKnowledgeInteraction(CommunicativeAct anAct, GraphPattern aPattern, boolean anIsFullMatch) { - this(anAct, aPattern, null, false, anIsFullMatch); + public AnswerKnowledgeInteraction(CommunicativeAct anAct, GraphPattern aPattern, boolean anIncludeMetaKIs) { + this(anAct, aPattern, null, false, anIncludeMetaKIs, null); } public AnswerKnowledgeInteraction(CommunicativeAct anAct, GraphPattern aPattern, boolean anIsMeta, - boolean anIsFullMatch) { - this(anAct, aPattern, null, anIsMeta, anIsFullMatch); + boolean anIncludeMetaKIs) { + this(anAct, aPattern, null, anIsMeta, anIncludeMetaKIs, null); } - public AnswerKnowledgeInteraction(CommunicativeAct anAct, GraphPattern aPattern, String name, boolean anIsMeta, - boolean anIsFullMatch) { - super(anAct, name, anIsMeta, anIsFullMatch); + /** + * Create an {@link AnswerKnowledgeInteraction}. See + * {@link KnowledgeInteraction#KnowledgeInteraction(CommunicativeAct, String, boolean, boolean, MatchStrategy) + * + * @param aPattern The {@link GraphPattern} expresses the 'shape' of knowledge + * that this {@link KnowledgeInteraction} can provide. + */ + public AnswerKnowledgeInteraction(CommunicativeAct anAct, GraphPattern aPattern, String aName, boolean anIsMeta, + boolean anIncludeMetaKIs, MatchStrategy aMatchStrategy) { + super(anAct, aName, anIsMeta, anIncludeMetaKIs, aMatchStrategy); this.pattern = aPattern; } diff --git a/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/AskKnowledgeInteraction.java b/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/AskKnowledgeInteraction.java index 1f680cdc..8fc7e2f2 100644 --- a/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/AskKnowledgeInteraction.java +++ b/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/AskKnowledgeInteraction.java @@ -16,36 +16,33 @@ public final class AskKnowledgeInteraction extends KnowledgeInteraction { */ private final GraphPattern pattern; - /** - * Create a {@link AskKnowledgeInteraction}. - * - * @param act The {@link CommunicativeAct} of this - * {@link KnowledgeInteraction}. It can be read as the 'goal' or - * 'purpose' of the data exchange and whether it has side-effects - * or not. - * @param pattern The {@link GraphPattern} expresses the 'shape' of knowledge - * that this {@link KnowledgeInteraction} asks for. - */ public AskKnowledgeInteraction(CommunicativeAct act, GraphPattern pattern) { - this(act, pattern, null, false, false); + this(act, pattern, null, false, false, null); } public AskKnowledgeInteraction(CommunicativeAct act, GraphPattern pattern, String name) { - this(act, pattern, name, false, false); + this(act, pattern, name, false, false, null); } public AskKnowledgeInteraction(CommunicativeAct act, GraphPattern pattern, boolean anIncludeMetaKIs) { - this(act, pattern, null, false, anIncludeMetaKIs); + this(act, pattern, null, false, anIncludeMetaKIs, null); } public AskKnowledgeInteraction(CommunicativeAct act, GraphPattern pattern, boolean anisMeta, boolean anIncludeMetaKIs) { - this(act, pattern, null, anisMeta, anIncludeMetaKIs); + this(act, pattern, null, anisMeta, anIncludeMetaKIs, null); } + /** + * Create a {@link AskKnowledgeInteraction}. See + * {@link KnowledgeInteraction#KnowledgeInteraction(CommunicativeAct, String, boolean, boolean, MatchStrategy) + * + * @param pattern The {@link GraphPattern} expresses the 'shape' of knowledge + * that this {@link KnowledgeInteraction} asks for. + */ public AskKnowledgeInteraction(CommunicativeAct act, GraphPattern pattern, String name, boolean anisMeta, - boolean anIncludeMetaKIs) { - super(act, name, anisMeta, anIncludeMetaKIs); + boolean anIncludeMetaKIs, MatchStrategy aMatchStrategy) { + super(act, name, anisMeta, anIncludeMetaKIs, aMatchStrategy); this.pattern = pattern; } diff --git a/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/KnowledgeInteraction.java b/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/KnowledgeInteraction.java index 86e6cdf9..fb8b3ae0 100644 --- a/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/KnowledgeInteraction.java +++ b/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/KnowledgeInteraction.java @@ -1,5 +1,7 @@ package eu.knowledge.engine.smartconnector.api; +import com.fasterxml.jackson.core.format.MatchStrength; + /** * A {@link KnowledgeInteraction} represents an agreement about the exchange of * knowledge between the {@link SmartConnectorImpl} and the @@ -14,7 +16,7 @@ public abstract class KnowledgeInteraction { * the intent/purpose or goal of this interaction and whether it has * side-effects. */ - private final CommunicativeAct act; + private final CommunicativeAct anAct; /** * When executing this knowledge interaction, should the meta knowledge @@ -30,6 +32,17 @@ public abstract class KnowledgeInteraction { */ private final boolean isMeta; + /** + * Optionally configure the matching strategy (which has a large impact on the + * performance) to a different level than the one configured at the smart + * connector level. + * + * For example, this configuration option is used to set the matching process on + * all internal (meta) KIs to {@link MatchStrategy#ENTRY_LEVEL}, because we can + * make certain assumptions to increase the performance. + */ + private MatchStrategy matchStrategy = null; + protected final String name; /** @@ -40,33 +53,45 @@ public abstract class KnowledgeInteraction { * whether it has side-effects or not. */ public KnowledgeInteraction(CommunicativeAct act) { - this(act, null, false, false); + this(act, null, false, false, null); } - /** - * Create a {@link KnowledgeInteraction}. - * - * @param act The {@link CommunicativeAct} of this - * {@link KnowledgeInteraction}. It can be read as the 'goal' or - * 'purpose' of the data exchange and whether it has side-effects - * or not. - * @param isMeta Whether or not this knowledge interaction contains metadata - * about the knowledge base itself. - */ public KnowledgeInteraction(CommunicativeAct act, boolean isMeta) { - this(act, null, isMeta, false); + this(act, null, isMeta, false, null); } public KnowledgeInteraction(CommunicativeAct act, boolean isMeta, boolean anIncludeMetaKIs) { - this(act, null, isMeta, anIncludeMetaKIs); + this(act, null, isMeta, anIncludeMetaKIs, null); } - public KnowledgeInteraction(CommunicativeAct act, String name, boolean isMeta, boolean anIncludeMetaKIs) { + /** + * Create a {@link KnowledgeInteraction}. + * + * @param act The {@link CommunicativeAct} of this + * {@link KnowledgeInteraction}. It can be read as the + * 'goal' or 'purpose' of the data exchange and whether + * it has side-effects or not. + * @param name An optional name for the KI (that also influences and + * stabilize the URI). + * @param isMeta Whether or not this knowledge interaction contains + * metadata about the knowledge base itself. + * @param anIncludeMetaKIs When processing this knowledge interaction, do we + * need to include the meta KIs of the SCs within the + * network? This needs to be set to {@code true} if this + * KI relies on exchanging KE metadata. + * @param aMatchConfig Optionally configure the match strategy that will be + * used when executing this KI. If set to {@code null} + * the match strategy configured at the SC level will be + * used. + */ + public KnowledgeInteraction(CommunicativeAct act, String name, boolean isMeta, boolean anIncludeMetaKIs, + MatchStrategy aMatchConfig) { this.validateName(name); - this.act = act; + this.anAct = act; this.name = name; this.isMeta = isMeta; this.includeMetaKIs = anIncludeMetaKIs; + this.matchStrategy = aMatchConfig; } public String getName() { @@ -77,7 +102,7 @@ public String getName() { * @return The {@link CommunicativeAct} of this {@link KnowledgeInteraction}. */ public CommunicativeAct getAct() { - return this.act; + return this.anAct; } public boolean isMeta() { @@ -88,6 +113,10 @@ public boolean includeMetaKIs() { return this.includeMetaKIs; } + public MatchStrategy getMatchStrategy() { + return this.matchStrategy; + } + /** * Throws an exception if {@code name} does not conform to the requirements of * knowledge interaction names. diff --git a/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/MatchStrategy.java b/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/MatchStrategy.java new file mode 100644 index 00000000..7a3c65f2 --- /dev/null +++ b/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/MatchStrategy.java @@ -0,0 +1,49 @@ +package eu.knowledge.engine.smartconnector.api; + +import java.util.EnumSet; + +import eu.knowledge.engine.reasoner.BaseRule.MatchFlag; + +/** + * the match strategy determines how many matches will be found between graph + * patterns. This is a very costly operation (especially with bigger (10+) + * triples per graph pattern. For some use cases it suffices to use ENTRY_LEVEL + * matching which is much faster, while others require SUPREME_LEVEL, which is + * extremely slow and often throws out of memory exceptions. + */ +public enum MatchStrategy { + SUPREME_LEVEL, ULTRA_LEVEL, ADVANCED_LEVEL, NORMAL_LEVEL, ENTRY_LEVEL; + + public EnumSet toConfig(boolean antecedentOfTarget) { + EnumSet config; + switch (this) { + case ENTRY_LEVEL: + config = EnumSet.of(MatchFlag.ONE_TO_ONE, MatchFlag.ONLY_BIGGEST, MatchFlag.SINGLE_RULE, + MatchFlag.FULLY_COVERED); + break; + case NORMAL_LEVEL: + config = EnumSet.of(MatchFlag.ONE_TO_ONE, MatchFlag.ONLY_BIGGEST); + + // disable fully covered when matching consequents. + if (antecedentOfTarget) + config.add(MatchFlag.FULLY_COVERED); + break; + case ADVANCED_LEVEL: + config = EnumSet.of(MatchFlag.ONLY_BIGGEST); + + // disable fully covered when matching consequents. + if (antecedentOfTarget) + config.add(MatchFlag.FULLY_COVERED); + break; + case ULTRA_LEVEL: + config = EnumSet.of(MatchFlag.ONLY_BIGGEST); + break; + case SUPREME_LEVEL: + config = EnumSet.noneOf(MatchFlag.class); + break; + default: + config = EnumSet.noneOf(MatchFlag.class); + } + return config; + } +} \ No newline at end of file diff --git a/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/PostKnowledgeInteraction.java b/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/PostKnowledgeInteraction.java index 98b8493e..17625d06 100644 --- a/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/PostKnowledgeInteraction.java +++ b/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/PostKnowledgeInteraction.java @@ -22,43 +22,42 @@ public final class PostKnowledgeInteraction extends KnowledgeInteraction { */ private final GraphPattern result; - /** - * Create a {@link PostKnowledgeInteraction}. - * - * @param act The {@link CommunicativeAct} of this - * {@link KnowledgeInteraction}. It can be read as the 'goal' or - * 'purpose' of the data exchange and whether it has - * side-effects or not. - * @param argument The {@code argument} of this - * {@link PostKnowledgeInteraction}. It can be seen as the - * argument of a function call. - * @param result The {@code result} of this {@link PostKnowledgeInteraction}. - * It can be seen as the result of a function call. Can be - * {@code null} if this interaction does not expect any result. - * @apiNote TODO Can {@code argument} also be {@code null}? Note that not both - * {@code argument} and {@code result} can be {@code null}. - */ public PostKnowledgeInteraction(CommunicativeAct act, GraphPattern argument, GraphPattern result) { - this(act, argument, result, null, false, false); + this(act, argument, result, null, false, false, null); } public PostKnowledgeInteraction(CommunicativeAct act, GraphPattern argument, GraphPattern result, String name) { - this(act, argument, result, name, false, false); + this(act, argument, result, name, false, false, null); } public PostKnowledgeInteraction(CommunicativeAct act, GraphPattern argument, GraphPattern result, boolean anIncludeMetaKIs) { - this(act, argument, result, null, false, anIncludeMetaKIs); + this(act, argument, result, null, false, anIncludeMetaKIs, null); } public PostKnowledgeInteraction(CommunicativeAct act, GraphPattern argument, GraphPattern result, boolean anIsMeta, boolean anIncludeMetaKIs) { - this(act, argument, result, null, anIsMeta, anIncludeMetaKIs); + this(act, argument, result, null, anIsMeta, anIncludeMetaKIs, null); } - public PostKnowledgeInteraction(CommunicativeAct act, GraphPattern argument, GraphPattern result, String name, boolean anIsMeta, - boolean anIncludeMetaKIs) { - super(act, name, anIsMeta, anIncludeMetaKIs); + /** + * Create a {@link PostKnowledgeInteraction}. See + * {@link KnowledgeInteraction#KnowledgeInteraction(CommunicativeAct, String, boolean, boolean, MatchStrategy)} + * + * + * @param argument The {@code argument} of this + * {@link PostKnowledgeInteraction}. It can be seen as the + * argument of a function call. + * @param result The {@code result} of this {@link PostKnowledgeInteraction}. + * It can be seen as the result of a function call. Can be + * {@code null} if this interaction does not expect any result. + * @apiNote Can {@code argument} also be {@code null}? No, only the + * {@code result} graph pattern can be {@code null}. Note that not both + * {@code argument} and {@code result} can be {@code null}. + */ + public PostKnowledgeInteraction(CommunicativeAct act, GraphPattern argument, GraphPattern result, String name, + boolean anIsMeta, boolean anIncludeMetaKIs, MatchStrategy aMatchStrategy) { + super(act, name, anIsMeta, anIncludeMetaKIs, aMatchStrategy); this.argument = argument; this.result = result; } diff --git a/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/ReactKnowledgeInteraction.java b/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/ReactKnowledgeInteraction.java index 06cbd373..aa9951ba 100644 --- a/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/ReactKnowledgeInteraction.java +++ b/smart-connector-api/src/main/java/eu/knowledge/engine/smartconnector/api/ReactKnowledgeInteraction.java @@ -19,42 +19,40 @@ public final class ReactKnowledgeInteraction extends KnowledgeInteraction { */ private final GraphPattern result; - /** - * Creates a {@link ReactKnowledgeInteraction}. - * - * @param act The {@link CommunicativeAct} of this - * {@link KnowledgeInteraction}. It can be read as the 'goal' or - * 'purpose' of the data exchange and whether it has - * side-effects or not. - * @param argument The {@code argument} of this {@link KnowledgeInteraction}. It - * can be seen as the argument of a function call. - * @param result The {@code result} of this {@link PostKnowledgeInteraction}. - * It can be seen as the result of a function call. Can be - * {@code null} if this interaction does not expect any result. - * @apiNote TODO Can {@code argument} also be {@code null}? Note that not both - * {@code argument} and {@code result} can be {@code null}. - */ public ReactKnowledgeInteraction(CommunicativeAct act, GraphPattern argument, GraphPattern result) { - this(act, argument, result, null, false, false); + this(act, argument, result, null, false, false, null); } public ReactKnowledgeInteraction(CommunicativeAct act, GraphPattern argument, GraphPattern result, String name) { - this(act, argument, result, name, false, false); + this(act, argument, result, name, false, false, null); } public ReactKnowledgeInteraction(CommunicativeAct act, GraphPattern argument, GraphPattern result, boolean anIsFullMatch) { - this(act, argument, result, null, false, anIsFullMatch); + this(act, argument, result, null, false, anIsFullMatch, null); } public ReactKnowledgeInteraction(CommunicativeAct act, GraphPattern argument, GraphPattern result, boolean anIsMeta, boolean anIsFullMatch) { - this(act, argument, result, null, anIsMeta, anIsFullMatch); + this(act, argument, result, null, anIsMeta, anIsFullMatch, null); } - public ReactKnowledgeInteraction(CommunicativeAct act, GraphPattern argument, GraphPattern result, String name, boolean anIsMeta, - boolean anIsFullMatch) { - super(act, name, anIsMeta, anIsFullMatch); + /** + * Creates a {@link ReactKnowledgeInteraction}. See + * {@link KnowledgeInteraction#KnowledgeInteraction(CommunicativeAct, String, boolean, boolean, MatchStrategy)} + * + * @param argument The {@code argument} of this {@link KnowledgeInteraction}. It + * can be seen as the argument of a function call. + * @param result The {@code result} of this {@link PostKnowledgeInteraction}. + * It can be seen as the result of a function call. Can be + * {@code null} if this interaction does not expect any result. + * @apiNote TODO Can {@code argument} also be {@code null}? No, only the + * {@code result} graph pattern can be {@code null}. Note that not both + * {@code argument} and {@code result} can be {@code null}. + */ + public ReactKnowledgeInteraction(CommunicativeAct act, GraphPattern argument, GraphPattern result, String name, + boolean anIsMeta, boolean anIsFullMatch, MatchStrategy aMatchStrategy) { + super(act, name, anIsMeta, anIsFullMatch, aMatchStrategy); this.argument = argument; this.result = result; } diff --git a/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/InteractionProcessorImpl.java b/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/InteractionProcessorImpl.java index be08b3c0..3a9e7d04 100644 --- a/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/InteractionProcessorImpl.java +++ b/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/InteractionProcessorImpl.java @@ -39,6 +39,7 @@ import eu.knowledge.engine.smartconnector.api.BindingValidator; import eu.knowledge.engine.smartconnector.api.CommunicativeAct; import eu.knowledge.engine.smartconnector.api.GraphPattern; +import eu.knowledge.engine.smartconnector.api.MatchStrategy; import eu.knowledge.engine.smartconnector.api.PostPlan; import eu.knowledge.engine.smartconnector.api.ReactExchangeInfo; import eu.knowledge.engine.smartconnector.api.ReactKnowledgeInteraction; @@ -134,7 +135,7 @@ public AskPlan planAskFromKnowledgeBase(MyKnowledgeInteractionInfo anAKI, Recipi } else { processor = new ReasonerProcessor(otherKnowledgeInteractions, messageRouter, this.additionalDomainKnowledge); - ((ReasonerProcessor) processor).setMatchStrategy(ReasonerProcessor.MatchStrategy.ENTRY_LEVEL); + ((ReasonerProcessor) processor).setMatchStrategy(MatchStrategy.ENTRY_LEVEL); } // give the caller something to chew on while it waits. This method starts the @@ -304,7 +305,7 @@ public PostPlan planPostFromKnowledgeBase(MyKnowledgeInteractionInfo aPKI, Recip } else { processor = new ReasonerProcessor(otherKnowledgeInteractions, this.messageRouter, this.additionalDomainKnowledge); - ((ReasonerProcessor) processor).setMatchStrategy(ReasonerProcessor.MatchStrategy.ENTRY_LEVEL); + ((ReasonerProcessor) processor).setMatchStrategy(MatchStrategy.ENTRY_LEVEL); } // give the caller something to chew on while it waits. This method starts the // interaction process as far as it can until it is blocked because it waits for diff --git a/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/MetaKnowledgeBaseImpl.java b/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/MetaKnowledgeBaseImpl.java index 6f54b517..f2a73dc3 100644 --- a/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/MetaKnowledgeBaseImpl.java +++ b/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/MetaKnowledgeBaseImpl.java @@ -40,6 +40,7 @@ import eu.knowledge.engine.smartconnector.api.CommunicativeAct; import eu.knowledge.engine.smartconnector.api.GraphPattern; import eu.knowledge.engine.smartconnector.api.KnowledgeEngineRuntimeException; +import eu.knowledge.engine.smartconnector.api.MatchStrategy; import eu.knowledge.engine.smartconnector.api.PostKnowledgeInteraction; import eu.knowledge.engine.smartconnector.api.PostResult; import eu.knowledge.engine.smartconnector.api.ReactKnowledgeInteraction; @@ -86,47 +87,52 @@ public MetaKnowledgeBaseImpl(LoggerProvider loggerProvider, MessageRouter aMessa "?ki rdf:type ?kiType .", "?ki kb:isMeta ?isMeta .", "?ki kb:hasCommunicativeAct ?act .", "?act rdf:type kb:CommunicativeAct .", "?act kb:hasRequirement ?req .", "?act kb:hasSatisfaction ?sat .", "?req rdf:type ?reqType .", "?sat rdf:type ?satType .", - "?ki kb:hasGraphPattern ?gp .", "?gp rdf:type ?patternType .", - "?gp kb:hasPattern ?pattern ."); + "?ki kb:hasGraphPattern ?gp .", "?gp rdf:type ?patternType .", "?gp kb:hasPattern ?pattern ."); - this.metaAnswerKI = new AnswerKnowledgeInteraction(new CommunicativeAct(), this.metaGraphPattern, true, true); - this.knowledgeBaseStore.register(this.metaAnswerKI, (anAKI, anAnswerExchangeInfo) -> this.fillMetaBindings(anAnswerExchangeInfo.getIncomingBindings()), + this.metaAnswerKI = new AnswerKnowledgeInteraction(new CommunicativeAct(), this.metaGraphPattern, null, true, + true, MatchStrategy.ENTRY_LEVEL); + this.knowledgeBaseStore.register(this.metaAnswerKI, + (anAKI, anAnswerExchangeInfo) -> this.fillMetaBindings(anAnswerExchangeInfo.getIncomingBindings()), true); - this.metaAskKI = new AskKnowledgeInteraction(new CommunicativeAct(), this.metaGraphPattern, true, true); + this.metaAskKI = new AskKnowledgeInteraction(new CommunicativeAct(), this.metaGraphPattern, null, true, true, + MatchStrategy.ENTRY_LEVEL); this.knowledgeBaseStore.register(this.metaAskKI, true); this.metaPostNewKI = new PostKnowledgeInteraction( new CommunicativeAct(new HashSet<>(Arrays.asList(Vocab.INFORM_PURPOSE)), new HashSet<>(Arrays.asList(Vocab.NEW_KNOWLEDGE_PURPOSE))), - this.metaGraphPattern, null, true, true); + this.metaGraphPattern, null, null, true, true, MatchStrategy.ENTRY_LEVEL); this.knowledgeBaseStore.register(this.metaPostNewKI, true); this.metaPostChangedKI = new PostKnowledgeInteraction( new CommunicativeAct(new HashSet<>(Arrays.asList(Vocab.INFORM_PURPOSE)), new HashSet<>(Arrays.asList(Vocab.CHANGED_KNOWLEDGE_PURPOSE))), - this.metaGraphPattern, null, true, true); + this.metaGraphPattern, null, null, true, true, MatchStrategy.ENTRY_LEVEL); this.knowledgeBaseStore.register(this.metaPostChangedKI, true); this.metaPostRemovedKI = new PostKnowledgeInteraction( new CommunicativeAct(new HashSet<>(Arrays.asList(Vocab.INFORM_PURPOSE)), new HashSet<>(Arrays.asList(Vocab.REMOVED_KNOWLEDGE_PURPOSE))), - this.metaGraphPattern, null, true, true); + this.metaGraphPattern, null, null, true, true, MatchStrategy.ENTRY_LEVEL); this.knowledgeBaseStore.register(this.metaPostRemovedKI, true); this.metaReactNewKI = new ReactKnowledgeInteraction( new CommunicativeAct(new HashSet<>(Arrays.asList(Vocab.NEW_KNOWLEDGE_PURPOSE)), new HashSet<>(Arrays.asList(Vocab.INFORM_PURPOSE))), - this.metaGraphPattern, null, true, true); + this.metaGraphPattern, null, null, true, true, MatchStrategy.ENTRY_LEVEL); this.knowledgeBaseStore.register(this.metaReactNewKI, (aRKI, aReactExchangeInfo) -> { var postingKi = aReactExchangeInfo.getPostingKnowledgeInteractionId(); - var itShouldBeThis = this.knowledgeBaseStore.getMetaId(aReactExchangeInfo.getPostingKnowledgeBaseId(), KnowledgeInteractionInfo.Type.POST, Vocab.NEW_KNOWLEDGE_PURPOSE); + var itShouldBeThis = this.knowledgeBaseStore.getMetaId(aReactExchangeInfo.getPostingKnowledgeBaseId(), + KnowledgeInteractionInfo.Type.POST, Vocab.NEW_KNOWLEDGE_PURPOSE); if (!postingKi.equals(itShouldBeThis)) { this.LOG.error("Received meta bindings from non-meta (or incorrect meta) KI {}", postingKi); this.LOG.debug("Received meta bindings: {}", aReactExchangeInfo.getArgumentBindings()); - throw new KnowledgeEngineRuntimeException("Received meta bindings from non-meta (or incorrect meta) KI."); + throw new KnowledgeEngineRuntimeException( + "Received meta bindings from non-meta (or incorrect meta) KI."); } - var newKb = this.constructOtherKnowledgeBaseFromBindingSet(aReactExchangeInfo.getArgumentBindings(), aReactExchangeInfo.getPostingKnowledgeBaseId()); + var newKb = this.constructOtherKnowledgeBaseFromBindingSet(aReactExchangeInfo.getArgumentBindings(), + aReactExchangeInfo.getPostingKnowledgeBaseId()); this.otherKnowledgeBaseStore.addKnowledgeBase(newKb); return new BindingSet(); }, true); @@ -134,16 +140,19 @@ public MetaKnowledgeBaseImpl(LoggerProvider loggerProvider, MessageRouter aMessa this.metaReactChangedKI = new ReactKnowledgeInteraction( new CommunicativeAct(new HashSet<>(Arrays.asList(Vocab.CHANGED_KNOWLEDGE_PURPOSE)), new HashSet<>(Arrays.asList(Vocab.INFORM_PURPOSE))), - this.metaGraphPattern, null, true, true); + this.metaGraphPattern, null, null, true, true, MatchStrategy.ENTRY_LEVEL); this.knowledgeBaseStore.register(this.metaReactChangedKI, (aRKI, aReactExchangeInfo) -> { var postingKi = aReactExchangeInfo.getPostingKnowledgeInteractionId(); - var itShouldBeThis = this.knowledgeBaseStore.getMetaId(aReactExchangeInfo.getPostingKnowledgeBaseId(), KnowledgeInteractionInfo.Type.POST, Vocab.CHANGED_KNOWLEDGE_PURPOSE); + var itShouldBeThis = this.knowledgeBaseStore.getMetaId(aReactExchangeInfo.getPostingKnowledgeBaseId(), + KnowledgeInteractionInfo.Type.POST, Vocab.CHANGED_KNOWLEDGE_PURPOSE); if (!postingKi.equals(itShouldBeThis)) { this.LOG.error("Received meta bindings from non-meta (or incorrect meta) KI {}", postingKi); this.LOG.debug("Received meta bindings: {}", aReactExchangeInfo.getArgumentBindings()); - throw new KnowledgeEngineRuntimeException("Received meta bindings from non-meta (or incorrect meta) KI."); + throw new KnowledgeEngineRuntimeException( + "Received meta bindings from non-meta (or incorrect meta) KI."); } - var changedKb = this.constructOtherKnowledgeBaseFromBindingSet(aReactExchangeInfo.getArgumentBindings(), aReactExchangeInfo.getPostingKnowledgeBaseId()); + var changedKb = this.constructOtherKnowledgeBaseFromBindingSet(aReactExchangeInfo.getArgumentBindings(), + aReactExchangeInfo.getPostingKnowledgeBaseId()); this.otherKnowledgeBaseStore.updateKnowledgeBase(changedKb); return new BindingSet(); }, true); @@ -151,33 +160,36 @@ public MetaKnowledgeBaseImpl(LoggerProvider loggerProvider, MessageRouter aMessa this.metaReactRemovedKI = new ReactKnowledgeInteraction( new CommunicativeAct(new HashSet<>(Arrays.asList(Vocab.REMOVED_KNOWLEDGE_PURPOSE)), new HashSet<>(Arrays.asList(Vocab.INFORM_PURPOSE))), - this.metaGraphPattern, null, true, true); + this.metaGraphPattern, null, null, true, true, MatchStrategy.ENTRY_LEVEL); this.knowledgeBaseStore.register(this.metaReactRemovedKI, (aRKI, aReactExchangeInfo) -> { var postingKi = aReactExchangeInfo.getPostingKnowledgeInteractionId(); - var itShouldBeThis = this.knowledgeBaseStore.getMetaId(aReactExchangeInfo.getPostingKnowledgeBaseId(), KnowledgeInteractionInfo.Type.POST, Vocab.REMOVED_KNOWLEDGE_PURPOSE); + var itShouldBeThis = this.knowledgeBaseStore.getMetaId(aReactExchangeInfo.getPostingKnowledgeBaseId(), + KnowledgeInteractionInfo.Type.POST, Vocab.REMOVED_KNOWLEDGE_PURPOSE); if (!postingKi.equals(itShouldBeThis)) { this.LOG.error("Received meta bindings from non-meta (or incorrect meta) KI {}", postingKi); this.LOG.debug("Received meta bindings: {}", aReactExchangeInfo.getArgumentBindings()); - throw new KnowledgeEngineRuntimeException("Received meta bindings from non-meta (or incorrect meta) KI."); + throw new KnowledgeEngineRuntimeException( + "Received meta bindings from non-meta (or incorrect meta) KI."); } - var removedKb = this.constructOtherKnowledgeBaseFromBindingSet(aReactExchangeInfo.getArgumentBindings(), aReactExchangeInfo.getPostingKnowledgeBaseId()); + var removedKb = this.constructOtherKnowledgeBaseFromBindingSet(aReactExchangeInfo.getArgumentBindings(), + aReactExchangeInfo.getPostingKnowledgeBaseId()); this.otherKnowledgeBaseStore.removeKnowledgeBase(removedKb); return new BindingSet(); }, true); } /** - * Generate a binding set that (together with the meta graph pattern) - * represents this knowledge base and its knowledge interactions. + * Generate a binding set that (together with the meta graph pattern) represents + * this knowledge base and its knowledge interactions. * - * @param incoming If given, this method will make sure to return a binding - * set that only contains bindings that 'fit on' a binding in the given - * binding set by removing bindings that do not 'fit'. If null, no such - * operation is performed. + * @param incoming If given, this method will make sure to return a binding set + * that only contains bindings that 'fit on' a binding in the + * given binding set by removing bindings that do not 'fit'. If + * null, no such operation is performed. * * @return a binding set (or part thereof, if {@code incoming} is given) that - * (together with the meta graph pattern) represents this knowledge base and - * its knowledge interactions. + * (together with the meta graph pattern) represents this knowledge base + * and its knowledge interactions. */ private BindingSet fillMetaBindings(BindingSet incoming) { @@ -326,13 +338,17 @@ public CompletableFuture getOtherKnowledgeBase(URI toKnowled try { this.LOG.trace("Received message: {}", answerMsg); var answeringKi = answerMsg.getFromKnowledgeInteraction(); - var itShouldBeThis = this.knowledgeBaseStore.getMetaId(toKnowledgeBaseId, KnowledgeInteractionInfo.Type.ANSWER, null); + var itShouldBeThis = this.knowledgeBaseStore.getMetaId(toKnowledgeBaseId, + KnowledgeInteractionInfo.Type.ANSWER, null); if (!answeringKi.equals(itShouldBeThis)) { - this.LOG.error("Received meta bindings from non-meta (or incorrect meta) KI {}", answeringKi); + this.LOG.error("Received meta bindings from non-meta (or incorrect meta) KI {}", + answeringKi); this.LOG.debug("Received meta bindings: {}", answerMsg.getBindings()); - throw new KnowledgeEngineRuntimeException("Received meta bindings from non-meta (or incorrect meta) KI."); + throw new KnowledgeEngineRuntimeException( + "Received meta bindings from non-meta (or incorrect meta) KI."); } - var otherKB = this.constructOtherKnowledgeBaseFromBindingSet(answerMsg.getBindings(), toKnowledgeBaseId); + var otherKB = this.constructOtherKnowledgeBaseFromBindingSet(answerMsg.getBindings(), + toKnowledgeBaseId); return otherKB; } catch (Throwable t) { this.LOG.error("The construction of other knowledge base should succeed.", t); @@ -349,7 +365,8 @@ public CompletableFuture getOtherKnowledgeBase(URI toKnowled } } - private OtherKnowledgeBase constructOtherKnowledgeBaseFromBindingSet(BindingSet bindings, URI otherKnowledgeBaseId) { + private OtherKnowledgeBase constructOtherKnowledgeBaseFromBindingSet(BindingSet bindings, + URI otherKnowledgeBaseId) { assert !bindings.isEmpty() : "An answer meta message should always have at least a single binding."; Model model; @@ -395,8 +412,8 @@ private OtherKnowledgeBase constructOtherKnowledgeBaseFromBindingSet(BindingSet var kiMeta = model.listObjectsOfProperty(ki, Vocab.IS_META).next(); boolean isMeta = kiMeta.asLiteral().getBoolean(); - assert isMeta == kiMeta.toString().contains("true") : "If the text contains 'true' (=" + kiMeta - + ") then the boolean should be true."; + assert isMeta == kiMeta.toString().contains("true") + : "If the text contains 'true' (=" + kiMeta + ") then the boolean should be true."; this.LOG.trace("meta: {} = {}", FmtUtils.stringForNode(kiMeta.asNode()), isMeta); @@ -462,19 +479,23 @@ private OtherKnowledgeBase constructOtherKnowledgeBaseFromBindingSet(BindingSet Resource gpType = graphPattern.getPropertyResourceValue(RDF.type); if (gpType.equals(Vocab.ARGUMENT_GRAPH_PATTERN)) { if (argumentGraphPatternString != null) { - throw new IllegalArgumentException("Knowledge interaction cannot have multiple argument patterns."); + throw new IllegalArgumentException( + "Knowledge interaction cannot have multiple argument patterns."); } argumentGraphPatternString = graphPattern.getProperty(Vocab.HAS_PATTERN).getString(); } else if (gpType.equals(Vocab.RESULT_GRAPH_PATTERN)) { if (resultGraphPatternString != null) { - throw new IllegalArgumentException("Knowledge interaction cannot have multiple result patterns."); + throw new IllegalArgumentException( + "Knowledge interaction cannot have multiple result patterns."); } resultGraphPatternString = graphPattern.getProperty(Vocab.HAS_PATTERN).getString(); } else { - throw new IllegalArgumentException(String.format("For a POST/REACT Knowledge Interaction, their graph pattern must be either %s or %s. Not %s.", Vocab.ARGUMENT_GRAPH_PATTERN, Vocab.RESULT_GRAPH_PATTERN, gpType)); + throw new IllegalArgumentException(String.format( + "For a POST/REACT Knowledge Interaction, their graph pattern must be either %s or %s. Not %s.", + Vocab.ARGUMENT_GRAPH_PATTERN, Vocab.RESULT_GRAPH_PATTERN, gpType)); } } - + if (argumentGraphPatternString == null) { throw new IllegalArgumentException( "Every Post or React Knowledge Interaction should have an argument graph pattern."); diff --git a/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/ReasonerProcessor.java b/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/ReasonerProcessor.java index 1d481fdb..f561bce4 100644 --- a/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/ReasonerProcessor.java +++ b/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/ReasonerProcessor.java @@ -42,6 +42,7 @@ import eu.knowledge.engine.smartconnector.api.ExchangeInfo.Status; import eu.knowledge.engine.smartconnector.api.GraphPattern; import eu.knowledge.engine.smartconnector.api.KnowledgeInteraction; +import eu.knowledge.engine.smartconnector.api.MatchStrategy; import eu.knowledge.engine.smartconnector.api.PostExchangeInfo; import eu.knowledge.engine.smartconnector.api.PostKnowledgeInteraction; import eu.knowledge.engine.smartconnector.api.PostResult; @@ -67,51 +68,6 @@ public class ReasonerProcessor extends SingleInteractionProcessor { private static final Logger LOG = LoggerFactory.getLogger(ReasonerProcessor.class); - // the match strategy determines how many matches will be found between graph - // patterns. This is a very costly operation (especially with bigger (10+) - // triples per graph pattern. For some use cases it suffices to use ENTRY_LEVEL - // matching which is much faster, while others require SUPREME_LEVEL, which is - // extremely slow and often throws out of memory exceptions. - public static enum MatchStrategy { - SUPREME_LEVEL, ULTRA_LEVEL, ADVANCED_LEVEL, NORMAL_LEVEL, ENTRY_LEVEL; - - private EnumSet toConfig(boolean antecedentOfTarget) { - EnumSet config; - switch (this) { - case ENTRY_LEVEL: - config = EnumSet.of(MatchFlag.ONE_TO_ONE, MatchFlag.ONLY_BIGGEST, MatchFlag.SINGLE_RULE); - - // disable fully covered when matching consequents. - if (antecedentOfTarget) - config.add(MatchFlag.FULLY_COVERED); - break; - case NORMAL_LEVEL: - config = EnumSet.of(MatchFlag.ONE_TO_ONE, MatchFlag.ONLY_BIGGEST); - - // disable fully covered when matching consequents. - if (antecedentOfTarget) - config.add(MatchFlag.FULLY_COVERED); - break; - case ADVANCED_LEVEL: - config = EnumSet.of(MatchFlag.ONLY_BIGGEST); - - // disable fully covered when matching consequents. - if (antecedentOfTarget) - config.add(MatchFlag.FULLY_COVERED); - break; - case ULTRA_LEVEL: - config = EnumSet.of(MatchFlag.ONLY_BIGGEST); - break; - case SUPREME_LEVEL: - config = EnumSet.noneOf(MatchFlag.class); - break; - default: - config = EnumSet.noneOf(MatchFlag.class); - } - return config; - } - } - private RuleStore store; private MyKnowledgeInteractionInfo myKnowledgeInteraction; private final Set askExchangeInfos; @@ -194,7 +150,11 @@ public void planAskInteraction(MyKnowledgeInteractionInfo aAKI) { new HashSet<>()); this.store.addRule(aRule); - EnumSet config = this.matchStrategy.toConfig(true); + EnumSet config; + if (aAKI.getKnowledgeInteraction().getMatchStrategy() == null) + config = this.matchStrategy.toConfig(true); + else + config = aAKI.getKnowledgeInteraction().getMatchStrategy().toConfig(true); this.reasonerPlan = new ReasonerPlan(this.store, aRule, config); } else { @@ -261,7 +221,11 @@ public void planPostInteraction(MyKnowledgeInteractionInfo aPKI) { ProactiveRule aRule = new ProactiveRule(ruleName, new HashSet<>(), new HashSet<>(translatedGraphPattern)); store.addRule(aRule); - EnumSet config = this.matchStrategy.toConfig(false); + EnumSet config; + if (aPKI.getKnowledgeInteraction().getMatchStrategy() == null) + config = this.matchStrategy.toConfig(false); + else + config = aPKI.getKnowledgeInteraction().getMatchStrategy().toConfig(false); this.reasonerPlan = new ReasonerPlan(this.store, aRule, config); diff --git a/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/util/KnowledgeNetwork.java b/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/util/KnowledgeNetwork.java index a177f9cf..bf35d20d 100644 --- a/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/util/KnowledgeNetwork.java +++ b/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/util/KnowledgeNetwork.java @@ -17,6 +17,7 @@ import eu.knowledge.engine.smartconnector.api.AskKnowledgeInteraction; import eu.knowledge.engine.smartconnector.api.CommunicativeAct; import eu.knowledge.engine.smartconnector.api.GraphPattern; +import eu.knowledge.engine.smartconnector.api.MatchStrategy; import eu.knowledge.engine.smartconnector.api.Vocab; public class KnowledgeNetwork { @@ -76,7 +77,7 @@ private void startAndWaitForReady() { // register our state check Knowledge Interaction on each Smart Connecotr GraphPattern gp = new GraphPattern(this.prefixMapping, - // @formatter:off + // @formatter:off "?kb rdf:type kb:KnowledgeBase .", "?kb kb:hasName ?name .", "?kb kb:hasDescription ?description .", "?kb kb:hasKnowledgeInteraction ?ki .", "?ki rdf:type ?kiType .", "?ki kb:isMeta ?isMeta .", "?ki kb:hasCommunicativeAct ?act .", "?act rdf:type kb:CommunicativeAct .", @@ -87,7 +88,8 @@ private void startAndWaitForReady() { ); for (MockedKnowledgeBase kb : justStartedKBs) { - AskKnowledgeInteraction anAskKI = new AskKnowledgeInteraction(new CommunicativeAct(), gp, true); + AskKnowledgeInteraction anAskKI = new AskKnowledgeInteraction(new CommunicativeAct(), gp, null, false, true, + MatchStrategy.ENTRY_LEVEL); this.knowledgeInteractionMetadata.put(kb, anAskKI); kb.register(anAskKI); } diff --git a/smart-connector/src/test/java/eu/knowledge/engine/smartconnector/api/TestComplexGraphPatternMatching.java b/smart-connector/src/test/java/eu/knowledge/engine/smartconnector/api/TestComplexGraphPatternMatching.java index 0331b5b2..7db735f5 100644 --- a/smart-connector/src/test/java/eu/knowledge/engine/smartconnector/api/TestComplexGraphPatternMatching.java +++ b/smart-connector/src/test/java/eu/knowledge/engine/smartconnector/api/TestComplexGraphPatternMatching.java @@ -262,7 +262,8 @@ public float convert(float fahrenheit) { """; GraphPattern gp2 = new GraphPattern(prefixes, TestUtils.convertGP(pattern)); - AskKnowledgeInteraction askKI = new AskKnowledgeInteraction(new CommunicativeAct(), gp2); + AskKnowledgeInteraction askKI = new AskKnowledgeInteraction(new CommunicativeAct(), gp2, null, false, false, + MatchStrategy.NORMAL_LEVEL); dashboardKB.setDomainKnowledge(new HashSet<>( Arrays.asList(locatedInRule, thermostatSensorRule, opencloseSensorRule, fahrenheitConverterRule))); dashboardKB.register(askKI);