diff --git a/src/main/java/org/fujaba/graphengine/PatternEngine.java b/src/main/java/org/fujaba/graphengine/PatternEngine.java index a88e98c..51586c6 100644 --- a/src/main/java/org/fujaba/graphengine/PatternEngine.java +++ b/src/main/java/org/fujaba/graphengine/PatternEngine.java @@ -1,9 +1,12 @@ package org.fujaba.graphengine; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import javax.naming.spi.DirStateFactory.Result; + import org.fujaba.graphengine.graph.Graph; import org.fujaba.graphengine.graph.Node; import org.fujaba.graphengine.pattern.PatternAttribute; @@ -155,6 +158,373 @@ private static int indexOf(ArrayList graphs, Graph graph) { } return -1; } + + private static ArrayList> doDepthFirstExplore(ArrayList patternNodes) { + ArrayList> result = new ArrayList>(); + ArrayList unprocessed = new ArrayList(patternNodes); + while (unprocessed.size() > 0) { + PatternNode start = unprocessed.remove(0); + ArrayList open = new ArrayList(); + ArrayList closed = new ArrayList(); + open.add(start); + while (open.size() > 0) { + PatternNode current = open.remove(0); + closed.add(current); + ArrayList succ = new ArrayList(); + for (PatternEdge patternEdge: current.getPatternEdges()) { + if (!open.contains(patternEdge.getTarget()) + && !closed.contains(patternEdge.getTarget()) + && !succ.contains(patternEdge.getTarget()) + && unprocessed.contains(patternEdge.getTarget())) { + succ.add(patternEdge.getTarget()); + } + } + for (PatternNode ingoing: unprocessed) { + for (PatternEdge patternEdge: ingoing.getPatternEdges()) { + if (patternEdge.getTarget() == current + && !open.contains(ingoing) + && !closed.contains(ingoing) + && !succ.contains(ingoing)) { + succ.add(ingoing); + break; + } + } + } + open.addAll(succ); + } + unprocessed.removeAll(closed); + result.add(closed); + } + return result; + } + + private static ArrayList> calculateNodeMatchingLists(PatternGraph pattern) { + ArrayList> result = new ArrayList>(); + result.add(new ArrayList()); // positive list + + // first check which nodes are positive and which nodes are negative (don't mind 'neutral' nodes): + ArrayList positiveNodes = new ArrayList(); + ArrayList negativeNodes = new ArrayList(); + for (PatternNode node: pattern.getPatternNodes()) { + switch (node.getAction()) { + case "==": + case "-": + positiveNodes.add(node); + break; + case "+": + break; + case "!=": + negativeNodes.add(node); + break; + default: + try { + throw new IOException("can't handle PatternNode action '" + node.getAction() + "'!"); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + // now do continuous depth-first 'explore's until all positive nodes are in a 'clever' order for later checks: + ArrayList> positiveLists = doDepthFirstExplore(positiveNodes); + for (ArrayList listPart: positiveLists) { + result.get(0).addAll(listPart); + } + + // finally do continuous depth-first 'explore's for negative nodes, but this time yielding separate node lists: + ArrayList> negativeLists = doDepthFirstExplore(negativeNodes); + result.addAll(negativeLists); + + return result; + } + + private static ArrayList>> findPossibleMatchesForPositiveAndNegativeNodes(Graph graph, ArrayList> nodeMatchLists) { + // now check for 'loosely matched candidates' of nodes to match (level == 0: positive nodes, level > 0: negative node sets): + ArrayList>> couldMatch = new ArrayList>>(); + for (int level = 0; level < nodeMatchLists.size(); ++level) { + couldMatch.add(new ArrayList>()); + for (int i = 0; i < nodeMatchLists.get(level).size(); ++i) { + PatternNode patternNode = nodeMatchLists.get(level).get(i); + couldMatch.get(level).add(new ArrayList()); + nodeMatch: for (int j = 0; j < graph.getNodes().size(); ++j) { + Node node = graph.getNodes().get(j); + // check node attribute expression: + if (!PatternEngine.evaluate(node, patternNode.getAttributeMatchExpression())) { + continue nodeMatch; + } + // check existence of outgoing edges: + for (PatternEdge patternEdge: patternNode.getPatternEdges()) { + if ("+".equals(patternEdge.getAction())) { + continue; + } + // TODO verify: we shouldn't mind edges from positive to negative nodes here (yet): + boolean dontMind = !"!=".equals(patternEdge.getSource().getAction()) && "!=".equals(patternEdge.getSource().getAction()); + if (!dontMind) { + boolean exists = !(node.getEdges(patternEdge.getName()) == null || node.getEdges(patternEdge.getName()).size() == 0); + if (("!=".equals(patternEdge.getAction()) && exists) || !"!=".equals(patternEdge.getAction()) && !exists) { + continue nodeMatch; + } + } + } + // check every attribute's expression: + for (PatternAttribute patternAttribute: patternNode.getPatternAttributes()) { + if ("+".equals(patternAttribute.getAction())) { + continue; + } + boolean isSame = PatternEngine.evaluate(node, patternAttribute.getValue()); + if (("!=".equals(patternAttribute.getAction()) && isSame) || (!"!=".equals(patternAttribute.getAction()) && !isSame)) { + continue nodeMatch; + } + } + couldMatch.get(level).get(couldMatch.get(level).size() - 1).add(node); + } + if (level == 0 && couldMatch.get(level).get(couldMatch.get(level).size() - 1).size() == 0) { + return null; // no mapping for this node => fail (only in level == 0) + } + } + } + return couldMatch; + } + + public static boolean doesntMatchNegativeNodes(HashMap map, Graph graph, ArrayList> nodeMatchLists, ArrayList>> couldMatch) { + for (int level = 1; level < nodeMatchLists.size(); ++level) { + /* + * now we check for each set of negative nodes (here: nodeMatchLists.get(level)), + * if there is a possible match within the mapping of the positive match, + * until a match for these negative nodes is found => return false! + * or all level were completed without matching negative nodes => return true! + */ + HashMap mapping = (HashMap)map.clone(); + ArrayList currentTry = new ArrayList(); + for (int i = 0; i < couldMatch.get(level).size(); ++i) { + currentTry.add(0); + mapping.put(nodeMatchLists.get(level).get(i), couldMatch.get(level).get(i).get(0)); + } + /* + * only check this index against previous ones, + * if ok, increment and check only that one, and so on + */ + int checkIndex = 0; + loop: while (checkIndex > -1) { + for (int i = checkIndex; i < nodeMatchLists.get(level).size(); ++i) { + /* + * check nodeMatchLists.get(0).get(i) only against all previous nodes, + * if it is duplicate, or any edge (outgoing or incoming) is missing. + * if it fails: count this nodes candidate up (++currentTry.get(i)) if possible, + * if it can't be counted up, go one level back (i-1) and try increment there and so on. + * if nothing can't be counted up, return null (or set checkIndex to -1 and break); + * after incrementing a candidate, reset all currentTry-elements after it to 0, + * and set the checkIndex to the index of the increment currentTry-element, finally break + */ + PatternNode currentSubNode = nodeMatchLists.get(level).get(i); + boolean fail = false; + match: for (int j = 0; j < i; ++j) { + PatternNode otherSubNode = nodeMatchLists.get(level).get(j); + // check if the negative node has a duplicate mapping to another negative node of this set + if (mapping.get(currentSubNode) == mapping.get(otherSubNode)) { + fail = true; // found duplicate! + break match; + } + // check outgoing edges to previous negative nodes + for (PatternEdge patternEdge: currentSubNode.getPatternEdges()) { + if ("+".equals(patternEdge.getAction())) { + continue; + } + if (patternEdge.getTarget() == otherSubNode) { + boolean exists = mapping.get(currentSubNode).getEdges(patternEdge.getName()).contains(mapping.get(otherSubNode)); + if (("!=".equals(patternEdge.getAction()) && exists) || (!"!=".equals(patternEdge.getAction()) && !exists)) { + fail = true; // failure at outgoing edge + break match; + } + } + } + // check incoming edges from previous negative nodes + for (PatternEdge patternEdge: otherSubNode.getPatternEdges()) { + if ("+".equals(patternEdge.getAction())) { + continue; + } + if (patternEdge.getTarget() == currentSubNode) { + boolean exists = mapping.get(otherSubNode).getEdges(patternEdge.getName()).contains(mapping.get(currentSubNode)); + if (("!=".equals(patternEdge.getAction()) && exists) || (!"!=".equals(patternEdge.getAction()) && !exists)) { + fail = true; // failure at incoming edge + break match; + } + } + } + } + // check if the negative node has a duplicate mapping to a positive node + for (int k = 0; k < nodeMatchLists.get(0).size(); ++k) { + if (mapping.get(currentSubNode) == mapping.get(nodeMatchLists.get(0).get(k))) { + fail = true; // found duplicate! + break; + } + } + // check outgoing edges to positive nodes TODO: debug + for (int k = 0; k < nodeMatchLists.get(0).size(); ++k) { + for (PatternEdge patternEdge: currentSubNode.getPatternEdges()) { + if ("+".equals(patternEdge.getAction())) { + continue; + } + if (patternEdge.getTarget() == nodeMatchLists.get(0).get(k)) { + boolean exists = mapping.get(currentSubNode).getEdges(patternEdge.getName()).contains(mapping.get(nodeMatchLists.get(0).get(k))); + if (("!=".equals(patternEdge.getAction()) && exists) || (!"!=".equals(patternEdge.getAction()) && !exists)) { + fail = true; // failure at outgoing edge + break; + } + } + } + } + // check incoming edges from positive nodes TODO: debug + for (int k = 0; k < nodeMatchLists.get(0).size(); ++k) { + for (PatternEdge patternEdge: nodeMatchLists.get(0).get(k).getPatternEdges()) { + if ("+".equals(patternEdge.getAction())) { + continue; + } + if (patternEdge.getTarget() == currentSubNode) { + boolean exists = mapping.get(nodeMatchLists.get(0).get(k)).getEdges(patternEdge.getName()).contains(mapping.get(currentSubNode)); + if (("!=".equals(patternEdge.getAction()) && exists) || (!"!=".equals(patternEdge.getAction()) && !exists)) { + fail = true; // failure at incoming edge + break; + } + } + } + } + if (fail) { + // found an error with the 'new' candidate at index i + /* + * change candidate of node[i] or if not possible, the next possible earlier one, + * reset the ones after it (also update the mapping) + * and set checkIndex to the new index to check (the one that got incremented) + */ + checkIndex = i; + while (checkIndex >= 0 && currentTry.get(checkIndex) == couldMatch.get(level).get(checkIndex).size() - 1) { + --checkIndex; + } + if (checkIndex >= 0) { + currentTry.set(checkIndex, currentTry.get(checkIndex) + 1); + mapping.put(nodeMatchLists.get(level).get(checkIndex), couldMatch.get(level).get(checkIndex).get(currentTry.get(checkIndex))); + for (int j = checkIndex + 1; j < nodeMatchLists.get(level).size(); ++j) { + currentTry.set(j, 0); + mapping.put(nodeMatchLists.get(0).get(j), couldMatch.get(level).get(j).get(0)); + } + } + continue loop; + } + } + return false; + } + } + return true; + } + + public static ArrayList matchPattern(Graph graph, PatternGraph pattern, boolean single, ArrayList> nodeMatchLists, ArrayList>> couldMatch) { + ArrayList matches = new ArrayList(); + + HashMap mapping = new HashMap(); + // now going through all valid combinations (that make sense) of those loosely fitted candidates to find a match: + ArrayList currentTry = new ArrayList(); + for (int i = 0; i < couldMatch.get(0).size(); ++i) { + currentTry.add(0); + mapping.put(nodeMatchLists.get(0).get(i), couldMatch.get(0).get(i).get(0)); + } + /* + * only check this index against previous ones, + * if ok, increment and check only that one, and so on + */ + ArrayList> mappings = new ArrayList>(); + int checkIndex = 1; +loop: while (checkIndex > -1) { + for (int i = checkIndex; i < nodeMatchLists.get(0).size(); ++i) { + /* + * check nodeMatchLists.get(0).get(i) only against all previous nodes, + * if it is duplicate, or any edge (outgoing or incoming) is missing. + * if it fails: count this nodes candidate up (++currentTry.get(i)) if possible, + * if it can't be counted up, go one level back (i-1) and try increment there and so on. + * if nothing can't be counted up, return null (or set checkIndex to -1 and break); + * after incrementing a candidate, reset all currentTry-elements after it to 0, + * and set the checkIndex to the index of the increment currentTry-element, finally break + */ + PatternNode currentSubNode = nodeMatchLists.get(0).get(i); + boolean fail = false; +match: for (int j = 0; j < i; ++j) { + PatternNode otherSubNode = nodeMatchLists.get(0).get(j); + if (mapping.get(currentSubNode) == mapping.get(otherSubNode)) { + fail = true; // found duplicate! + break match; + } + for (PatternEdge patternEdge: currentSubNode.getPatternEdges()) { + if ("+".equals(patternEdge.getAction())) { + continue; + } + if (patternEdge.getTarget() == otherSubNode) { + boolean exists = mapping.get(currentSubNode).getEdges(patternEdge.getName()).contains(mapping.get(otherSubNode)); + if (("!=".equals(patternEdge.getAction()) && exists) || (!"!=".equals(patternEdge.getAction()) && !exists)) { + fail = true; // failure at outgoing edge + break match; + } + } + } + for (PatternEdge patternEdge: otherSubNode.getPatternEdges()) { + if ("+".equals(patternEdge.getAction())) { + continue; + } + if (patternEdge.getTarget() == currentSubNode) { + boolean exists = mapping.get(otherSubNode).getEdges(patternEdge.getName()).contains(mapping.get(currentSubNode)); + if (("!=".equals(patternEdge.getAction()) && exists) || (!"!=".equals(patternEdge.getAction()) && !exists)) { + fail = true; // failure at incoming edge + break match; + } + } + } + } + if (fail) { + // found an error with the 'new' candidate at index i + /* + * change candidate of node[i] or if not possible, the next possible earlier one, + * reset the ones after it (also update the mapping) + * and set checkIndex to the new index to check (the one that got incremented) + */ + checkIndex = i; + while (checkIndex >= 0 && currentTry.get(checkIndex) == couldMatch.get(0).get(checkIndex).size() - 1) { + --checkIndex; + } + if (checkIndex >= 0) { + currentTry.set(checkIndex, currentTry.get(checkIndex) + 1); + mapping.put(nodeMatchLists.get(0).get(checkIndex), couldMatch.get(0).get(checkIndex).get(currentTry.get(checkIndex))); + for (int j = checkIndex + 1; j < nodeMatchLists.get(0).size(); ++j) { + currentTry.set(j, 0); + mapping.put(nodeMatchLists.get(0).get(j), couldMatch.get(0).get(j).get(0)); + } + } + continue loop; + } + } + if (doesntMatchNegativeNodes(mapping, graph, nodeMatchLists, couldMatch)) { + mappings.add((HashMap)mapping.clone()); // it ran through with no errors => success + if (single) { + break loop; + } + } + // even if a match was found: count up, to find the next match: + checkIndex = nodeMatchLists.get(0).size() - 1; + while (checkIndex >= 0 && currentTry.get(checkIndex) == couldMatch.get(0).get(checkIndex).size() - 1) { + --checkIndex; + } + if (checkIndex >= 0) { + currentTry.set(checkIndex, currentTry.get(checkIndex) + 1); + mapping.put(nodeMatchLists.get(0).get(checkIndex), couldMatch.get(0).get(checkIndex).get(currentTry.get(checkIndex))); + for (int j = checkIndex + 1; j < nodeMatchLists.get(0).size(); ++j) { + currentTry.set(j, 0); + mapping.put(nodeMatchLists.get(0).get(j), couldMatch.get(0).get(j).get(0)); + } + } + } + // nothing left to check => return results + for (HashMap successfulMapping: mappings) { + matches.add(new Match(graph, pattern, successfulMapping)); + } + return matches; + } /** * finds matches for a pattern in a graph. @@ -164,6 +534,27 @@ private static int indexOf(ArrayList graphs, Graph graph) { * @return a list of matches for the pattern in the graph */ public static ArrayList matchPattern(Graph graph, PatternGraph pattern, boolean single) { + // first get a 'smart' list of first all positive nodes and then multiple lists of negative nodes that belong together: + ArrayList> nodeMatchLists = calculateNodeMatchingLists(pattern); + + if (nodeMatchLists.get(0).size() > graph.getNodes().size()) { + return new ArrayList(); // more positive nodes to match, than existing -> fail + } + + // now check for 'loosely matched candidates' of nodes to match (level == 0: positive nodes, level > 0: negative node sets): + ArrayList>> couldMatch = findPossibleMatchesForPositiveAndNegativeNodes(graph, nodeMatchLists); + + // TODO: maybe do something like this 'remove impossible matches'... + + if (couldMatch == null) { + return new ArrayList(); // some positive node has no match -> fail + } + + // finally find those matches: + return matchPattern(graph, pattern, single, nodeMatchLists, couldMatch); + } + + public static ArrayList matchPatternOld(Graph graph, PatternGraph pattern, boolean single) { ArrayList matches = new ArrayList(); // step 1: for every PatternNode, find all possible Nodes: ArrayList> couldMatch = new ArrayList>(); @@ -202,8 +593,8 @@ public static ArrayList matchPattern(Graph graph, PatternGraph pattern, b // now edges are being checked 'loosely' for (int k = 0; k < patternNode.getPatternEdges().size(); ++k) { PatternEdge patternEdge = patternNode.getPatternEdges().get(k); - // edges that shouldn't exist are skipped ("+" and "!=") - if ("+".equals(patternEdge.getAction()) || "!=".equals(patternEdge.getAction())) { + // edges that shouldn't exist are skipped ("+" and "!=" plus all edges to nodes with "!=") + if ("+".equals(patternEdge.getAction()) || "!=".equals(patternEdge.getAction()) || "!=".equals(patternEdge.getTarget().getAction())) { continue; } // other edges ("==" and "-") should at least exist with this name at all... diff --git a/src/main/resources/data.json b/src/main/resources/data.json index 950acf6..3fbbbd8 100644 --- a/src/main/resources/data.json +++ b/src/main/resources/data.json @@ -1 +1 @@ -{"nodes":[{"id":0,"label":"{type\u003dCar}","x":-0.010452846326765346,"y":-0.09945218953682733,"size":10,"color":"#00F"},{"id":1,"label":"{color\u003dblack, type\u003dWheel}","x":-0.03090169943749474,"y":-0.09510565162951536,"size":10,"color":"#F00"},{"id":2,"label":"{color\u003dblack, type\u003dWheel}","x":-0.049999999999999996,"y":-0.08660254037844387,"size":10,"color":"#F00"},{"id":3,"label":"{color\u003dblack, type\u003dWheel}","x":-0.06691306063588583,"y":-0.07431448254773942,"size":10,"color":"#F00"},{"id":4,"label":"{color\u003dblack, type\u003dWheel}","x":-0.08090169943749474,"y":-0.058778525229247314,"size":10,"color":"#F00"},{"id":5,"label":"{type\u003dCar}","x":-0.09135454576426008,"y":-0.040673664307580036,"size":10,"color":"#000"},{"id":6,"label":"{color\u003dblue, type\u003dWheel}","x":-0.09781476007338057,"y":-0.020791169081775925,"size":10,"color":"#F00"},{"id":7,"label":"{color\u003dblack, type\u003dWheel}","x":-0.1,"y":-6.123233995736766E-18,"size":10,"color":"#F00"},{"id":8,"label":"{color\u003dblack, type\u003dWheel}","x":-0.09781476007338057,"y":0.02079116908177591,"size":10,"color":"#F00"},{"id":9,"label":"{color\u003dblack, type\u003dWheel}","x":-0.0913545457642601,"y":0.04067366430758001,"size":10,"color":"#F00"},{"id":10,"label":"{type\u003dCar}","x":-0.08090169943749474,"y":0.0587785252292473,"size":10,"color":"#000"},{"id":11,"label":"{color\u003dblue, type\u003dWheel}","x":-0.0669130606358858,"y":0.07431448254773944,"size":10,"color":"#F00"},{"id":12,"label":"{color\u003dblue, type\u003dWheel}","x":-0.049999999999999996,"y":0.08660254037844387,"size":10,"color":"#F00"},{"id":13,"label":"{color\u003dblue, type\u003dWheel}","x":-0.03090169943749475,"y":0.09510565162951536,"size":10,"color":"#F00"},{"id":14,"label":"{color\u003dblue, type\u003dWheel}","x":-0.010452846326765373,"y":0.09945218953682733,"size":10,"color":"#F00"},{"id":15,"label":"{type\u003dCar}","x":0.01045284632676535,"y":0.09945218953682733,"size":10,"color":"#000"},{"id":16,"label":"{color\u003dorange, type\u003dWheel}","x":0.030901699437494774,"y":0.09510565162951536,"size":10,"color":"#F00"},{"id":17,"label":"{color\u003dorange, type\u003dWheel}","x":0.05000000000000001,"y":0.08660254037844387,"size":10,"color":"#F00"},{"id":18,"label":"{color\u003dorange, type\u003dWheel}","x":0.06691306063588583,"y":0.07431448254773942,"size":10,"color":"#F00"},{"id":19,"label":"{color\u003dorange, type\u003dWheel}","x":0.08090169943749473,"y":0.05877852522924733,"size":10,"color":"#F00"},{"id":20,"label":"{type\u003dCar}","x":0.0913545457642601,"y":0.04067366430758001,"size":10,"color":"#000"},{"id":21,"label":"{color\u003dred, type\u003dWheel}","x":0.09781476007338055,"y":0.02079116908177598,"size":10,"color":"#F00"},{"id":22,"label":"{color\u003dred, type\u003dWheel}","x":0.1,"y":1.8369701987210297E-17,"size":10,"color":"#F00"},{"id":23,"label":"{color\u003dred, type\u003dWheel}","x":0.09781476007338055,"y":-0.020791169081775942,"size":10,"color":"#F00"},{"id":24,"label":"{color\u003dred, type\u003dWheel}","x":0.09135454576426011,"y":-0.040673664307579974,"size":10,"color":"#F00"},{"id":25,"label":"{type\u003dCar}","x":0.08090169943749476,"y":-0.05877852522924729,"size":10,"color":"#000"},{"id":26,"label":"{color\u003dgreen, type\u003dWheel}","x":0.06691306063588588,"y":-0.07431448254773937,"size":10,"color":"#F00"},{"id":27,"label":"{color\u003dgreen, type\u003dWheel}","x":0.050000000000000044,"y":-0.08660254037844384,"size":10,"color":"#F00"},{"id":28,"label":"{color\u003dgreen, type\u003dWheel}","x":0.03090169943749476,"y":-0.09510565162951536,"size":10,"color":"#F00"},{"id":29,"label":"{color\u003dgreen, type\u003dWheel}","x":0.01045284632676543,"y":-0.09945218953682733,"size":10,"color":"#F00"}],"edges":[{"id":30,"label":"next","source":0,"target":5,"size":10,"color":"#888"},{"id":31,"label":"has","source":0,"target":1,"size":10,"color":"#888"},{"id":32,"label":"has","source":0,"target":2,"size":10,"color":"#888"},{"id":33,"label":"has","source":0,"target":3,"size":10,"color":"#888"},{"id":34,"label":"has","source":0,"target":4,"size":10,"color":"#888"},{"id":35,"label":"next","source":5,"target":10,"size":10,"color":"#888"},{"id":36,"label":"has","source":5,"target":6,"size":10,"color":"#888"},{"id":37,"label":"has","source":5,"target":7,"size":10,"color":"#888"},{"id":38,"label":"has","source":5,"target":8,"size":10,"color":"#888"},{"id":39,"label":"has","source":5,"target":9,"size":10,"color":"#888"},{"id":40,"label":"next","source":10,"target":15,"size":10,"color":"#888"},{"id":41,"label":"has","source":10,"target":11,"size":10,"color":"#888"},{"id":42,"label":"has","source":10,"target":12,"size":10,"color":"#888"},{"id":43,"label":"has","source":10,"target":13,"size":10,"color":"#888"},{"id":44,"label":"has","source":10,"target":14,"size":10,"color":"#888"},{"id":45,"label":"next","source":15,"target":20,"size":10,"color":"#888"},{"id":46,"label":"has","source":15,"target":16,"size":10,"color":"#888"},{"id":47,"label":"has","source":15,"target":17,"size":10,"color":"#888"},{"id":48,"label":"has","source":15,"target":18,"size":10,"color":"#888"},{"id":49,"label":"has","source":15,"target":19,"size":10,"color":"#888"},{"id":50,"label":"next","source":20,"target":25,"size":10,"color":"#888"},{"id":51,"label":"has","source":20,"target":21,"size":10,"color":"#888"},{"id":52,"label":"has","source":20,"target":22,"size":10,"color":"#888"},{"id":53,"label":"has","source":20,"target":23,"size":10,"color":"#888"},{"id":54,"label":"has","source":20,"target":24,"size":10,"color":"#888"},{"id":55,"label":"has","source":25,"target":26,"size":10,"color":"#888"},{"id":56,"label":"has","source":25,"target":27,"size":10,"color":"#888"},{"id":57,"label":"has","source":25,"target":28,"size":10,"color":"#888"},{"id":58,"label":"has","source":25,"target":29,"size":10,"color":"#888"}]} \ No newline at end of file +{"nodes":[{"id":0,"label":"{type\u003dCar}","x":-0.03090169943749474,"y":-0.09510565162951536,"size":10,"color":"#00F"},{"id":1,"label":"{color\u003dblack, type\u003dWheel}","x":-0.08090169943749474,"y":-0.058778525229247314,"size":10,"color":"#F00"},{"id":2,"label":"{color\u003dblack, type\u003dWheel}","x":-0.1,"y":-6.123233995736766E-18,"size":10,"color":"#F00"},{"id":3,"label":"{color\u003dblack, type\u003dWheel}","x":-0.08090169943749474,"y":0.0587785252292473,"size":10,"color":"#F00"},{"id":4,"label":"{color\u003dblack, type\u003dWheel}","x":-0.03090169943749475,"y":0.09510565162951536,"size":10,"color":"#F00"},{"id":5,"label":"{type\u003dCar}","x":0.030901699437494774,"y":0.09510565162951536,"size":10,"color":"#000"},{"id":6,"label":"{color\u003dblue, type\u003dWheel}","x":0.08090169943749473,"y":0.05877852522924733,"size":10,"color":"#F00"},{"id":7,"label":"{color\u003dblack, type\u003dWheel}","x":0.1,"y":1.8369701987210297E-17,"size":10,"color":"#F00"},{"id":8,"label":"{color\u003dblack, type\u003dWheel}","x":0.08090169943749476,"y":-0.05877852522924729,"size":10,"color":"#F00"},{"id":9,"label":"{color\u003dblack, type\u003dWheel}","x":0.03090169943749476,"y":-0.09510565162951536,"size":10,"color":"#F00"}],"edges":[{"id":10,"label":"next","source":0,"target":5,"size":10,"color":"#888"},{"id":11,"label":"has","source":0,"target":1,"size":10,"color":"#888"},{"id":12,"label":"has","source":0,"target":2,"size":10,"color":"#888"},{"id":13,"label":"has","source":0,"target":3,"size":10,"color":"#888"},{"id":14,"label":"has","source":0,"target":4,"size":10,"color":"#888"},{"id":15,"label":"has","source":5,"target":6,"size":10,"color":"#888"},{"id":16,"label":"has","source":5,"target":7,"size":10,"color":"#888"},{"id":17,"label":"has","source":5,"target":8,"size":10,"color":"#888"},{"id":18,"label":"has","source":5,"target":9,"size":10,"color":"#888"}]} \ No newline at end of file diff --git a/src/test/java/org/fujaba/graphengine/unitTests/PatternTest.java b/src/test/java/org/fujaba/graphengine/unitTests/PatternTest.java index d1dfe6c..d2c56ae 100644 --- a/src/test/java/org/fujaba/graphengine/unitTests/PatternTest.java +++ b/src/test/java/org/fujaba/graphengine/unitTests/PatternTest.java @@ -193,45 +193,45 @@ public void testNegativePatternVariantsWithDifferentIsomorphismCheckApproaches() carB.addEdge("has", wheelB1).addEdge("has", wheelB2).addEdge("has", wheelB3).addEdge("has", wheelB4); carA.addEdge("next", carB); - // build a car 'C' with 4 blue wheels: - Node carC = new Node().setAttribute("type", "Car"); - Node wheelC1 = new Node().setAttribute("type", "Wheel").setAttribute("color", "blue"); - Node wheelC2 = new Node().setAttribute("type", "Wheel").setAttribute("color", "blue"); - Node wheelC3 = new Node().setAttribute("type", "Wheel").setAttribute("color", "blue"); - Node wheelC4 = new Node().setAttribute("type", "Wheel").setAttribute("color", "blue"); - carGraph.addNode(carC).addNode(wheelC1).addNode(wheelC2).addNode(wheelC3).addNode(wheelC4); - carC.addEdge("has", wheelC1).addEdge("has", wheelC2).addEdge("has", wheelC3).addEdge("has", wheelC4); - carB.addEdge("next", carC); - - // build a car 'D' with 4 orange wheels - Node carD = new Node().setAttribute("type", "Car"); - Node wheelD1 = new Node().setAttribute("type", "Wheel").setAttribute("color", "orange"); - Node wheelD2 = new Node().setAttribute("type", "Wheel").setAttribute("color", "orange"); - Node wheelD3 = new Node().setAttribute("type", "Wheel").setAttribute("color", "orange"); - Node wheelD4 = new Node().setAttribute("type", "Wheel").setAttribute("color", "orange"); - carGraph.addNode(carD).addNode(wheelD1).addNode(wheelD2).addNode(wheelD3).addNode(wheelD4); - carD.addEdge("has", wheelD1).addEdge("has", wheelD2).addEdge("has", wheelD3).addEdge("has", wheelD4); - carC.addEdge("next", carD); - - // build a car 'E' with 4 red wheels - Node carE = new Node().setAttribute("type", "Car"); - Node wheelE1 = new Node().setAttribute("type", "Wheel").setAttribute("color", "red"); - Node wheelE2 = new Node().setAttribute("type", "Wheel").setAttribute("color", "red"); - Node wheelE3 = new Node().setAttribute("type", "Wheel").setAttribute("color", "red"); - Node wheelE4 = new Node().setAttribute("type", "Wheel").setAttribute("color", "red"); - carGraph.addNode(carE).addNode(wheelE1).addNode(wheelE2).addNode(wheelE3).addNode(wheelE4); - carE.addEdge("has", wheelE1).addEdge("has", wheelE2).addEdge("has", wheelE3).addEdge("has", wheelE4); - carD.addEdge("next", carE); - - // build a car 'F' with 4 green wheels - Node carF = new Node().setAttribute("type", "Car"); - Node wheelF1 = new Node().setAttribute("type", "Wheel").setAttribute("color", "green"); - Node wheelF2 = new Node().setAttribute("type", "Wheel").setAttribute("color", "green"); - Node wheelF3 = new Node().setAttribute("type", "Wheel").setAttribute("color", "green"); - Node wheelF4 = new Node().setAttribute("type", "Wheel").setAttribute("color", "green"); - carGraph.addNode(carF).addNode(wheelF1).addNode(wheelF2).addNode(wheelF3).addNode(wheelF4); - carF.addEdge("has", wheelF1).addEdge("has", wheelF2).addEdge("has", wheelF3).addEdge("has", wheelF4); - carE.addEdge("next", carF); +// // build a car 'C' with 4 blue wheels: +// Node carC = new Node().setAttribute("type", "Car"); +// Node wheelC1 = new Node().setAttribute("type", "Wheel").setAttribute("color", "blue"); +// Node wheelC2 = new Node().setAttribute("type", "Wheel").setAttribute("color", "blue"); +// Node wheelC3 = new Node().setAttribute("type", "Wheel").setAttribute("color", "blue"); +// Node wheelC4 = new Node().setAttribute("type", "Wheel").setAttribute("color", "blue"); +// carGraph.addNode(carC).addNode(wheelC1).addNode(wheelC2).addNode(wheelC3).addNode(wheelC4); +// carC.addEdge("has", wheelC1).addEdge("has", wheelC2).addEdge("has", wheelC3).addEdge("has", wheelC4); +// carB.addEdge("next", carC); +// +// // build a car 'D' with 4 orange wheels +// Node carD = new Node().setAttribute("type", "Car"); +// Node wheelD1 = new Node().setAttribute("type", "Wheel").setAttribute("color", "orange"); +// Node wheelD2 = new Node().setAttribute("type", "Wheel").setAttribute("color", "orange"); +// Node wheelD3 = new Node().setAttribute("type", "Wheel").setAttribute("color", "orange"); +// Node wheelD4 = new Node().setAttribute("type", "Wheel").setAttribute("color", "orange"); +// carGraph.addNode(carD).addNode(wheelD1).addNode(wheelD2).addNode(wheelD3).addNode(wheelD4); +// carD.addEdge("has", wheelD1).addEdge("has", wheelD2).addEdge("has", wheelD3).addEdge("has", wheelD4); +// carC.addEdge("next", carD); +// +// // build a car 'E' with 4 red wheels +// Node carE = new Node().setAttribute("type", "Car"); +// Node wheelE1 = new Node().setAttribute("type", "Wheel").setAttribute("color", "red"); +// Node wheelE2 = new Node().setAttribute("type", "Wheel").setAttribute("color", "red"); +// Node wheelE3 = new Node().setAttribute("type", "Wheel").setAttribute("color", "red"); +// Node wheelE4 = new Node().setAttribute("type", "Wheel").setAttribute("color", "red"); +// carGraph.addNode(carE).addNode(wheelE1).addNode(wheelE2).addNode(wheelE3).addNode(wheelE4); +// carE.addEdge("has", wheelE1).addEdge("has", wheelE2).addEdge("has", wheelE3).addEdge("has", wheelE4); +// carD.addEdge("next", carE); +// +// // build a car 'F' with 4 green wheels +// Node carF = new Node().setAttribute("type", "Car"); +// Node wheelF1 = new Node().setAttribute("type", "Wheel").setAttribute("color", "green"); +// Node wheelF2 = new Node().setAttribute("type", "Wheel").setAttribute("color", "green"); +// Node wheelF3 = new Node().setAttribute("type", "Wheel").setAttribute("color", "green"); +// Node wheelF4 = new Node().setAttribute("type", "Wheel").setAttribute("color", "green"); +// carGraph.addNode(carF).addNode(wheelF1).addNode(wheelF2).addNode(wheelF3).addNode(wheelF4); +// carF.addEdge("has", wheelF1).addEdge("has", wheelF2).addEdge("has", wheelF3).addEdge("has", wheelF4); +// carE.addEdge("next", carF); // build a pattern that says 'car without a blue wheel': PatternGraph carWithoutBlueWheel = new PatternGraph(); @@ -250,7 +250,7 @@ public void testNegativePatternVariantsWithDifferentIsomorphismCheckApproaches() matches = PatternEngine.matchPattern(carGraph, carWithoutBlueWheel, false); duration = (System.nanoTime() - begin) / 1e6; System.out.println("PatternEngine.matchPattern(): " + duration + "ms"); - Assert.assertEquals(4, matches.size()); + Assert.assertEquals(1, matches.size()); GraphEngine.prepareGraphAsJsonFileForSigmaJs(carGraph); @@ -457,7 +457,7 @@ private PatternGraph getCorrectTranportRule() { eater.setAction("!="); getsEaten.setAttributeMatchExpression("#{type} == 'Cargo'"); getsEaten.addPatternEdge(new PatternEdge() - .setTarget(getsEaten) + .setSource(getsEaten) .setName("at") .setTarget(bankHere)); getsEaten.setAction("!="); @@ -506,7 +506,7 @@ private PatternGraph getCorrectEmptyTranportRule() { eater.setAction("!="); getsEaten.setAttributeMatchExpression("#{type} == 'Cargo'"); getsEaten.addPatternEdge(new PatternEdge() - .setTarget(getsEaten) + .setSource(getsEaten) .setName("at") .setTarget(bankHere)); getsEaten.setAction("!="); diff --git a/src/test/java/org/fujaba/graphengine/unitTests/RoadworkExample.java b/src/test/java/org/fujaba/graphengine/unitTests/RoadworkExample.java index f35e8ad..d995f95 100644 --- a/src/test/java/org/fujaba/graphengine/unitTests/RoadworkExample.java +++ b/src/test/java/org/fujaba/graphengine/unitTests/RoadworkExample.java @@ -66,7 +66,7 @@ Graph getStartGraph() { private PatternGraph createCarsPattern() { PatternGraph graph = new PatternGraph(); - PatternNode prevRoad = new PatternNode().setAttributeMatchExpression("#{type} == 'road'").setAction("!="); + PatternNode prevRoad = new PatternNode().setAttributeMatchExpression("#{type} == 'road'"); PatternNode thisRoad = new PatternNode().setAttributeMatchExpression("#{type} == 'road'"); PatternNode nextRoad = new PatternNode().setAttributeMatchExpression("#{type} == 'road'"); @@ -80,8 +80,8 @@ private PatternGraph createCarsPattern() { thisRoad.addPatternEdge("car", existingCarAtEntrance); thisRoad.addPatternEdge(new PatternEdge().setSource(thisRoad).setName("car").setTarget(nonExistingCarAtEntrance).setAction("+")); -// existingCarAtEntrance.addPatternEdge("at", thisRoad); -// nonExistingCarAtEntrance.addPatternEdge(new PatternEdge().setSource(nonExistingCarAtEntrance).setName("at").setTarget(thisRoad).setAction("+")); + existingCarAtEntrance.addPatternEdge("at", thisRoad); + nonExistingCarAtEntrance.addPatternEdge(new PatternEdge().setSource(nonExistingCarAtEntrance).setName("at").setTarget(thisRoad).setAction("+")); graph.addPatternNode(prevRoad, thisRoad, nextRoad, existingCarAtEntrance, nonExistingCarAtEntrance); diff --git a/src/test/java/org/fujaba/graphengine/unitTests/TestVisualization.java b/src/test/java/org/fujaba/graphengine/unitTests/TestVisualization.java index b2ebc47..6cb0b20 100644 --- a/src/test/java/org/fujaba/graphengine/unitTests/TestVisualization.java +++ b/src/test/java/org/fujaba/graphengine/unitTests/TestVisualization.java @@ -124,7 +124,7 @@ private PatternGraph getCorrectTranportRule() { eater.setAction("!="); getsEaten.setAttributeMatchExpression("#{type} == 'Cargo'"); getsEaten.addPatternEdge(new PatternEdge() - .setTarget(getsEaten) + .setSource(getsEaten) .setName("at") .setTarget(bankHere)); getsEaten.setAction("!="); @@ -173,7 +173,7 @@ private PatternGraph getCorrectEmptyTranportRule() { eater.setAction("!="); getsEaten.setAttributeMatchExpression("#{type} == 'Cargo'"); getsEaten.addPatternEdge(new PatternEdge() - .setTarget(getsEaten) + .setSource(getsEaten) .setName("at") .setTarget(bankHere)); getsEaten.setAction("!=");