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 a5ff2c74..fa9a4bf0 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/BaseRule.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/BaseRule.java @@ -4,7 +4,10 @@ import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -31,8 +34,56 @@ public class BaseRule { public static final String ARROW = "->"; + /** + * A comparator to make sure the smaller matches collection is ordered from big + * to small. + */ + public static class CombiMatchSizeComparator implements Comparator { + public int compare(CombiMatch object1, CombiMatch object2) { + return object2.getSize() - object1.getSize(); + } + } + + /** + * Some flags used during the matching process. It is targeted to the internally + * generated combi matches. + */ + public static enum MatchFlag { + + /** + * Only look for combi matches that fully cover the target graph pattern. Note + * that for backward reasoning (where we match neighbors to antecedents) this is + * an essential configuration property to improve performance, however, for + * backward reasoning (where we match consequent neighbors) this leaves out + * matches to neighbors. The reason is that when forward reasoning, the + * consequent graph pattern can match partially so only part of the forwarded + * data is being used. + */ + FULLY_COVERED, + + /** + * Only look for combi matches that consist of a single candidate rule. + */ + SINGLE_RULE, + + /** + * Only look for combi matches in which a candidate triple pattern can only map + * to a single target triple pattern. Note that setting this flag prevents some + * transitivity rules to correctly match (x p y . y p z . -> x p z) + */ + ONE_TO_ONE, + + /** + * Only look for the biggest combi matches and ignore combi matches that are a + * submatch of another combi match. + */ + ONLY_BIGGEST; + + public static final EnumSet ALL_OPTS = EnumSet.allOf(MatchFlag.class); + } + public static enum MatchStrategy { - FIND_ALL_MATCHES, FIND_ONLY_BIGGEST_MATCHES, FIND_ONLY_FULL_MATCHES + SUPREME_LEVEL, ULTRA_LEVEL, ADVANCED_LEVEL, NORMAL_LEVEL, ENTRY_LEVEL } public static class TrivialBindingSetHandler implements TransformBindingSetHandler { @@ -119,201 +170,46 @@ public Set getConsequent() { } public Set consequentMatches(Set anAntecedent, MatchStrategy aMatchStrategy) { - if (!this.consequent.isEmpty()) - return matches(anAntecedent, this.consequent, aMatchStrategy); - return new HashSet<>(); - } - - public Set antecedentMatches(Set aConsequent, MatchStrategy aMatchStrategy) { - if (!this.antecedent.isEmpty()) - return matches(aConsequent, this.antecedent, aMatchStrategy); - return new HashSet<>(); - } - - /** - * FInd the biggest matches bewteen two graph patterns. - * - * @param aFirstPattern - * @param aSecondPattern - * @param aMatchStrategy - * @return - */ - public static Set matches(Set aFirstPattern, Set aSecondPattern, - MatchStrategy aMatchStrategy) { - - assert aFirstPattern != null; - assert aSecondPattern != null; - assert !aFirstPattern.isEmpty(); - assert !aSecondPattern.isEmpty(); - - long start = System.currentTimeMillis(); - - List allMatches = new ArrayList<>(); - - // first find all triples in the consequent that match each triple in the - // antecedent - Map> matchesPerTriple = new HashMap<>(); - List findMatches; - for (TriplePattern anteTriple : aFirstPattern) { - // find all possible matches of the current antecedent triple in the consequent - findMatches = findMatches(anteTriple, aSecondPattern); - if (!findMatches.isEmpty()) - matchesPerTriple.put(anteTriple, findMatches); - } - - // if not every triple pattern can be matched, we stop the process if we require - // a full match. - if (aMatchStrategy.equals(MatchStrategy.FIND_ONLY_FULL_MATCHES) - && matchesPerTriple.keySet().size() < aFirstPattern.size()) - return new HashSet<>(); - - // next, correctly combine all found matches - List biggestMatches = new ArrayList<>(); - List smallerMatches = new ArrayList<>(); - Match mergedMatch = null; - List matches = null; - List toBeAddedToBiggestMatches = null, toBeAddedToSmallerMatches = null; - Set toBeDemotedMatchIndices = null; - - Iterator>> matchIter = matchesPerTriple.entrySet().iterator(); - - // always add first matches - if (matchIter.hasNext()) { - biggestMatches.addAll(matchIter.next().getValue()); - } - - int idx = 0; - while (matchIter.hasNext()) { - idx++; - LOG.trace("Processing triple pattern {}/{} with {} biggest and {} smaller matches.", idx, - matchesPerTriple.size(), biggestMatches.size(), smallerMatches.size()); - - long innerStart = System.currentTimeMillis(); - - Map.Entry> entry = matchIter.next(); + if (!this.consequent.isEmpty()) { + Rule r = new Rule(anAntecedent, new SinkBindingSetHandler() { - // keep a set of new/remove matches, so we can add/remove them at the end of - // this loop - toBeAddedToBiggestMatches = new ArrayList<>(); - toBeAddedToSmallerMatches = new ArrayList<>(); - toBeDemotedMatchIndices = new HashSet<>(); - - matches = entry.getValue(); - assert matches != null; - - for (Match m1 : matches) { - // check if we need to merge with existing matches - boolean hasMerged = false; - // first check if m1 can be merged with any of the existing biggest matches. - int biggestMatchesSize = biggestMatches.size(); - for (int i = 0; i < biggestMatchesSize; i++) { - Match m2 = biggestMatches.get(i); - mergedMatch = m2.merge(m1); - if (mergedMatch != null) { - hasMerged = true; - toBeAddedToBiggestMatches.add(mergedMatch); - toBeDemotedMatchIndices.add(i); - } else if (aMatchStrategy.equals(MatchStrategy.FIND_ONLY_FULL_MATCHES)) { - toBeDemotedMatchIndices.add(i); - } - } - - // then check if m1 can be merged with any of the existing smaller matches - if (!aMatchStrategy.equals(MatchStrategy.FIND_ONLY_FULL_MATCHES)) { - for (Match m2 : smallerMatches) { - mergedMatch = m2.merge(m1); - if (mergedMatch != null) { - - if (hasMerged) { - // add to smallerMatches and sometimes to biggestMatches. - if (isSubMatch(m2, toBeAddedToBiggestMatches)) { - // add to smaller matches - toBeAddedToSmallerMatches.add(mergedMatch); - } else { - // add to biggest matches - toBeAddedToBiggestMatches.add(mergedMatch); - } - } else { - // add to biggestMatches - hasMerged = true; - toBeAddedToBiggestMatches.add(mergedMatch); - } - } - } + @Override + public CompletableFuture handle(BindingSet aBindingSet) { + // TODO Auto-generated method stub + return null; } + }); + Map> matches = getMatches(this, new HashSet<>(Arrays.asList(r)), false, + aMatchStrategy); - if (!hasMerged && !aMatchStrategy.equals(MatchStrategy.FIND_ONLY_FULL_MATCHES)) { - toBeAddedToBiggestMatches.add(m1); - } else { - toBeAddedToSmallerMatches.add(m1); - } - } - - // remove all toBeDemotedMatches from the biggestMatches and add them to the - // smallerMatches. - - List sortedList = new ArrayList<>(toBeDemotedMatchIndices); - Collections.sort(sortedList, Collections.reverseOrder()); - for (int i : sortedList) { - smallerMatches.add(biggestMatches.get(i)); - biggestMatches.remove(i); - } - - // add all toBeAddedMatches - biggestMatches.addAll(toBeAddedToBiggestMatches); - smallerMatches.addAll(toBeAddedToSmallerMatches); - - long innerEnd = System.currentTimeMillis(); - toBeAddedToBiggestMatches = null; - toBeDemotedMatchIndices = null; - toBeAddedToSmallerMatches = null; + if (matches.containsKey(r)) + return matches.get(r); } - - assert biggestMatches != null; - - long finalEnd = System.currentTimeMillis(); - - if (aMatchStrategy.equals(MatchStrategy.FIND_ALL_MATCHES)) { - allMatches.addAll(biggestMatches); - allMatches.addAll(smallerMatches); - } else if (aMatchStrategy.equals(MatchStrategy.FIND_ONLY_BIGGEST_MATCHES) - || aMatchStrategy.equals(MatchStrategy.FIND_ONLY_FULL_MATCHES)) { - allMatches.addAll(biggestMatches); - } - - return new HashSet<>(allMatches); + return new HashSet<>(); } - /** - * Go over all matches in toBeaddedToBiggesetMatches and check if aMatch is a - * subMatch of one of those. - * - * @param aMatch - * @param toBeAddedToBiggestMatches - * @return - */ - private static boolean isSubMatch(Match aMatch, List toBeAddedToBiggestMatches) { - - for (Match m : toBeAddedToBiggestMatches) { - if (m.isSubMatch(aMatch)) { - return true; - } + public Set antecedentMatches(Set aConsequent, MatchStrategy aMatchStrategy) { + if (!this.antecedent.isEmpty()) { + Rule r = new Rule(new HashSet<>(), aConsequent); + Map> matches = getMatches(this, new HashSet<>(Arrays.asList(r)), true, aMatchStrategy); + if (matches.containsKey(r)) + return matches.get(r); } - return false; + return new HashSet<>(); } - private static List findMatches(TriplePattern antecedent, Set consequent) { + private static List findMatches(TriplePattern targetTriple, Set someCandidateTriplePatterns) { - assert consequent != null; - assert antecedent != null; - assert !consequent.isEmpty(); + assert someCandidateTriplePatterns != null; + assert targetTriple != null; + assert !someCandidateTriplePatterns.isEmpty(); List matchingTriplePatterns = new ArrayList<>(); Map map; - for (TriplePattern tp : consequent) { - map = antecedent.findMatches(tp); + for (TriplePattern candidateTriple : someCandidateTriplePatterns) { + map = targetTriple.findMatches(candidateTriple); if (map != null) { - matchingTriplePatterns.add(new Match(antecedent, tp, map)); + matchingTriplePatterns.add(new Match(candidateTriple, targetTriple, map)); } } @@ -392,7 +288,7 @@ protected void setName(String aName) { this.name = aName; } - protected String getName() { + public String getName() { return name; } @@ -433,4 +329,438 @@ public boolean equals(Object obj) { return true; } + public static Map> getMatchesPerTriplePerRule(Set aTargetPattern, + List allCandidateRules, boolean useCandidateConsequent) { + + Map> matchesPerRule = new HashMap<>(); + + for (BaseRule candidateRule : allCandidateRules) { + // first find all triples in the consequent that match each triple in the + // antecedent + List foundMatches; + for (TriplePattern tripleTarget : aTargetPattern) { + // find all possible matches of the current antecedent triple in the consequent + if (useCandidateConsequent ? !candidateRule.consequent.isEmpty() + : !candidateRule.antecedent.isEmpty()) { + foundMatches = findMatches(tripleTarget, + useCandidateConsequent ? candidateRule.consequent : candidateRule.antecedent); + if (!foundMatches.isEmpty()) { + + Set ruleMatches = matchesPerRule.get(tripleTarget); + if (ruleMatches == null) { + ruleMatches = new HashSet<>(); + matchesPerRule.put(tripleTarget, ruleMatches); + } + + for (Match m : foundMatches) { + CombiMatch newCombiMatch = new CombiMatch(); + Set newMatchSet = new HashSet<>(); + newMatchSet.add(m); + newCombiMatch.put(candidateRule, newMatchSet); + ruleMatches.add(newCombiMatch); + } + } + } + } + } + return matchesPerRule; + } + + // new implementation of matching towards full match + + /** + * This method finds matches of {@code otherGraphPatterns} on + * {@code aGraphPattern} that are part of a full match of {@code aGraphPattern}. + * + * @param aGraphPattern The graph pattern for which we want to find + * matches. + * @param otherGraphPatterns The other graph patterns that we want to check + * whether and how they match to + * {@code aGraphPattern}. + * @param antecedentOfTarget Whether to match the antecedent or consequent of + * the target rule + * @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); + + Set targetGP = antecedentOfTarget ? aTargetRule.getAntecedent() : aTargetRule.getConsequent(); + + /* + * we use a list instead of a set for performance reasons. The list does not + * call Match#equals(...) method often everytime we add an entry. The algorithm + * makes sure matches that are added do not already exist. + */ + List allMatches = new ArrayList(); + + // first find all triples in the someCandidateRules that match each triple + // in targetGP + Map> combiMatchesPerTriple = getMatchesPerTriplePerRule(targetGP, + new ArrayList<>(someCandidateRules), antecedentOfTarget); + + printCombiMatchesPerTriple(combiMatchesPerTriple); + + // if not every triple pattern can be matched, we stop the process if we require + // a full match. + if (config.contains(MatchFlag.FULLY_COVERED) && combiMatchesPerTriple.keySet().size() < targetGP.size()) + return new HashMap<>(); + + List biggestMatches = new ArrayList<>(); + List smallerMatches = new ArrayList<>(); + List toBeAddedToBiggestMatches = null, toBeAddedToSmallerMatches = null; + Set toBeDemotedMatchIndices = null; + + Iterator>> triplePatternMatchesIter = combiMatchesPerTriple.entrySet() + .iterator(); + + if (triplePatternMatchesIter.hasNext()) { + Set value = triplePatternMatchesIter.next().getValue(); + LOG.trace("{}/{} ({}): biggest: {}, smaller: {}", 1, combiMatchesPerTriple.size(), value.size(), + biggestMatches.size(), smallerMatches.size()); + biggestMatches.addAll(value); + } + + int cnt = 1; + // iterate all (except the first) triple patterns of the target graph pattern. + while (triplePatternMatchesIter.hasNext()) { + Map.Entry> currentTriplePatternMatches = triplePatternMatchesIter.next(); + LOG.trace("{}/{} ({}): biggest: {}, smaller: {}", ++cnt, combiMatchesPerTriple.size(), + currentTriplePatternMatches.getValue().size(), biggestMatches.size(), smallerMatches.size()); + + Set candidateCombiMatches = currentTriplePatternMatches.getValue(); + + toBeAddedToBiggestMatches = new ArrayList<>(); + toBeAddedToSmallerMatches = new ArrayList<>(); + toBeDemotedMatchIndices = new HashSet<>(); + + // iterate all candidates for current triple pattern + for (CombiMatch candidateCombiMatch : candidateCombiMatches) { + + boolean candidateWasMerged = false; + + // try to merge with biggest combi matches + for (int i = 0; i < biggestMatches.size(); i++) { + CombiMatch aBiggestMatch = biggestMatches.get(i); + // compare/combine combimatches. + CombiMatch newCombiMatch = mergeCombiMatches(candidateCombiMatch, aBiggestMatch, config); + + if (newCombiMatch != null) { + // successful merge add new biggest and demote old biggest + toBeAddedToBiggestMatches.add(newCombiMatch); + candidateWasMerged = true; + toBeDemotedMatchIndices.add(i); + } else if (config.contains(MatchFlag.FULLY_COVERED)) + toBeDemotedMatchIndices.add(i); + } + + if (!config.contains(MatchFlag.FULLY_COVERED)) { + + // we need to sort the smaller matches on size (from big to small) + // to make sure the isSubCombiMatch method works correctly in this algo + + // try to merge with smaller combi matches + for (CombiMatch aSmallerMatch : smallerMatches) { + CombiMatch newCombiMatch = mergeCombiMatches(candidateCombiMatch, aSmallerMatch, config); + if (newCombiMatch != null) { + // merge successful, add to smaller matches + if (candidateWasMerged) { + if (isSubCombiMatch(newCombiMatch, toBeAddedToBiggestMatches)) { + toBeAddedToSmallerMatches.add(newCombiMatch); + } else { + toBeAddedToBiggestMatches.add(newCombiMatch); + } + } else { + // add to biggest matches + candidateWasMerged = true; + toBeAddedToBiggestMatches.add(newCombiMatch); + } + + } + } + } + + if (!candidateWasMerged && !config.contains(MatchFlag.FULLY_COVERED)) + toBeAddedToBiggestMatches.add(candidateCombiMatch); + else + toBeAddedToSmallerMatches.add(candidateCombiMatch); + } + + // update collections + List sortedList = new ArrayList<>(toBeDemotedMatchIndices); + Collections.sort(sortedList, Collections.reverseOrder()); + for (int i : sortedList) { + + if (!config.contains(MatchFlag.FULLY_COVERED)) { + toBeAddedToSmallerMatches.add(biggestMatches.get(i)); + } + biggestMatches.remove(i); + } + + // add all toBeAddedMatches + biggestMatches.addAll(toBeAddedToBiggestMatches); + smallerMatches.addAll(toBeAddedToSmallerMatches); + + Collections.sort(smallerMatches, new CombiMatchSizeComparator()); + } + + toBeAddedToBiggestMatches = null; + toBeDemotedMatchIndices = null; + toBeAddedToSmallerMatches = null; + + allMatches.addAll(biggestMatches); + + if (!config.contains(MatchFlag.ONLY_BIGGEST)) { + allMatches.addAll(smallerMatches); + } + +// printAllMatches(allMatches); + + return convertToMap(allMatches); + } + + private static void printAllMatches(List allMatches) { + + for (CombiMatch cm : allMatches) { + System.out.println("--------- combi match ------------"); + for (Map.Entry> entry : cm.entrySet()) { + BaseRule key = entry.getKey(); + System.out.println(key.getAntecedent() + " -> " + key.getConsequent() + ":"); + for (Match m : entry.getValue()) { + System.out.println("\t" + m); + } + } + } + } + + private static boolean isSubCombiMatch(CombiMatch aSmallerMatch, List toBeAddedToBiggestMatches) { + + for (CombiMatch combiMatch : toBeAddedToBiggestMatches) { + if (combiMatch.isSubMatch(aSmallerMatch)) { + return true; + } + } + + return false; + } + + private static EnumSet 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(); + + int total = 1; + for (Set combiMatch : combiMatchesPerTriple.values()) { + total = total * combiMatch.size(); + sb.append(combiMatch.size()).append(" * "); + } + + LOG.trace("{} = {}", total, sb.toString()); + + } + + /** + * Tries to merge the singleton candidate combi match with the given biggest + * match. It takes into account that in some rare cases a single triple pattern + * should be allowed to match multiple triple patterns (see {@link CombiMatch}). + * + * @param candidateCombiMatch The candidate combi match + * @param aBiggestCombiMatch The biggest match to merge with + * @return a new CombiMatch if merge is possible, {@code null} otherwise. + */ + private static CombiMatch mergeCombiMatches(CombiMatch candidateCombiMatch, CombiMatch aBiggestCombiMatch, + EnumSet config) { + + assert candidateCombiMatch.keySet().size() == 1; + Map.Entry> entry = candidateCombiMatch.entrySet().iterator().next(); + + BaseRule candidateRule = entry.getKey(); + + assert entry.getValue().size() == 1; + Match candidateMatch = entry.getValue().iterator().next(); + + if (aBiggestCombiMatch.containsKey(candidateRule)) { + // rule already present, try to merge matches. + Set biggestMatch = aBiggestCombiMatch.get(candidateRule); + + Match newMatch; + + CombiMatch newCombiMatch = new CombiMatch(aBiggestCombiMatch); + Set newBiggestMatch = new HashSet<>(biggestMatch); + + // we merge it with one of the avaialble matches (does that work?) + for (Match m : biggestMatch) { + newMatch = m.merge(candidateMatch); + if (newMatch != null) { + // merge successful + newBiggestMatch.remove(m); + newBiggestMatch.add(newMatch); + newCombiMatch.put(candidateRule, newBiggestMatch); + return newCombiMatch; + } + } + + // merge unsuccessful, if special case of same triple matches multiple triples. + if (!config.contains(MatchFlag.ONE_TO_ONE) + && doesSameCandidateTripleMapToDifferentTriple(candidateMatch, biggestMatch)) { + newBiggestMatch.add(candidateMatch); + newCombiMatch.put(candidateRule, newBiggestMatch); + return newCombiMatch; + } + + } else if (!config.contains(MatchFlag.SINGLE_RULE)) { + // rule not yet present, add new entry for rule. + CombiMatch newCombiMatch = new CombiMatch(aBiggestCombiMatch); + Set matches = new HashSet<>(); + matches.add(candidateMatch); + newCombiMatch.put(candidateRule, matches); + return newCombiMatch; + } + return null; + } + + private static boolean doesSameCandidateTripleMapToDifferentTriple(Match candidateMatch, + Set biggestMatches) { + assert candidateMatch.getMatchingPatterns().entrySet().size() == 1; + var candidateMatchMP = candidateMatch.getMatchingPatterns().entrySet().iterator().next(); + for (Match biggestMatch : biggestMatches) { + var biggestMatchMP = biggestMatch.getMatchingPatterns(); + for (Map.Entry entry : biggestMatchMP.entrySet()) { + if (entry.getValue().equals(candidateMatchMP.getValue())) { + return true; + } + } + } + + return false; + } + + private static Map> convertToMap(List combiMatches) { + Map> matchesPerRule = new HashMap<>(); + for (CombiMatch combiMatch : combiMatches) { + for (Map.Entry> ruleMatch : combiMatch.entrySet()) { + // create if not already exists + if (!matchesPerRule.containsKey(ruleMatch.getKey())) { + matchesPerRule.put(ruleMatch.getKey(), new HashSet<>()); + } + assert matchesPerRule.containsKey(ruleMatch.getKey()); + matchesPerRule.get(ruleMatch.getKey()).addAll(ruleMatch.getValue()); + + } + } + + return matchesPerRule; + } + + /** + * This class represents a single combi match which consists of one or more + * rules mapped to (in most cases) a single Match object. However, in some + * scenario's (like a transitivity rule) we need to store multiple Match objects + * for a single rule. Therefore, it maps to a Set of matches. This is caused by + * the fact that a Match object does not allow a multiple triple patterns to be + * 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> { + private static final long serialVersionUID = 1L; + + public CombiMatch() { + super(); + } + + /** + * @param aMatch a combi match object to check whether it is a sub combi match + * of this combi match. + * @return {@code true} when {@code aMatch} is a sub combi match of this combi + * match and {@code false} otherwise. + */ + public boolean isSubMatch(CombiMatch aMatch) { + + for (Map.Entry> entry : aMatch.entrySet()) { + + if (!this.containsKey(entry.getKey())) { + return false; + } else if (!isSubSetMatch(entry.getValue(), this.get(entry.getKey()))) { + return false; + } + } + + return true; + } + + private boolean isSubSetMatch(Set candidateMatches, Set targetMatches) { + + for (Match candidateMatch : candidateMatches) { + boolean found = false; + for (Match targetMatch : targetMatches) { + found |= targetMatch.isSubMatch(candidateMatch); + } + + if (!found) { + return false; + } + } + return true; + } + + public CombiMatch(CombiMatch aBiggestCombiMatch) { + super(aBiggestCombiMatch); + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + + sb.append("{"); + for (Map.Entry> entry : this.entrySet()) { + sb.append(entry.getValue()).append("=").append(entry.getKey()); + } + sb.append("}"); + return sb.toString(); + } + + public int getSize() { + + Set tps = new HashSet(); + for (Map.Entry> entry : this.entrySet()) { + + for (Match m : entry.getValue()) { + tps.addAll(m.getMatchingPatterns().values()); + } + } + + return tps.size(); + } + } } diff --git a/reasoner/src/main/java/eu/knowledge/engine/reasoner/Match.java b/reasoner/src/main/java/eu/knowledge/engine/reasoner/Match.java index 0e902d57..68a0a3fb 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/Match.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/Match.java @@ -169,7 +169,41 @@ public Node getOtherNode(Map aContext, Node aNode) { @Override public String toString() { - return "Match " + mapping; + + StringBuilder sb = new StringBuilder(); + sb.append("{"); + for (Map.Entry entry : this.matchingPatterns.entrySet()) { + + boolean firstTriplePattern = true; + for (TriplePattern tp : new TriplePattern[] { entry.getKey(), entry.getValue() }) { + boolean firstTime = true; + Node[] nodes = new Node[] { tp.getSubject(), tp.getPredicate(), tp.getObject() }; + for (int i = 0; i < 3; i++) { + Node n = nodes[i]; + + if (!firstTime) { + sb.append(" "); + } + var truncatedNode = TriplePattern.trunc(n); + + var tn = new TripleNode(tp, n, i); + + if (firstTriplePattern ? this.mapping.containsValue(tn) : this.mapping.containsKey(tn)) { + sb.append("|").append(truncatedNode).append("|"); + } else { + sb.append(truncatedNode); + } + firstTime = false; + } + if (firstTriplePattern) + sb.append("="); + firstTriplePattern = false; + } + sb.append(", "); + } + sb.deleteCharAt(sb.length() - 1).deleteCharAt(sb.length() - 1).append("}"); + + return "Match " + sb.toString(); } /** 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 9dc0c726..537234c1 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/ReasonerPlan.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/ReasonerPlan.java @@ -5,7 +5,6 @@ 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; @@ -39,7 +38,7 @@ public class ReasonerPlan { private final ProactiveRule start; private final Map ruleToRuleNode; private boolean done; - private MatchStrategy strategy = MatchStrategy.FIND_ALL_MATCHES; + private MatchStrategy strategy = MatchStrategy.ULTRA_LEVEL; private boolean useTaskBoard = true; public ReasonerPlan(RuleStore aStore, ProactiveRule aStartRule) { @@ -101,6 +100,7 @@ public TaskBoard execute(BindingSet bindingSet) { Set changed = new HashSet<>(); do { + LOG.trace("New round."); stack.clear(); visited.clear(); changed.clear(); @@ -267,8 +267,8 @@ private RuleNode createOrGetReasonerNode(BaseRule aRule, BaseRule aParent) { boolean ourAntecedentFullyMatchesParentConsequent = false; if (aParent != null && this.store.getAntecedentNeighbors(aRule, this.strategy).containsKey(aParent)) { - ourAntecedentFullyMatchesParentConsequent = antecedentFullyMatchesConsequent(aRule.getAntecedent(), - aParent.getConsequent(), this.getMatchStrategy()); + ourAntecedentFullyMatchesParentConsequent = antecedentFullyMatchesConsequent(aRule, aParent, + this.store.getAntecedentNeighbors(aRule, this.strategy).get(aParent), this.strategy); } if (!ourAntecedentFullyMatchesParentConsequent) { @@ -312,12 +312,15 @@ private void scheduleOrDoTask(RuleNode current, TaskBoard taskBoard) { * that if the antecedent is a subset of the consequent this method also return * true. * - * @param consequent - * @param antecedent + * @param consequentRule + * @param antecedentRule * @return */ - private boolean antecedentFullyMatchesConsequent(Set antecedent, Set consequent, - MatchStrategy aMatchStrategy) { + private boolean antecedentFullyMatchesConsequent(BaseRule antecedentRule, BaseRule consequentRule, + Set someMatches, MatchStrategy aMatchStrategy) { + + var antecedent = antecedentRule.getAntecedent(); + var consequent = consequentRule.getConsequent(); assert !antecedent.isEmpty(); assert !consequent.isEmpty(); @@ -325,9 +328,7 @@ private boolean antecedentFullyMatchesConsequent(Set antecedent, if (antecedent.size() > consequent.size()) return false; - Set matches = BaseRule.matches(antecedent, consequent, aMatchStrategy); - - for (Match m : matches) { + for (Match m : someMatches) { // check if there is a match that is full boolean allFound = true; for (TriplePattern tp : antecedent) { diff --git a/reasoner/src/main/java/eu/knowledge/engine/reasoner/api/TriplePattern.java b/reasoner/src/main/java/eu/knowledge/engine/reasoner/api/TriplePattern.java index f631f387..dd67c766 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/api/TriplePattern.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/api/TriplePattern.java @@ -109,8 +109,9 @@ public static String trunc(Node n) { return uri.getFragment(); } var path = uri.getPath(); - if (path != null) + if (path != null && path.substring(path.lastIndexOf('/') + 1).length() > 0) return path.substring(path.lastIndexOf('/') + 1); + } else if (n.isLiteral()) { return n.getLiteralLexicalForm(); } diff --git a/reasoner/src/main/java/eu/knowledge/engine/reasoner/api/TripleVarBinding.java b/reasoner/src/main/java/eu/knowledge/engine/reasoner/api/TripleVarBinding.java index 86cb81a5..8f7a9920 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/api/TripleVarBinding.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/api/TripleVarBinding.java @@ -1,6 +1,7 @@ package eu.knowledge.engine.reasoner.api; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -141,6 +142,16 @@ public boolean containsKey(TripleNode key) { return this.tripleVarMapping.containsKey(key); } + public Set getTripleNodes(TriplePattern aTriplePattern) { + Set nodes = new HashSet(); + for (TripleNode tv : this.tripleVarMapping.keySet()) { + if (tv.tp.equals(aTriplePattern)) { + nodes.add(tv); + } + } + return nodes; + } + public boolean containsVar(Var aVar) { assert aVar instanceof Var; 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 e5bd056f..55aa6d1f 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,12 +1,17 @@ package eu.knowledge.engine.reasoner.api; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; -import org.apache.jena.atlas.logging.Log; +import org.apache.jena.graph.Node; import org.apache.jena.graph.Node_Concrete; import org.apache.jena.sparql.core.Var; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import eu.knowledge.engine.reasoner.Match; @@ -16,17 +21,17 @@ public class TripleVarBindingSet { private Set bindings; private Set tripleVarsCache; + private static final Logger LOG = LoggerFactory.getLogger(TripleVarBindingSet.class); + public TripleVarBindingSet(Set aGraphPattern) { this.graphPattern = aGraphPattern; - bindings = new HashSet<>(); + bindings = ConcurrentHashMap.newKeySet(); } public TripleVarBindingSet(Set aGraphPattern, BindingSet aBindingSet) { - this.graphPattern = aGraphPattern; - - this.bindings = new HashSet<>(); + this(aGraphPattern); for (Binding b : aBindingSet) { this.add(new TripleVarBinding(this.graphPattern, b)); @@ -160,21 +165,17 @@ public TripleVarBindingSet merge(TripleVarBindingSet aGraphBindingSet) { } } else { // Cartesian product is the base case - boolean firstTime = true; - for (TripleVarBinding tvb1 : this.bindings) { - gbs.add(tvb1); + gbs.addAll(aGraphBindingSet.getBindings()); + gbs.addAll(this.bindings); + this.bindings.stream().parallel().forEach(tvb1 -> { for (TripleVarBinding otherB : aGraphBindingSet.getBindings()) { - if (firstTime) - gbs.add(otherB); - // always add a merged version of the two bindings, except when they conflict. if (!tvb1.isConflicting(otherB)) { gbs.add(tvb1.merge(otherB)); } } - firstTime = false; - } + }); } return gbs; @@ -199,6 +200,10 @@ public boolean isEmpty() { * @return */ public TripleVarBindingSet translate(Set graphPattern, Set match) { + LOG.trace("Translating binding set with '{}' bindings and '{}' matches.", this.bindings.size(), match.size()); + + long start = System.currentTimeMillis(); + TripleVarBindingSet newOne = new TripleVarBindingSet(graphPattern); TripleVarBinding toB; for (TripleVarBinding fromB : this.bindings) { @@ -247,6 +252,7 @@ public TripleVarBindingSet translate(Set graphPattern, Set newOne.add(toB); } } + return newOne; } @@ -287,4 +293,79 @@ public TripleVarBindingSet keepCompatible(TripleVarBindingSet bindingSet) { return newBS; } + + /** + * Prints the debugging table of this triple Var binding set + */ + public void printDebuggingTable() { + StringBuilder table = new StringBuilder(); + + // header row + int count = 0; + table.append("| Graph Pattern |"); + count++; + + List tvbindings = new ArrayList<>(this.getBindings()); + + for (int i = 0; i < tvbindings.size(); i++) { + table.append("Binding-" + i).append(" | "); + count++; + } + + table.append("\n"); + + // separator row + table.append("|"); + for (int i = 0; i < count; i++) { + table.append("-------|"); + } + table.append("\n"); + + // content rows + for (TriplePattern tp : this.graphPattern) { + // triple pattern + table.append(" | ").append(tp.toString()).append(" | "); + + // bindings + + for (TripleVarBinding tvb : tvbindings) { + Set nodes = tvb.getTripleNodes(tp); + if (!nodes.isEmpty()) { + + Node_Concrete subject = null; + Node_Concrete predicate = null; + Node_Concrete object = null; + + for (TripleNode tn : nodes) { + if (tn.nodeIdx == 0) { + subject = tvb.get(tn); + } else if (tn.nodeIdx == 1) + predicate = tvb.get(tn); + else if (tn.nodeIdx == 2) + object = tvb.get(tn); + } + + table.append(subject != null ? subject : formatNode(tp.getSubject())).append(" "); + table.append(predicate != null ? predicate : formatNode(tp.getPredicate())).append(" "); + table.append(object != null ? object : formatNode(tp.getObject())).append(" "); + + } else { + table.append(""); + } + + table.append(" | "); + } + + table.append("\n"); + } + System.out.println(table.toString()); + } + + private String formatNode(Node n) { + + String before = ""; + String after = ""; + + return n.isVariable() ? before + TriplePattern.trunc(n) + after : TriplePattern.trunc(n); + } } 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 a3448d50..9575fbf9 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 @@ -53,12 +53,12 @@ public Set getAllNeighbours() { public boolean addResultBindingSetInput(RuleNode aNeighbor, TripleVarBindingSet aBindingSet) { assert (antecedentNeighbours.keySet().contains(aNeighbor)); TripleVarBindingSet filteredBS = aBindingSet; - if (this.filterBindingSetOutput != null) { + if (this.filterBindingSetOutput != null && !this.hasProactiveParent(aNeighbor)) { filteredBS = aBindingSet.keepCompatible(this.filterBindingSetOutput); } var changed = this.resultBindingSetInput.add(aNeighbor, filteredBS); - if (changed && this.filterBindingSetOutput == null) { + if (changed && this.filterBindingSetOutput == null && this.hasProactiveParent(aNeighbor)) { var previousBindingSetOutput = this.filterBindingSetOutput; this.filterBindingSetOutput = this.resultBindingSetInput.get(); @@ -94,4 +94,12 @@ public TripleVarBindingSet getFilterBindingSetInput() { return null; } + /** + * Prints the binding set store of this Rule Node to the std out in a markdown + * table format. + */ + public void printResultBindingSetInputStore() { + this.resultBindingSetInput.printDebuggingTable(); + } + } 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 681a47fc..4f01f151 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 @@ -1,11 +1,19 @@ package eu.knowledge.engine.reasoner.rulenode; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; +import org.apache.jena.graph.Node; +import org.apache.jena.graph.Node_Concrete; + +import eu.knowledge.engine.reasoner.BaseRule; +import eu.knowledge.engine.reasoner.api.TripleNode; import eu.knowledge.engine.reasoner.api.TriplePattern; +import eu.knowledge.engine.reasoner.api.TripleVarBinding; import eu.knowledge.engine.reasoner.api.TripleVarBindingSet; /** @@ -98,4 +106,100 @@ public String toString() { return "BindingSetStore [neighborBindingSet=" + neighborBindingSet + "]"; } + private String getName(BaseRule r) { + if (r.getName().isEmpty()) { + return r.toString(); + } else { + return r.getName(); + } + } + + public void printDebuggingTable() { + StringBuilder table = new StringBuilder(); + + List allNeighbors = new ArrayList<>(neighbors); + + // header row + int count = 0; + table.append("| Graph Pattern |"); + count++; + for (RuleNode 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(" | "); + count++; + } + } else { + table.append(this.getName(neighbor.getRule())).append(" | "); + count++; + } + } + + table.append("\n"); + + // separator row + table.append("|"); + for (int i = 0; i < count; i++) { + table.append("-------|"); + } + table.append("\n"); + + // content rows + for (TriplePattern tp : this.graphPattern) { + // triple pattern + table.append(" | ").append(tp.toString()).append(" | "); + + // bindings + for (RuleNode neigh : allNeighbors) { + + TripleVarBindingSet tvbs = this.neighborBindingSet.get(neigh); + + if (tvbs != null) { + for (TripleVarBinding tvb : tvbs.getBindings()) { + Set nodes = tvb.getTripleNodes(tp); + if (!nodes.isEmpty()) { + + Node_Concrete subject = null; + Node_Concrete predicate = null; + Node_Concrete object = null; + + for (TripleNode tn : nodes) { + if (tn.nodeIdx == 0) { + subject = tvb.get(tn); + } else if (tn.nodeIdx == 1) + predicate = tvb.get(tn); + else if (tn.nodeIdx == 2) + object = tvb.get(tn); + } + +// tp.getSubject().isVariable() ? + + table.append(subject != null ? subject : formatNode(tp.getSubject())).append(" "); + table.append(predicate != null ? predicate : formatNode(tp.getPredicate())).append(" "); + table.append(object != null ? object : formatNode(tp.getObject())).append(" "); + + } else { + table.append(""); + } + + table.append(" | "); + } + } else { + table.append("|"); + } + } + table.append("\n"); + } + System.out.println(table.toString()); + } + + private String formatNode(Node n) { + + String before = ""; + String after = ""; + + return n.isVariable() ? before + TriplePattern.trunc(n) + after : TriplePattern.trunc(n); + } + } 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 f367f46a..e8303a0c 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 @@ -76,4 +76,13 @@ public TripleVarBindingSet getFilterBindingSetInput() { public Set getAllSameLoopNeighbors() { return new HashSet<>(); } + + /** + * Prints the binding set store of this Rule Node to the std out in a markdown + * table format. + */ + public void printInputBindingSetStore() { + this.filterBindingSetInput.printDebuggingTable(); + } + } 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 6dcab97b..f1c2a684 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,6 +11,10 @@ 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.ConsSide; @@ -24,6 +28,8 @@ */ public class FullRuleNode extends RuleNode implements AntSide, ConsSide { + private static final Logger LOG = LoggerFactory.getLogger(FullRuleNode.class); + private BindingSetStore resultBindingSetInput; private BindingSetStore filterBindingSetInput; private TripleVarBindingSet resultBindingSetOutput; @@ -77,12 +83,12 @@ public boolean addFilterBindingSetInput(RuleNode aNeighbor, TripleVarBindingSet public boolean addResultBindingSetInput(RuleNode aNeighbor, TripleVarBindingSet bs) { TripleVarBindingSet filteredBS = bs; - if (this.filterBindingSetOutput != null) { + if (this.filterBindingSetOutput != null && !this.hasProactiveParent(aNeighbor)) { filteredBS = bs.keepCompatible(this.filterBindingSetOutput); } var changed = this.resultBindingSetInput.add(aNeighbor, filteredBS); - if (changed && this.filterBindingSetOutput == null) { + if (changed && this.filterBindingSetOutput == null && this.hasProactiveParent(aNeighbor)) { var previousBindingSetOutput = this.filterBindingSetOutput; this.filterBindingSetOutput = this.resultBindingSetInput.get(); @@ -135,8 +141,7 @@ public void transformFilterBS() { this.isFilterBindingSetOutputDirty = true; } catch (InterruptedException | ExecutionException e) { - // TODO - e.printStackTrace(); + LOG.error("{}", e); } } @@ -259,4 +264,21 @@ public boolean shouldPropagateFilterBindingSetOutput() { public boolean shouldPropagateResultBindingSetOutput() { return this.isResultBindingSetOutputDirty; } + + /** + * Prints the binding set store of this Rule Node to the std out in a markdown + * table format. + */ + public void printResultBindingSetInputStore() { + this.resultBindingSetInput.printDebuggingTable(); + } + + /** + * Prints the binding set store of this Rule Node to the std out in a markdown + * table format. + */ + public void printFilterBindingSetInputStore() { + this.filterBindingSetInput.printDebuggingTable(); + } + } diff --git a/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulenode/RuleNode.java b/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulenode/RuleNode.java index 6fad2058..5d34df42 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulenode/RuleNode.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulenode/RuleNode.java @@ -1,14 +1,18 @@ package eu.knowledge.engine.reasoner.rulenode; +import java.util.Deque; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedList; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.Future; +import eu.knowledge.engine.reasoner.AntSide; import eu.knowledge.engine.reasoner.BaseRule; import eu.knowledge.engine.reasoner.Match; +import eu.knowledge.engine.reasoner.ProactiveRule; import eu.knowledge.engine.reasoner.api.TriplePattern; import eu.knowledge.engine.reasoner.api.TripleVarBindingSet; @@ -115,4 +119,32 @@ public void setResultBindingSetOutputPropagated() { this.isResultBindingSetOutputDirty = false; } + /** + * Follows the antecedents of this neighbor until they are all visited. If one + * of the antecedent neighbors is a ProactiveRule returns {@code true}, {@code + * false} otherwise. + */ + protected boolean hasProactiveParent(RuleNode aNeighbor) { + + Set visited = new HashSet<>(); + Deque stack = new LinkedList<>(); + + stack.push(aNeighbor); + + while (!stack.isEmpty()) { + var current = stack.pop(); + if (visited.contains(current)) + continue; + + if (aNeighbor.getRule() instanceof ProactiveRule) { + return true; + } else if (aNeighbor instanceof AntSide) { + stack.addAll(((AntSide) aNeighbor).getAntecedentNeighbours().keySet()); + } + visited.add(current); + } + + return false; + } + } 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 00dc63a7..7166ac05 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 @@ -29,52 +29,48 @@ public class MatchNode { /** * All other rules in the {@link MatchNode#store} whose consequents match this - * rule's antecedent according to the - * {@link MatchStrategy#FIND_ONLY_FULL_MATCHES} + * rule's antecedent according to the {@link MatchStrategy#NORMAL_LEVEL} */ - private Map> antecedentNeighborsFull; + private Map> antecedentNeighborsNormal; /** * All other rules in the {@link MatchNode#store} whose consequents match this - * rule's antecedent according to the - * {@link MatchStrategy#FIND_ONLY_BIGGEST_MATCHES} + * rule's antecedent according to the {@link MatchStrategy#ADVANCED_LEVEL} */ - private Map> antecedentNeighborsBiggest; + private Map> antecedentNeighborsAdvanced; /** * All other rules in the {@link MatchNode#store} whose consequents match this - * rule's antecedent according to the {@link MatchStrategy#FIND_ALL_MATCHES} + * rule's antecedent according to the {@link MatchStrategy#ULTRA_LEVEL} */ - private Map> antecedentNeighborsAll; + private Map> antecedentNeighborsUltra; /** * All other rules in the {@link MatchNode#store} whose antecedents match this - * rule's consequent according to the - * {@link MatchStrategy#FIND_ONLY_FULL_MATCHES} + * rule's consequent according to the {@link MatchStrategy#NORMAL_LEVEL} */ - private Map> consequentNeighborsFull; + private Map> consequentNeighborsNormal; /** * All other rules in the {@link MatchNode#store} whose antecedents match this - * rule's consequent according to the - * {@link MatchStrategy#FIND_ONLY_BIGGEST_MATCHES} + * rule's consequent according to the {@link MatchStrategy#ADVANCED_LEVEL} */ - private Map> consequentNeighborsBiggest; + private Map> consequentNeighborsAdvanced; /** * All other rules in the {@link MatchNode#store} whose antecedents match this - * rule's consequent according to the {@link MatchStrategy#FIND_ALL_MATCHES} + * rule's consequent according to the {@link MatchStrategy#ULTRA_LEVEL} */ - private Map> consequentNeighborsAll; + private Map> consequentNeighborsUltra; public MatchNode(BaseRule aRule) { this.rule = aRule; - this.antecedentNeighborsFull = new HashMap<>(); - this.antecedentNeighborsBiggest = new HashMap<>(); - this.antecedentNeighborsAll = new HashMap<>(); - this.consequentNeighborsFull = new HashMap<>(); - this.consequentNeighborsBiggest = new HashMap<>(); - this.consequentNeighborsAll = new HashMap<>(); + this.antecedentNeighborsNormal = new HashMap<>(); + this.antecedentNeighborsAdvanced = new HashMap<>(); + this.antecedentNeighborsUltra = new HashMap<>(); + this.consequentNeighborsNormal = new HashMap<>(); + this.consequentNeighborsAdvanced = new HashMap<>(); + this.consequentNeighborsUltra = new HashMap<>(); } public BaseRule getRule() { @@ -82,21 +78,21 @@ public BaseRule getRule() { } public void setConsequentNeighbor(BaseRule aRule, Set someMatches) { - this.setConsequentNeighbor(aRule, someMatches, MatchStrategy.FIND_ALL_MATCHES); + this.setConsequentNeighbor(aRule, someMatches, MatchStrategy.ULTRA_LEVEL); } public void setAntecedentNeighbor(BaseRule aRule, Set someMatches) { - this.setAntecedentNeighbor(aRule, someMatches, MatchStrategy.FIND_ALL_MATCHES); + this.setAntecedentNeighbor(aRule, someMatches, MatchStrategy.ULTRA_LEVEL); } public void setConsequentNeighbor(BaseRule aRule, Set someMatches, MatchStrategy aStrategy) { Map> neighbors; - if (aStrategy.equals(MatchStrategy.FIND_ONLY_FULL_MATCHES)) { - neighbors = this.consequentNeighborsFull; - } else if (aStrategy.equals(MatchStrategy.FIND_ONLY_BIGGEST_MATCHES)) { - neighbors = this.consequentNeighborsBiggest; - } else if (aStrategy.equals(MatchStrategy.FIND_ALL_MATCHES)) { - neighbors = this.consequentNeighborsAll; + 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<>(); @@ -109,12 +105,12 @@ public void setConsequentNeighbor(BaseRule aRule, Set someMatches, MatchS public void setAntecedentNeighbor(BaseRule aRule, Set someMatches, MatchStrategy aStrategy) { Map> neighbors; - if (aStrategy.equals(MatchStrategy.FIND_ONLY_FULL_MATCHES)) { - neighbors = this.antecedentNeighborsFull; - } else if (aStrategy.equals(MatchStrategy.FIND_ONLY_BIGGEST_MATCHES)) { - neighbors = this.antecedentNeighborsBiggest; - } else if (aStrategy.equals(MatchStrategy.FIND_ALL_MATCHES)) { - neighbors = this.antecedentNeighborsAll; + 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<>(); @@ -126,20 +122,20 @@ public void setAntecedentNeighbor(BaseRule aRule, Set someMatches, MatchS } public Map> getConsequentNeighbors() { - return this.getConsequentNeighbors(MatchStrategy.FIND_ALL_MATCHES); + return this.getConsequentNeighbors(MatchStrategy.ULTRA_LEVEL); } public Map> getAntecedentNeighbors() { - return this.getAntecedentNeighbors(MatchStrategy.FIND_ALL_MATCHES); + return this.getAntecedentNeighbors(MatchStrategy.ULTRA_LEVEL); } public Map> getConsequentNeighbors(MatchStrategy aStrategy) { - if (aStrategy.equals(MatchStrategy.FIND_ONLY_FULL_MATCHES)) { - return this.consequentNeighborsFull; - } else if (aStrategy.equals(MatchStrategy.FIND_ONLY_BIGGEST_MATCHES)) { - return this.consequentNeighborsBiggest; - } else if (aStrategy.equals(MatchStrategy.FIND_ALL_MATCHES)) { - return this.consequentNeighborsAll; + 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; @@ -147,12 +143,12 @@ public Map> getConsequentNeighbors(MatchStrategy aStrategy) } public Map> getAntecedentNeighbors(MatchStrategy aStrategy) { - if (aStrategy.equals(MatchStrategy.FIND_ONLY_FULL_MATCHES)) { - return this.antecedentNeighborsFull; - } else if (aStrategy.equals(MatchStrategy.FIND_ONLY_BIGGEST_MATCHES)) { - return this.antecedentNeighborsBiggest; - } else if (aStrategy.equals(MatchStrategy.FIND_ALL_MATCHES)) { - return this.antecedentNeighborsAll; + 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; @@ -167,12 +163,12 @@ public RuleStore getStore() { } public void reset() { - this.antecedentNeighborsFull.clear(); - this.antecedentNeighborsBiggest.clear(); - this.antecedentNeighborsAll.clear(); - this.consequentNeighborsFull.clear(); - this.consequentNeighborsBiggest.clear(); - this.consequentNeighborsAll.clear(); + this.antecedentNeighborsNormal.clear(); + this.antecedentNeighborsAdvanced.clear(); + this.antecedentNeighborsUltra.clear(); + this.consequentNeighborsNormal.clear(); + this.consequentNeighborsAdvanced.clear(); + this.consequentNeighborsUltra.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 ef239b05..f0fc5442 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 @@ -4,7 +4,10 @@ package eu.knowledge.engine.reasoner.rulestore; import java.net.URI; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -72,7 +75,7 @@ public Set getRules() { * @see #getAntecedentNeighbors(BaseRule, MatchStrategy) */ public Map> getAntecedentNeighbors(BaseRule aRule) { - return this.getAntecedentNeighbors(aRule, MatchStrategy.FIND_ALL_MATCHES); + return this.getAntecedentNeighbors(aRule, MatchStrategy.NORMAL_LEVEL); } /** @@ -88,32 +91,37 @@ public Map> getAntecedentNeighbors(BaseRule aRule, MatchStr assert aRuleNode != null; - for (BaseRule someRule : this.getRules()) { - MatchNode someRuleNode = this.ruleToRuleNode.get(someRule); - if (!someRule.getConsequent().isEmpty() - && !aRuleNode.getAntecedentNeighbors(aStrategy).containsKey(someRule)) { - Set someMatches = aRule.antecedentMatches(someRule.getConsequent(), aStrategy); - if (!someMatches.isEmpty()) { - aRuleNode.setAntecedentNeighbor(someRule, someMatches, aStrategy); - someRuleNode.setConsequentNeighbor(aRule, Match.invertAll(someMatches), aStrategy); - } + Map> newMapping = BaseRule.getMatches(aRule, this.getRules(), true, aStrategy); - } + 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); + } + + 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<>()); } - return aRuleNode.getAntecedentNeighbors(aStrategy); + Set matches = newMapping.get(entryToAdd.getKey()); + matches.add(entryToAdd.getValue()); } /** * @see #getConsequentNeighbors(BaseRule, MatchStrategy) */ public Map> getConsequentNeighbors(BaseRule aRule) { - return this.getConsequentNeighbors(aRule, MatchStrategy.FIND_ALL_MATCHES); + return this.getConsequentNeighbors(aRule, MatchStrategy.NORMAL_LEVEL); } /** * Calculate the consequent neighbors of this rule. That means all the other - * rules in {@code store} whose antecedent matches this rule's antecedent. Note + * rules in {@code store} whose antecedent matches this rule's consequent. Note * that it also adds the same information to the neighbor.
* * This method is cached to improve performance. @@ -126,20 +134,14 @@ public Map> getConsequentNeighbors(BaseRule aRule, MatchStr assert aRuleNode != null; - for (BaseRule someRule : this.getRules()) { - MatchNode someRuleNode = this.ruleToRuleNode.get(someRule); - if (!someRule.getAntecedent().isEmpty() - && !aRuleNode.getConsequentNeighbors(aStrategy).containsKey(someRule)) { - Set someMatches = aRule.consequentMatches(someRule.getAntecedent(), aStrategy); - if (!someMatches.isEmpty()) { - aRuleNode.setConsequentNeighbor(someRule, someMatches, aStrategy); - someRuleNode.setAntecedentNeighbor(aRule, Match.invertAll(someMatches), aStrategy); - } + Map> newMapping = BaseRule.getMatches(aRule, this.getRules(), false, aStrategy); - } + 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); } - return aRuleNode.getConsequentNeighbors(aStrategy); + return newMapping; } /** @@ -162,8 +164,8 @@ public void printGraphVizCode(ReasonerPlan aPlan) { String width = "2"; StringBuilder sb = new StringBuilder(); - sb.append("Visualize on website: http://viz-js.com/\n"); - sb.append("digraph {").append("\n"); + + sb.append("digraph {\n"); Map ruleToName = new HashMap<>(); int ruleNumber = 1; @@ -199,8 +201,9 @@ public void printGraphVizCode(ReasonerPlan aPlan) { } - Set anteNeigh = this.getAntecedentNeighbors(r.getRule(), - aPlan != null ? aPlan.getMatchStrategy() : MatchStrategy.FIND_ONLY_FULL_MATCHES).keySet(); + Map> antecedentNeighbors = this.getAntecedentNeighbors(r.getRule(), + aPlan != null ? aPlan.getMatchStrategy() : MatchStrategy.NORMAL_LEVEL); + Set anteNeigh = antecedentNeighbors.keySet(); String neighName; for (BaseRule neighR : anteNeigh) { neighName = ruleToName.get(neighR); @@ -238,14 +241,19 @@ public void printGraphVizCode(ReasonerPlan aPlan) { pen = "color=\"" + color + "\", penwidth=\"" + width + "\""; } - sb.append(neighName).append(BaseRule.ARROW).append(currentName).append("[").append(pen).append("]") - .append("\n"); + int nrOfMatches = antecedentNeighbors.get(neighR).size(); + + sb.append(neighName).append(BaseRule.ARROW).append(currentName).append("[label=").append(nrOfMatches) + .append(" ").append(pen).append("]").append("\n"); } } sb.append("}"); - LOG.info(sb.toString()); + + LOG.info("Visualize on website: https://dreampuf.github.io/GraphvizOnline/#" + + URLEncoder.encode(sb.toString(), StandardCharsets.UTF_8).replaceAll("\\+", "%20") + "\n" + + sb.toString()); } private String toStringRule(BaseRule neighR) { 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 5d194227..6373452c 100644 --- a/reasoner/src/test/java/eu/knowledge/engine/reasoner/BindingTest.java +++ b/reasoner/src/test/java/eu/knowledge/engine/reasoner/BindingTest.java @@ -60,7 +60,7 @@ public void testFruitfulVsActualMatches() { BaseRule r = new BaseRule("test", toTriplePattern(gp2), new HashSet<>()); - Set matches = r.antecedentMatches(toTriplePattern(gp1), MatchStrategy.FIND_ALL_MATCHES); + Set matches = r.antecedentMatches(toTriplePattern(gp1), MatchStrategy.ULTRA_LEVEL); LOG.info("matches size: {}", matches.size()); diff --git a/reasoner/src/test/java/eu/knowledge/engine/reasoner/ForwardTest.java b/reasoner/src/test/java/eu/knowledge/engine/reasoner/ForwardTest.java index 3ed11c03..86737a2e 100644 --- a/reasoner/src/test/java/eu/knowledge/engine/reasoner/ForwardTest.java +++ b/reasoner/src/test/java/eu/knowledge/engine/reasoner/ForwardTest.java @@ -12,16 +12,8 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import org.apache.jena.graph.Node; import org.apache.jena.graph.Node_Literal; -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; @@ -30,12 +22,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.SinkBindingSetHandler; -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; @@ -667,4 +653,106 @@ public CompletableFuture handle(BindingSet bs) { assertNotNull(aBindingSetHandler1.getBindingSet()); assertFalse(aBindingSetHandler1.getBindingSet().isEmpty()); } + + /** + * This test checks whether combining results from two antecedent succeeds. + * + * @throws InterruptedException + * @throws ExecutionException + */ + @Test + public void testCorrectlyCombineMultipleNeighbors() throws InterruptedException, ExecutionException { + + var aStore = new RuleStore(); + + TriplePattern tp1 = new TriplePattern("?s1 ?o1"); + TriplePattern tp2 = new TriplePattern("?s2 ?o2"); + TriplePattern tp21 = new TriplePattern("?s2 ?o2"); + TriplePattern tp3 = new TriplePattern("?s3 ?o3"); + TriplePattern tp31 = new TriplePattern("?s3 ?o3"); + TriplePattern tp4 = new TriplePattern("?s4 ?o4"); + + // rule 1 + Set aConsequent = new HashSet<>(); + aConsequent.add(tp1); + ProactiveRule r1 = new ProactiveRule(new HashSet<>(), aConsequent); + aStore.addRule(r1); + + // rule 2 + var hashSet = new HashSet(); + hashSet.add(tp2); + var hashSet2 = new HashSet(); + hashSet2.add(tp21); + Rule r2 = new Rule(hashSet, hashSet2, bs -> { + + BindingSet aBS = new BindingSet(); + + Binding e = new Binding("s2", ""); + e.put("o2", ""); + aBS.add(e); + + var future = new CompletableFuture(); + future.complete(aBS); + + return future; + + }); + aStore.addRule(r2); + + // rule 3 + hashSet = new HashSet(); + hashSet.add(tp3); + hashSet2 = new HashSet(); + hashSet2.add(tp31); + Rule r3 = new Rule(hashSet, hashSet2, bs -> { + + BindingSet aBS = new BindingSet(); + + Binding e = new Binding("s3", ""); + e.put("o3", ""); + aBS.add(e); + + var future = new CompletableFuture(); + future.complete(aBS); + + return future; + + }); + aStore.addRule(r3); + + // rule 4 + Set anAntecedent = new HashSet<>(); + anAntecedent.add(tp4); + MyBindingSetHandler aSinkBindingSetHandler = new MyBindingSetHandler(); + Rule r4 = new Rule(anAntecedent, aSinkBindingSetHandler); + aStore.addRule(r4); + + ReasonerPlan rp = new ReasonerPlan(aStore, r1); + + aStore.printGraphVizCode(rp); + + BindingSet bindingSet = new BindingSet(); + Binding e = new Binding("s1", ""); + e.put("o1", ""); + bindingSet.add(e); + + TaskBoard tb; + while ((tb = rp.execute(bindingSet)).hasTasks()) { + tb.executeScheduledTasks().get(); + } + + BindingSet bindingSet2 = aSinkBindingSetHandler.getBindingSet(); + System.out.println(bindingSet2); + + Binding e1 = new Binding("o4", ""); + e1.put("s4", ""); + + Binding e2 = new Binding("s4", ""); + e2.put("o4", ""); + + assertFalse(bindingSet2.isEmpty()); + assertTrue(bindingSet2.contains(e1)); + assertTrue(bindingSet2.contains(e2)); + + } } \ No newline at end of file diff --git a/reasoner/src/test/java/eu/knowledge/engine/reasoner/MinimalTest.java b/reasoner/src/test/java/eu/knowledge/engine/reasoner/MinimalTest.java index 7422697d..84585bbc 100644 --- a/reasoner/src/test/java/eu/knowledge/engine/reasoner/MinimalTest.java +++ b/reasoner/src/test/java/eu/knowledge/engine/reasoner/MinimalTest.java @@ -59,7 +59,7 @@ public void testConverter() throws InterruptedException, ExecutionException { // Start reasoning ReasonerPlan root = new ReasonerPlan(this.store, startRule); - System.out.println(root); + this.store.printGraphVizCode(root); 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 c9314e90..df6c293d 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 @@ -12,16 +12,19 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CompletableFuture; import org.apache.jena.sparql.sse.SSE; -import org.junit.jupiter.api.Disabled; 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; import eu.knowledge.engine.reasoner.BaseRule.MatchStrategy; import eu.knowledge.engine.reasoner.Match; +import eu.knowledge.engine.reasoner.ProactiveRule; import eu.knowledge.engine.reasoner.Rule; +import eu.knowledge.engine.reasoner.SinkBindingSetHandler; @TestInstance(Lifecycle.PER_CLASS) public class MatchTest { @@ -51,7 +54,7 @@ public void testGPMatcher() { Rule r = new Rule(new HashSet<>(), rhs); - Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.FIND_ALL_MATCHES); + Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.ULTRA_LEVEL); System.out.println(findMatchesWithConsequent); } @@ -66,7 +69,7 @@ public void testGPMatcher2() { Rule r = new Rule(new HashSet<>(), rhs); - Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.FIND_ALL_MATCHES); + Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.ULTRA_LEVEL); System.out.println(findMatchesWithConsequent); } @@ -83,7 +86,7 @@ public void testGPMatcher3() { Rule r = new Rule(new HashSet<>(), rhs); - Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.FIND_ALL_MATCHES); + Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.ULTRA_LEVEL); System.out.println(findMatchesWithConsequent); } @@ -97,7 +100,7 @@ public void testGPMatcher4() { Rule r = new Rule(new HashSet<>(), rhs); - Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.FIND_ALL_MATCHES); + Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.ULTRA_LEVEL); // there should be a match, but its mapping should be empty nothing needs to // happen to translate one to the other. @@ -114,7 +117,7 @@ public void testGPMatcher5() { Rule r = new Rule(new HashSet<>(), rhs); - Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.FIND_ALL_MATCHES); + Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.ULTRA_LEVEL); // there should be a match and its mapping should be empty because nothing needs // to happen to translate one to the other. @@ -134,7 +137,7 @@ public void testGPMatcher6() { Rule r = new Rule(new HashSet<>(), rhs); - Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.FIND_ALL_MATCHES); + Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.ULTRA_LEVEL); System.out.println(findMatchesWithConsequent); } @@ -159,9 +162,9 @@ public void testGPMatcher7() { Set obj = new HashSet<>( Arrays.asList(t1, t2, t3, t4, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16)); - Rule r = new Rule(new HashSet<>(), obj); + BaseRule r = new ProactiveRule(obj, new HashSet<>()); - Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.FIND_ONLY_FULL_MATCHES); + Set findMatchesWithConsequent = r.antecedentMatches(obj, MatchStrategy.NORMAL_LEVEL); System.out.println("Size: " + findMatchesWithConsequent.size()); for (Match m : findMatchesWithConsequent) { @@ -183,7 +186,7 @@ public void testGPMatcher8() { Rule r = new Rule(new HashSet<>(), tp2); - Set findMatchesWithConsequent = r.consequentMatches(tp1, MatchStrategy.FIND_ALL_MATCHES); + Set findMatchesWithConsequent = r.consequentMatches(tp1, MatchStrategy.ULTRA_LEVEL); System.out.println(findMatchesWithConsequent); } @@ -201,7 +204,7 @@ public void testGPMatcher9() { Rule r = new Rule(new HashSet<>(), tp2); - Set findMatchesWithConsequent = r.consequentMatches(tp1, MatchStrategy.FIND_ONLY_BIGGEST_MATCHES); + Set findMatchesWithConsequent = r.consequentMatches(tp1, MatchStrategy.ADVANCED_LEVEL); System.out.println(findMatchesWithConsequent); } @@ -265,8 +268,8 @@ public void testGPMatcher10OrderingWithinGraphPatternsShouldNotMatter() { Rule r = new Rule(new HashSet<>(), obj); Rule r2 = new Rule(new HashSet<>(), obj2); - Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.FIND_ONLY_FULL_MATCHES); - Set findMatchesWithConsequent2 = r2.consequentMatches(obj2, MatchStrategy.FIND_ONLY_FULL_MATCHES); + Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.NORMAL_LEVEL); + Set findMatchesWithConsequent2 = r2.consequentMatches(obj2, MatchStrategy.NORMAL_LEVEL); System.out.println("Size 1: " + findMatchesWithConsequent.size()); System.out.println(findMatchesWithConsequent); @@ -298,8 +301,8 @@ public void testGPMatcher11VariableNamesMatter() { Rule r = new Rule(new HashSet<>(), obj); Rule r2 = new Rule(new HashSet<>(), obj2); - Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.FIND_ONLY_FULL_MATCHES); - Set findMatchesWithConsequent2 = r2.consequentMatches(obj2, MatchStrategy.FIND_ONLY_FULL_MATCHES); + Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.NORMAL_LEVEL); + Set findMatchesWithConsequent2 = r2.consequentMatches(obj2, MatchStrategy.NORMAL_LEVEL); System.out.println("Size 1: " + findMatchesWithConsequent.size()); System.out.println(findMatchesWithConsequent); @@ -327,16 +330,18 @@ public void testGPMatcher12() { Set obj = new HashSet<>(Arrays.asList(/* t1, */ t5, t9, t8, t7, t6, t4, t3)); - Rule r = new Rule(new HashSet<>(), obj); + Rule r = new Rule(obj, (SinkBindingSetHandler) aBindingSet -> { + return new CompletableFuture(); + }); - Set findMatchesWithConsequent = r.consequentMatches( - new HashSet<>(Arrays.asList(/* t1, */ t5, t9, t8, t7, t6, t4, t3)), MatchStrategy.FIND_ALL_MATCHES); + Set findMatchesWithAntecedent = r.antecedentMatches( + new HashSet<>(Arrays.asList(/* t1, */ t5, t9, t8, t7, t6, t4, t3)), MatchStrategy.ADVANCED_LEVEL); - System.out.println("Size: " + findMatchesWithConsequent.size()); + System.out.println("Size: " + findMatchesWithAntecedent.size()); // System.out.println(findMatchesWithConsequent); int count = 0; - for (Match m : findMatchesWithConsequent) { + for (Match m : findMatchesWithAntecedent) { // System.out.println(m.getMatchingPatterns()); if (m.getMatchingPatterns().size() == 3) { @@ -418,7 +423,7 @@ public void testGPMatcherCardinalityTest() { Rule r = new Rule(new HashSet<>(), obj); Set findMatchesWithConsequent = r.consequentMatches(new HashSet<>(Arrays.asList(graphPattern)), - MatchStrategy.FIND_ALL_MATCHES); + MatchStrategy.SUPREME_LEVEL); System.out.println("graph pattern size " + gpSize + " gives matches size " + findMatchesWithConsequent.size() + "-" + getNumberOfMatches(gpSize)); @@ -468,7 +473,7 @@ public void testBugTranslate() { Rule r = new Rule(new HashSet<>(), rhs); - Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.FIND_ALL_MATCHES); + Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.ULTRA_LEVEL); BindingSet bs = Util.toBindingSet("s=,p=

,o="); @@ -491,7 +496,7 @@ public void testOtherBurgTranslate() { Rule r = new Rule(new HashSet<>(), rhs); - Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.FIND_ALL_MATCHES); + Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.ULTRA_LEVEL); BindingSet bs = Util.toBindingSet("p=,q=\"22.0\"^^"); @@ -542,7 +547,6 @@ public void testTranslateEmptyBindingSet() { assertTrue(tvbs2.isEmpty()); } - @Disabled // this unit test causes a out of memory after about 30 to 45 minutes. @Test public void testPloutosGPMatcher() { @@ -574,13 +578,155 @@ public void testPloutosGPMatcher() { Set obj = Util.toGP(gp); - Rule r = new Rule(new HashSet<>(), obj); + Rule r = new Rule(obj, new SinkBindingSetHandler() { + + @Override + public CompletableFuture handle(BindingSet aBindingSet) { + System.out.println("bla"); + return null; + } + }); System.out.println("NrOfMatches with " + obj.size() + " triple patterns: " + getNumberOfMatches(obj.size())); - Set findMatchesWithConsequent = r.consequentMatches(obj, MatchStrategy.FIND_ONLY_BIGGEST_MATCHES); + Set findMatchesWithAntecedent = r.antecedentMatches(obj, MatchStrategy.ADVANCED_LEVEL); + + System.out.println("Size: " + findMatchesWithAntecedent.size()); + + } + + @Test + public void testPloutosGPMatcher2() { + + String gp = """ + ?operation . + ?operation ?output . + ?operation ?parcel . + ?parcel ?crop . + ?crop ?cropType . + ?cropType