Skip to content

Commit

Permalink
Merge pull request #534 from TNO/add-reasonerplan-tostring
Browse files Browse the repository at this point in the history
Added a more useful ReasonerPlan toString.
  • Loading branch information
bnouwt authored Sep 30, 2024
2 parents 74da78a + 2e7882e commit f910f99
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 127 deletions.
71 changes: 44 additions & 27 deletions reasoner/src/main/java/eu/knowledge/engine/reasoner/BaseRule.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

import org.apache.jena.sparql.core.Var;
import org.slf4j.Logger;
Expand All @@ -34,6 +36,11 @@ public class BaseRule {

public static final String ARROW = "->";

/**
* Precalculated hashcode to improve performance of the matching algorithm.
*/
private int hashCodeValue;

/**
* A comparator to make sure the smaller matches collection is ordered from big
* to small.
Expand Down Expand Up @@ -147,6 +154,7 @@ protected BaseRule(Set<TriplePattern> anAntecedent, Set<TriplePattern> aConseque

this.antecedent = anAntecedent;
this.consequent = aConsequent;
this.hashCodeValue = this.calcHashCode();
}

public static Set<Var> getVars(Set<TriplePattern> aPattern) {
Expand Down Expand Up @@ -287,8 +295,7 @@ public String getName() {
return name;
}

@Override
public int hashCode() {
private int calcHashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((antecedent == null) ? 0 : antecedent.hashCode());
Expand All @@ -297,6 +304,11 @@ public int hashCode() {
return result;
}

@Override
public int hashCode() {
return this.hashCodeValue;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
Expand Down Expand Up @@ -393,13 +405,14 @@ public static Map<BaseRule, Set<Match>> getMatches(BaseRule aTargetRule, Set<Bas
Map<TriplePattern, Set<CombiMatch>> 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())
if (targetGP.isEmpty() || (config.contains(MatchFlag.FULLY_COVERED)
&& combiMatchesPerTriple.keySet().size() < targetGP.size()))
return new HashMap<>();

printCombiMatchesPerTriple(aTargetRule, combiMatchesPerTriple);

List<CombiMatch> biggestMatches = new ArrayList<>();
List<CombiMatch> smallerMatches = new ArrayList<>();
List<CombiMatch> toBeAddedToBiggestMatches = null, toBeAddedToSmallerMatches = null;
Expand Down Expand Up @@ -453,31 +466,36 @@ public static Map<BaseRule, Set<Match>> getMatches(BaseRule aTargetRule, Set<Bas
// 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);
}
// do this 'costly' merge operation in parallel
var newCombiMatches = smallerMatches.stream().parallel().map(aSmallerMatch -> {
return mergeCombiMatches(candidateCombiMatch, aSmallerMatch, config);
}).filter(Objects::nonNull).sorted(new CombiMatchSizeComparator()).collect(Collectors.toList());

// determine where to add new combi matches
for (CombiMatch newCombiMatch : newCombiMatches) {

// merge successful, add to smaller matches
if (candidateWasMerged) {
if (isSubCombiMatch(newCombiMatch, toBeAddedToBiggestMatches)) {
toBeAddedToSmallerMatches.add(newCombiMatch);
} else {
// add to biggest matches
candidateWasMerged = true;
toBeAddedToBiggestMatches.add(newCombiMatch);
candidateWasMerged = true;
}

} else {
// add to biggest matches
candidateWasMerged = true;
toBeAddedToBiggestMatches.add(newCombiMatch);
}
}
}

if (!candidateWasMerged && !config.contains(MatchFlag.FULLY_COVERED))
toBeAddedToBiggestMatches.add(candidateCombiMatch);
else
toBeAddedToSmallerMatches.add(candidateCombiMatch);
if (!config.contains(MatchFlag.FULLY_COVERED)) {
if (!candidateWasMerged)
toBeAddedToBiggestMatches.add(candidateCombiMatch);
else
toBeAddedToSmallerMatches.add(candidateCombiMatch);
}
}

// update collections
Expand All @@ -494,8 +512,6 @@ public static Map<BaseRule, Set<Match>> getMatches(BaseRule aTargetRule, Set<Bas
// add all toBeAddedMatches
biggestMatches.addAll(toBeAddedToBiggestMatches);
smallerMatches.addAll(toBeAddedToSmallerMatches);

Collections.sort(smallerMatches, new CombiMatchSizeComparator());
}

toBeAddedToBiggestMatches = null;
Expand Down Expand Up @@ -538,7 +554,8 @@ private static boolean isSubCombiMatch(CombiMatch aSmallerMatch, List<CombiMatch
return false;
}

private static void printCombiMatchesPerTriple(Map<TriplePattern, Set<CombiMatch>> combiMatchesPerTriple) {
private static void printCombiMatchesPerTriple(BaseRule aTargetRule,
Map<TriplePattern, Set<CombiMatch>> combiMatchesPerTriple) {
StringBuilder sb = new StringBuilder();

int total = 1;
Expand All @@ -547,7 +564,7 @@ private static void printCombiMatchesPerTriple(Map<TriplePattern, Set<CombiMatch
sb.append(combiMatch.size()).append(" * ");
}

LOG.trace("{} = {}", total, sb.toString());
LOG.trace("{}: {} = {}", aTargetRule.getName(), total, sb.toString());

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,13 +267,14 @@ 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.matchConfig).containsKey(aParent)) {
Map<BaseRule, Set<Match>> antecedentNeighbors = this.store.getAntecedentNeighbors(aRule, this.matchConfig);
if (aParent != null && antecedentNeighbors.containsKey(aParent)) {
ourAntecedentFullyMatchesParentConsequent = antecedentFullyMatchesConsequent(aRule, aParent,
this.store.getAntecedentNeighbors(aRule, this.matchConfig).get(aParent));
antecedentNeighbors.get(aParent));
}

if (!ourAntecedentFullyMatchesParentConsequent) {
this.store.getAntecedentNeighbors(aRule, this.matchConfig).forEach((rule, matches) -> {
antecedentNeighbors.forEach((rule, matches) -> {
if (!(rule instanceof ProactiveRule)) {
assert reasonerNode instanceof AntSide;
var newNode = createOrGetReasonerNode(rule, aRule);
Expand Down Expand Up @@ -357,4 +358,9 @@ public RuleStore getStore() {
return this.store;
}

@Override
public String toString() {
return "ReasonerPlan [link=" + this.store.getGraphVizCode(this, true) + "]";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -69,7 +70,7 @@ public void addRules(Set<BaseRule> someRules) {
*/
public Set<BaseRule> getRules() {

return this.ruleToRuleNode.values().stream().map(rn -> rn.getRule()).collect(Collectors.toSet());
return this.ruleToRuleNode.keySet();
}

/**
Expand Down Expand Up @@ -155,30 +156,34 @@ public void reset() {
}
}

public void printGraphVizCode(ReasonerPlan aPlan) {
LOG.info(getGraphVizCode(aPlan, false));
}

/**
* Prints all the rules and the connections between them in GraphViz encoding.
* Use code in: {@link http://viz-js.com/}
* Use code in: {@link https://dreampuf.github.io/GraphvizOnline/}
*/
public void printGraphVizCode(ReasonerPlan aPlan) {
public void printGraphVizCode(ReasonerPlan aPlan, boolean urlOnly) {
LOG.info(getGraphVizCode(aPlan, urlOnly));
}

public String getGraphVizCode(ReasonerPlan aPlan, boolean urlOnly) {

String color = "red";
String width = "2";

StringBuilder sb = new StringBuilder();

sb.append("digraph {\n");
sb.append("strict digraph {\n");
Map<BaseRule, String> ruleToName = new HashMap<>();

int ruleNumber = 1;

for (MatchNode r : ruleToRuleNode.values()) {

String currentName = ruleToName.get(r.getRule());
boolean sourceInPlan = false, destInPlan = false;
if (currentName == null) {
currentName = /* "rule" + ruleNumber; */ generateName(r.getRule());
assert !currentName.isEmpty();
ruleNumber++;
String replaceAll = toStringRule(r.getRule()).replaceAll("\\\"", "\\\\\"");

// check the colouring
Expand All @@ -187,7 +192,6 @@ public void printGraphVizCode(ReasonerPlan aPlan) {
RuleNode rn = aPlan.getRuleNodeForRule(r.getRule());
if (rn != null) {
pen = "color=\"" + color + "\", penwidth=\"" + width + "\",";
sourceInPlan = true;
}
}

Expand All @@ -201,16 +205,17 @@ public void printGraphVizCode(ReasonerPlan aPlan) {
ruleToName.put(r.getRule(), currentName);

}

Map<BaseRule, Set<Match>> antecedentNeighbors = r.getAntecedentNeighbors();
Set<BaseRule> anteNeigh = antecedentNeighbors.keySet();
String neighName;

for (BaseRule neighR : anteNeigh) {
neighName = ruleToName.get(neighR);

if (neighName == null) {
neighName = /* "rule" + ruleNumber; */ generateName(neighR);
assert !neighName.isEmpty();
ruleNumber++;
String replaceAll = toStringRule(neighR).replaceAll("\\\"", "\\\\\"");

// check the colouring
Expand All @@ -219,7 +224,6 @@ public void printGraphVizCode(ReasonerPlan aPlan) {
RuleNode rn = aPlan.getRuleNodeForRule(neighR);
if (rn != null) {
pen = "color=\"" + color + "\", penwidth=\"" + width + "\",";
destInPlan = true;
}
}

Expand All @@ -230,6 +234,7 @@ public void printGraphVizCode(ReasonerPlan aPlan) {

sb.append(neighName).append("[").append(shape).append(pen).append("tooltip=").append("\"")
.append(replaceAll).append("\"").append("]").append("\n");

ruleToName.put(neighR, neighName);
}

Expand All @@ -251,9 +256,9 @@ public void printGraphVizCode(ReasonerPlan aPlan) {

sb.append("}");

LOG.info("Visualize on website: https://dreampuf.github.io/GraphvizOnline/#"
+ URLEncoder.encode(sb.toString(), StandardCharsets.UTF_8).replaceAll("\\+", "%20") + "\n"
+ sb.toString());
return "Visualize on website: https://dreampuf.github.io/GraphvizOnline/#"
+ URLEncoder.encode(sb.toString(), StandardCharsets.UTF_8).replaceAll("\\+", "%20")
+ (urlOnly ? "" : "\n" + sb.toString());
}

private String toStringRule(BaseRule neighR) {
Expand All @@ -277,6 +282,7 @@ private String trimAtLength(String aString, int aLength) {
*
* @param r
* @return
* @throws NoSuchAlgorithmException
*/
private String generateName(BaseRule r) {

Expand All @@ -302,7 +308,29 @@ private String generateName(BaseRule r) {

String consequent = trimAtLength(sb.toString(), MAX_STR_LENGTH);

return "\"" + Integer.toHexString(r.hashCode()) + "\\n" + antecedent + "->\\n" + consequent + "\"";
String name = r.getName();
MessageDigest digest;
byte[] encodedhash = new byte[0];
try {
digest = MessageDigest.getInstance("SHA-256");
encodedhash = digest.digest(name.getBytes(StandardCharsets.UTF_8));
} catch (NoSuchAlgorithmException e) {
LOG.error("{}", e);
}

return "\"" + bytesToHex(encodedhash) + "\\n" + antecedent + "->\\n" + consequent + "\"";
}

private static String bytesToHex(byte[] hash) {
StringBuilder hexString = new StringBuilder(2 * hash.length);
for (int i = 0; i < hash.length; i++) {
String hex = Integer.toHexString(0xff & hash[i]);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}

private String generateName(TriplePattern tp) {
Expand Down
Loading

0 comments on commit f910f99

Please sign in to comment.