From ff83851192e574a544600f8bbb12b030fa5e7239 Mon Sep 17 00:00:00 2001 From: hoechp Date: Tue, 20 Dec 2016 07:38:36 +0100 Subject: [PATCH] #23 #28 trying to implement a few more versions of isomorphism checks --- .../org/fujaba/graphengine/GraphEngine.java | 27 +- .../IsomorphismHandlerCSP.java | 2 +- .../IsomorphismHandlerCSPWithHeuristics.java | 236 +++++++++++ ...omorphismHandlerCSPWithMoreHeuristics.java | 366 ++++++++++++++++++ .../heuristics/NodeWithConflict.java | 31 ++ src/main/resources/data.json | 2 +- .../graphengine/unitTests/GraphTest.java | 4 +- .../graphengine/unitTests/PatternTest.java | 6 +- .../unitTests/RoadworkExample.java | 85 +++- 9 files changed, 746 insertions(+), 13 deletions(-) create mode 100644 src/main/java/org/fujaba/graphengine/isomorphismtools/IsomorphismHandlerCSPWithHeuristics.java create mode 100644 src/main/java/org/fujaba/graphengine/isomorphismtools/IsomorphismHandlerCSPWithMoreHeuristics.java create mode 100644 src/main/java/org/fujaba/graphengine/isomorphismtools/heuristics/NodeWithConflict.java diff --git a/src/main/java/org/fujaba/graphengine/GraphEngine.java b/src/main/java/org/fujaba/graphengine/GraphEngine.java index 8226256..4e102db 100644 --- a/src/main/java/org/fujaba/graphengine/GraphEngine.java +++ b/src/main/java/org/fujaba/graphengine/GraphEngine.java @@ -12,7 +12,7 @@ import org.fujaba.graphengine.graph.adapter.GraphToSigmaJsAdapter; import org.fujaba.graphengine.graph.adapter.NodeAdapter; import org.fujaba.graphengine.isomorphismtools.IsomorphismHandler; -import org.fujaba.graphengine.isomorphismtools.IsomorphismHandlerCSP; +import org.fujaba.graphengine.isomorphismtools.IsomorphismHandlerCSPWithHeuristics; import org.fujaba.graphengine.isomorphismtools.IsomorphismHandlerSorting; import org.fujaba.graphengine.isomorphismtools.sort.NodeSortTree; import org.fujaba.graphengine.isomorphismtools.sort.adapter.NodeSortTreeAdapter; @@ -51,7 +51,7 @@ public static void setMainIsomorphismHandler(IsomorphismHandler isomorphismHandl */ public static IsomorphismHandler getMainIsomorphismHandler() { if (mainIsomorphismHandler == null) { - mainIsomorphismHandler = new IsomorphismHandlerCSP(); + mainIsomorphismHandler = new IsomorphismHandlerCSPWithHeuristics(); } return mainIsomorphismHandler; } @@ -61,7 +61,7 @@ public static IsomorphismHandler getMainIsomorphismHandler() { */ public static IsomorphismHandler getMappingFallback() { if (mappingFallback == null) { - mappingFallback = new IsomorphismHandlerCSP(); + mappingFallback = new IsomorphismHandlerCSPWithHeuristics(); } return mappingFallback; } @@ -81,7 +81,7 @@ public static IsomorphismHandler getNormalizationFallback() { */ public static IsomorphismHandler getSplitGraphFallback() { if (splitGraphFallback == null) { - splitGraphFallback = new IsomorphismHandlerCSP(); + splitGraphFallback = new IsomorphismHandlerCSPWithHeuristics(); } return splitGraphFallback; } @@ -162,16 +162,29 @@ public static boolean isIsomorphTo(Graph one, Graph other) { /** * Returns all separate graphs (parts of this graph with no edges in between) as new graphs. + * @param graph the graph to split * @return all separate graphs (parts of this graph with no edges in between) as new graphs */ public static ArrayList split(Graph graph) { + return split(graph, false); + } + /** + * Returns all separate graphs (parts of this graph with no edges in between) as new graphs. + * @param graph the graph to split + * @param keepGraph whether to keep the graph-nodes (true) or to use a clone (false) + * @return all separate graphs (parts of this graph with no edges in between) as new graphs + */ + public static ArrayList split(Graph graph, boolean keepGraph) { ArrayList result = new ArrayList(); if (graph.getNodes().size() <= 1) { result.add(graph.clone()); return result; } int count = 0; - Graph clone = graph.clone(); + Graph clone = graph; + if (!keepGraph) { + clone = clone.clone(); + } while (count < graph.getNodes().size()) { Graph subGraph = new Graph(); ArrayList subGraphNodes = connectedNodes(clone, clone.getNodes().get(0)); @@ -180,6 +193,10 @@ public static ArrayList split(Graph graph) { result.add(subGraph); count += subGraphNodes.size(); } + clone.getNodes().clear(); + for (int i = 0; i < result.size(); ++i) { + clone.getNodes().addAll(result.get(i).getNodes()); + } return result; } diff --git a/src/main/java/org/fujaba/graphengine/isomorphismtools/IsomorphismHandlerCSP.java b/src/main/java/org/fujaba/graphengine/isomorphismtools/IsomorphismHandlerCSP.java index ff4207e..11374d1 100644 --- a/src/main/java/org/fujaba/graphengine/isomorphismtools/IsomorphismHandlerCSP.java +++ b/src/main/java/org/fujaba/graphengine/isomorphismtools/IsomorphismHandlerCSP.java @@ -187,4 +187,4 @@ public Graph normalized(Graph graph) { return GraphEngine.getNormalizationFallback().normalized(graph); } -} +} \ No newline at end of file diff --git a/src/main/java/org/fujaba/graphengine/isomorphismtools/IsomorphismHandlerCSPWithHeuristics.java b/src/main/java/org/fujaba/graphengine/isomorphismtools/IsomorphismHandlerCSPWithHeuristics.java new file mode 100644 index 0000000..b0e854d --- /dev/null +++ b/src/main/java/org/fujaba/graphengine/isomorphismtools/IsomorphismHandlerCSPWithHeuristics.java @@ -0,0 +1,236 @@ +package org.fujaba.graphengine.isomorphismtools; + +import java.util.ArrayList; +import java.util.HashMap; + +import org.fujaba.graphengine.GraphEngine; +import org.fujaba.graphengine.graph.Graph; +import org.fujaba.graphengine.graph.Node; + +public class IsomorphismHandlerCSPWithHeuristics extends IsomorphismHandler { + + private static ArrayList getDepthFirstSortedNodeList(Graph graph) { + // obtain all parts of the graph - where each part's nodes are connected with each other: + ArrayList splitted = GraphEngine.split(graph, true); + ArrayList nodes = new ArrayList(); + for (Graph connectedGraph: splitted) { + // add the nodes to the node-list (note: each part's nodes are already sorted in a depth-first explore's order): + nodes.addAll(connectedGraph.getNodes()); + } + return nodes; + } + + @Override + /** + * This function checks for this graph and a given sub-graph, + * if the sub-graph is isomorph to a sub-graph of this graph and returns the mapping. + * + * @param graph the given base-graph + * @param subGraph the given sub-graph + * @return a mapping from the given sub-graph to nodes of this graph if possible, or null + */ + public HashMap mappingFrom(Graph subGraphInitial, Graph baseGraph) { + if (subGraphInitial.getNodes().size() == 0) { + // no nodes in sub-graph => empty mapping is the match (success) + return new HashMap(); + } + if (subGraphInitial.getNodes().size() > baseGraph.getNodes().size()) { + // too many sub-graph nodes => fail + return null; + } + // now I'm trying to find 'loosely matched candidates': + Graph subGraph = subGraphInitial.clone(); + ArrayList> couldMatch2 = new ArrayList>(); + for (int i = 0; i < subGraph.getNodes().size(); ++i) { + Node subNode = subGraph.getNodes().get(i); + couldMatch2.add(new ArrayList()); +nodeMatch: for (int j = 0; j < baseGraph.getNodes().size(); ++j) { + Node node = baseGraph.getNodes().get(j); + // check existence of outgoing edges and their count: + for (String key: subNode.getEdges().keySet()) { + int currentSubNodeEdgeCount = subNode.getEdges(key).size(); + int currentNodeEdgeCount = (node.getEdges(key) == null ? 0 : node.getEdges(key).size()); + if (currentNodeEdgeCount < currentSubNodeEdgeCount) { + continue nodeMatch; + } + } + // check attributes: + for (String key: subNode.getAttributes().keySet()) { + if (!subNode.getAttribute(key).equals(node.getAttribute(key))) { + continue nodeMatch; + } + } + couldMatch2.get(couldMatch2.size() - 1).add(node); + } + if (couldMatch2.get(couldMatch2.size() - 1).size() == 0) { + return null; // no mapping for this node => fail + } + } + couldMatch2 = removeImpossibleCandidates(couldMatch2); + if (couldMatch2 == null) { + // after removing 'impossible' candidates, there's no match anymore => fail + return null; + } + if (subGraph.getNodes().size() == 1) { + // a single node with a candidate is a match => success + HashMap singleNodeMapping = new HashMap(); + singleNodeMapping.put(subGraph.getNodes().get(0), couldMatch2.get(0).get(0)); + return singleNodeMapping; + } + /* + * here I'm starting the application of the heuristics of the maximum restricted variable (H1) and the minimum node order (H2): + */ + // first save the old order of the matches: + HashMap oldIndizes = new HashMap(); + for (int i = 0; i < subGraph.getNodes().size(); ++i) { + oldIndizes.put(subGraph.getNodes().get(i), i); + } + // now check for the maximum restricted variables (H1): + ArrayList minimumIndices = new ArrayList(); + int minimumValue = Integer.MAX_VALUE; + for (int i = 0; i < subGraph.getNodes().size(); ++i) { // minimum candidates + if (couldMatch2.get(i).size() <= minimumValue) { + if (couldMatch2.get(i).size() < minimumValue) { + minimumIndices = new ArrayList(); + minimumValue = couldMatch2.get(i).size(); + } + minimumIndices.add(i); + } + } + // now check within those for the minimum node order (H2): + int indicesIndex = -1; + minimumValue = Integer.MAX_VALUE; + for (int i = 0; i < minimumIndices.size(); ++i) { // minimum node order (outgoing) + int outgoingCount = 0; + Node currentNode = subGraph.getNodes().get(minimumIndices.get(i)); + for (String key: currentNode.getEdges().keySet()) { + outgoingCount += currentNode.getEdges(key).size(); + } + if (outgoingCount < minimumValue) { + minimumValue = outgoingCount; + indicesIndex = i; + } + } + // here we have the 'best' node to start with: + Node heuristicallySelectedFirstNode = subGraph.getNodes().get(minimumIndices.get(indicesIndex)); + subGraph.getNodes().remove(heuristicallySelectedFirstNode); // remove from old position + subGraph.getNodes().add(0, heuristicallySelectedFirstNode); // put in front + // now order the nodes in a depth-first fashion, with the heuristically selected first node as 'root': + ArrayList sortedNodes = getDepthFirstSortedNodeList(subGraph); + // restore the matches to the new order: + ArrayList> couldMatch = new ArrayList>(); + for (int i = 0; i < sortedNodes.size(); ++i) { + couldMatch.add(couldMatch2.get(oldIndizes.get(sortedNodes.get(i)))); + } + 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.size(); ++i) { + currentTry.add(0); + mapping.put(sortedNodes.get(i), couldMatch.get(i).get(0)); + } + /* + * only check this index against previous ones, + * if ok, increment and check only that one, and so on + */ + int checkIndex = 1; +loop: while (checkIndex != -1) { + for (int i = checkIndex; i < sortedNodes.size(); ++i) { + /* + * check sortedNodes.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 + */ + Node currentSubNode = sortedNodes.get(i); + boolean fail = false; +match: for (int j = 0; j < i; ++j) { + Node otherSubNode = sortedNodes.get(j); + if (mapping.get(currentSubNode) == mapping.get(otherSubNode)) { + fail = true; // found duplicate! + break match; + } + for (String key: currentSubNode.getEdges().keySet()) { + if (currentSubNode.getEdges(key).contains(otherSubNode)) { + if (!mapping.get(currentSubNode).getEdges(key).contains(mapping.get(otherSubNode))) { + fail = true; // missing outgoing edge + break match; + } + } + } + for (String key: otherSubNode.getEdges().keySet()) { + if (otherSubNode.getEdges(key).contains(currentSubNode)) { + if (!mapping.get(otherSubNode).getEdges(key).contains(mapping.get(currentSubNode))) { + fail = true; // missing 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(checkIndex).size() - 1) { + --checkIndex; + } + if (checkIndex >= 0) { + currentTry.set(checkIndex, currentTry.get(checkIndex) + 1); + mapping.put(sortedNodes.get(checkIndex), couldMatch.get(checkIndex).get(currentTry.get(checkIndex))); + for (int j = checkIndex + 1; j < sortedNodes.size(); ++j) { + currentTry.set(j, 0); + mapping.put(sortedNodes.get(j), couldMatch.get(j).get(0)); + } + } + continue loop; + } + } + return mapping; // it ran through with no errors => success + } + return null; // nothing left to check => fail + } + + private ArrayList> removeImpossibleCandidates(ArrayList> couldMatch) { + ArrayList> cantMatch = new ArrayList>(); + for (int i = 0; i < couldMatch.size(); ++i) { + cantMatch.add(new ArrayList()); + } + // go through all candidates: + for (int i = 0; i < couldMatch.size(); ++i) { + if (couldMatch.get(i).size() == 1) { + // one node has only one candidate, all other nodes can't have this candidate + for (int j = 0; j < couldMatch.size(); ++j) { + if (i != j) { + // 'tell' all other nodes, they can't have this candidate: + cantMatch.get(j).add(couldMatch.get(i).get(0)); + } + } + } + } + // go through all candidatzes again: + for (int i = 0; i < couldMatch.size(); ++i) { + // remove all impossible candidates: + for (int j = 0; j < cantMatch.get(i).size(); ++j) { + couldMatch.get(i).remove(cantMatch.get(i).get(j)); + } + // if one node has no candidates anymore, return null + if (couldMatch.get(i).size() < 1) { + return null; + } + } + return couldMatch; + } + + @Override + public Graph normalized(Graph graph) { + return GraphEngine.getNormalizationFallback().normalized(graph); + } + +} diff --git a/src/main/java/org/fujaba/graphengine/isomorphismtools/IsomorphismHandlerCSPWithMoreHeuristics.java b/src/main/java/org/fujaba/graphengine/isomorphismtools/IsomorphismHandlerCSPWithMoreHeuristics.java new file mode 100644 index 0000000..7b336ca --- /dev/null +++ b/src/main/java/org/fujaba/graphengine/isomorphismtools/IsomorphismHandlerCSPWithMoreHeuristics.java @@ -0,0 +1,366 @@ +package org.fujaba.graphengine.isomorphismtools; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; + +import org.fujaba.graphengine.GraphEngine; +import org.fujaba.graphengine.graph.Graph; +import org.fujaba.graphengine.graph.Node; +import org.fujaba.graphengine.isomorphismtools.heuristics.NodeWithConflict; + +public class IsomorphismHandlerCSPWithMoreHeuristics extends IsomorphismHandler { + + private static ArrayList getDepthFirstSortedNodeList(Graph graph) { + // obtain all parts of the graph - where each part's nodes are connected with each other: + ArrayList splitted = GraphEngine.split(graph, true); + ArrayList nodes = new ArrayList(); + for (Graph connectedGraph: splitted) { + // add the nodes to the node-list (note: each part's nodes are already sorted in a depth-first explore's order): + nodes.addAll(connectedGraph.getNodes()); + } + return nodes; + } + + @Override + /** + * This function checks for this graph and a given sub-graph, + * if the sub-graph is isomorph to a sub-graph of this graph and returns the mapping. + * + * @param graph the given base-graph + * @param subGraph the given sub-graph + * @return a mapping from the given sub-graph to nodes of this graph if possible, or null + */ + public HashMap mappingFrom(Graph subGraphInitial, Graph baseGraph) { + if (subGraphInitial.getNodes().size() == 0) { + // no nodes in sub-graph => empty mapping is the match (success) + return new HashMap(); + } + if (subGraphInitial.getNodes().size() > baseGraph.getNodes().size()) { + // too many sub-graph nodes => fail + return null; + } + // now I'm trying to find 'loosely matched candidates': + Graph subGraph = subGraphInitial.clone(); + ArrayList> couldMatch2 = new ArrayList>(); + for (int i = 0; i < subGraph.getNodes().size(); ++i) { + Node subNode = subGraph.getNodes().get(i); + couldMatch2.add(new ArrayList()); +nodeMatch: for (int j = 0; j < baseGraph.getNodes().size(); ++j) { + Node node = baseGraph.getNodes().get(j); + // check existence of outgoing edges and their count: + for (String key: subNode.getEdges().keySet()) { + int currentSubNodeEdgeCount = subNode.getEdges(key).size(); + int currentNodeEdgeCount = (node.getEdges(key) == null ? 0 : node.getEdges(key).size()); + if (currentNodeEdgeCount < currentSubNodeEdgeCount) { + continue nodeMatch; + } + } + // check attributes: + for (String key: subNode.getAttributes().keySet()) { + if (!subNode.getAttribute(key).equals(node.getAttribute(key))) { + continue nodeMatch; + } + } + couldMatch2.get(couldMatch2.size() - 1).add(node); + } + if (couldMatch2.get(couldMatch2.size() - 1).size() == 0) { + return null; // no mapping for this node => fail + } + } + couldMatch2 = removeImpossibleCandidates(couldMatch2); + if (couldMatch2 == null) { + // after removing 'impossible' candidates, there's no match anymore => fail + return null; + } + if (subGraph.getNodes().size() == 1) { + // a single node with a candidate is a match => success + HashMap singleNodeMapping = new HashMap(); + singleNodeMapping.put(subGraph.getNodes().get(0), couldMatch2.get(0).get(0)); + return singleNodeMapping; + } + /* + * here I'm starting the application of the heuristics of the maximum restricted variable (H1) and the minimum node order (H2): + */ + // first save the old order of the matches: + HashMap oldIndices = new HashMap(); + for (int i = 0; i < subGraph.getNodes().size(); ++i) { + oldIndices.put(subGraph.getNodes().get(i), i); + } + // now check for the maximum restricted variables (H1): + ArrayList minimumIndices = new ArrayList(); + int minimumValue = Integer.MAX_VALUE; + for (int i = 0; i < subGraph.getNodes().size(); ++i) { // minimum candidates + if (couldMatch2.get(i).size() <= minimumValue) { + if (couldMatch2.get(i).size() < minimumValue) { + minimumIndices = new ArrayList(); + minimumValue = couldMatch2.get(i).size(); + } + minimumIndices.add(i); + } + } + // now check within those for the minimum node order (H2): + int indicesIndex = -1; + minimumValue = Integer.MAX_VALUE; + for (int i = 0; i < minimumIndices.size(); ++i) { // minimum node order (outgoing) + int outgoingCount = 0; + Node currentNode = subGraph.getNodes().get(minimumIndices.get(i)); + for (String key: currentNode.getEdges().keySet()) { + outgoingCount += currentNode.getEdges(key).size(); + } + if (outgoingCount < minimumValue) { + minimumValue = outgoingCount; + indicesIndex = i; + } + } + // here we have the 'best' node to start with: + Node heuristicallySelectedFirstNode = subGraph.getNodes().get(minimumIndices.get(indicesIndex)); + subGraph.getNodes().remove(heuristicallySelectedFirstNode); // remove from old position + subGraph.getNodes().add(0, heuristicallySelectedFirstNode); // put in front + // now order the nodes in a depth-first fashion, with the heuristically selected first node as 'root': + ArrayList sortedNodes = getDepthFirstSortedNodeList(subGraph); + // restore the matches to the new order: + ArrayList> couldMatch = new ArrayList>(); + for (int i = 0; i < sortedNodes.size(); ++i) { + couldMatch.add(couldMatch2.get(oldIndices.get(sortedNodes.get(i)))); + } + // and build candidates into objects, that contain an additional conflict-value and are sortable + ArrayList> couldMatchWithConflict = new ArrayList>(); + for (int i = 0; i < couldMatch.size(); ++i) { + ArrayList nodesToAdd = couldMatch.get(i); + couldMatchWithConflict.add(new ArrayList()); + for (int j = 0; j < nodesToAdd.size(); ++j) { + couldMatchWithConflict.get(i).add(new NodeWithConflict(nodesToAdd.get(j))); + } + } + 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 < couldMatchWithConflict.size(); ++i) { + currentTry.add(0); + mapping.put(sortedNodes.get(i), couldMatchWithConflict.get(i).get(0).getNode()); + } + /* + * only check this index against previous ones, + * if ok, increment and check only that one, and so on + */ + int checkIndex = 1; + int lastIndex = checkIndex - 1; +loop: while (checkIndex != -1) { + if (checkIndex > lastIndex) { + + + int startThisHeuristicsAtIndex = checkIndex; + + + + + + + + + + /* + * here I'm starting the application of the heuristics of the maximum restricted variable (H1) and the minimum node order (H2): + */ + // first save the old order of the matches: + oldIndices = new HashMap(); + for (int i = 0; i < sortedNodes.size(); ++i) { + oldIndices.put(sortedNodes.get(i), i); + } + // now check for the maximum restricted variables (H1): + minimumIndices = new ArrayList(); + minimumValue = Integer.MAX_VALUE; + for (int i = startThisHeuristicsAtIndex; i < sortedNodes.size(); ++i) { // minimum candidates + if (couldMatchWithConflict.get(i).size() <= minimumValue) { + if (couldMatchWithConflict.get(i).size() < minimumValue) { + minimumIndices = new ArrayList(); + minimumValue = couldMatchWithConflict.get(i).size(); + } + minimumIndices.add(i); + } + } + // now check within those for the minimum node order (H2): + indicesIndex = -1; + minimumValue = Integer.MAX_VALUE; + for (int i = 0; i < minimumIndices.size(); ++i) { // minimum node order (outgoing) + int outgoingCount = 0; + Node currentNode = sortedNodes.get(minimumIndices.get(i)); + for (String key: currentNode.getEdges().keySet()) { + outgoingCount += currentNode.getEdges(key).size(); + } + if (outgoingCount < minimumValue) { + minimumValue = outgoingCount; + indicesIndex = i; + } + } + // here we have the 'best' node to start with: + heuristicallySelectedFirstNode = subGraph.getNodes().get(minimumIndices.get(indicesIndex)); +// sortedNodes.remove(heuristicallySelectedFirstNode); // remove from old position +// sortedNodes.add(startThisHeuristicsAtIndex, heuristicallySelectedFirstNode); // put in front + // restore the matches to the new order: + ArrayList> couldMatchWithConflict2 = new ArrayList>(); + for (int i = 0; i < sortedNodes.size(); ++i) { + couldMatchWithConflict2.add(couldMatchWithConflict.get(oldIndices.get(sortedNodes.get(i)))); + } + couldMatchWithConflict.clear(); + couldMatchWithConflict.addAll(couldMatchWithConflict2); + + + + + + + + + + + + + +// /* +// * we now use the heuristics of minimal conflict (H3) - regarding the initial situation, +// * to sort the 'couldMatch'-lists by that measure. +// */ +// ArrayList nodesWithConflictToCheck = couldMatchWithConflict.get(startThisHeuristicsAtIndex); +// for (int index = 0; index < nodesWithConflictToCheck.size(); ++index) { +// NodeWithConflict nodeWithConflictToCheckMapped = nodesWithConflictToCheck.get(index); +// Node nodeToCheckMapped = nodeWithConflictToCheckMapped.getNode(); +// // check available outgoing candidates for conflict: +// for (int k = startThisHeuristicsAtIndex + 1; k < sortedNodes.size(); ++k) { +// for (String key: sortedNodes.get(startThisHeuristicsAtIndex).getEdges().keySet()) { +// for (Node target: sortedNodes.get(startThisHeuristicsAtIndex).getEdges(key)) { +// if (target == sortedNodes.get(k)) { +// ArrayList mappingsToTarget = couldMatchWithConflict.get(sortedNodes.indexOf(sortedNodes.get(startThisHeuristicsAtIndex))); +// for (int j = 0; j < mappingsToTarget.size(); ++j) { +// NodeWithConflict mappingToTarget = mappingsToTarget.get(j); +// Node nodeMappedToTarget = mappingToTarget.getNode(); +// if (!nodeToCheckMapped.getEdges(key).contains(nodeMappedToTarget)) { +// nodeWithConflictToCheckMapped.incrementConflict(); +// } +// } +// } +// } +// } +// } +// // check available incoming candidates for conflict: +// for (int k = startThisHeuristicsAtIndex + 1; k < sortedNodes.size(); ++k) { +// for (String key: sortedNodes.get(k).getEdges().keySet()) { +// for (Node target: sortedNodes.get(k).getEdges(key)) { +// if (target == sortedNodes.get(startThisHeuristicsAtIndex)) { +// ArrayList mappingsToSource = couldMatchWithConflict.get(sortedNodes.indexOf(sortedNodes.get(k))); +// for (int j = 0; j < mappingsToSource.size(); ++j) { +// NodeWithConflict mappingToSource = mappingsToSource.get(j); +// Node nodeMappedToSource = mappingToSource.getNode(); +// if (!nodeMappedToSource.getEdges(key).contains(nodeToCheckMapped)) { +// nodeWithConflictToCheckMapped.incrementConflict(); +// } +// } +// } +// } +// } +// } +// } +// Collections.sort(nodesWithConflictToCheck); + // ok, done the candidates are sorted according to minimum conflict heuristics (H3) + } + for (int i = checkIndex; i < sortedNodes.size(); ++i) { + /* + * check sortedNodes.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 + */ + Node currentSubNode = sortedNodes.get(i); + boolean fail = false; +match: for (int j = 0; j < i; ++j) { + Node otherSubNode = sortedNodes.get(j); + if (mapping.get(currentSubNode) == mapping.get(otherSubNode)) { + fail = true; // found duplicate! + break match; + } + for (String key: currentSubNode.getEdges().keySet()) { + if (currentSubNode.getEdges(key).contains(otherSubNode)) { + if (!mapping.get(currentSubNode).getEdges(key).contains(mapping.get(otherSubNode))) { + fail = true; // missing outgoing edge + break match; + } + } + } + for (String key: otherSubNode.getEdges().keySet()) { + if (otherSubNode.getEdges(key).contains(currentSubNode)) { + if (!mapping.get(otherSubNode).getEdges(key).contains(mapping.get(currentSubNode))) { + fail = true; // missing 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) + */ + lastIndex = checkIndex; + checkIndex = i; + while (checkIndex >= 0 && currentTry.get(checkIndex) == couldMatchWithConflict.get(checkIndex).size() - 1) { + --checkIndex; + } + if (checkIndex >= 0) { + currentTry.set(checkIndex, currentTry.get(checkIndex) + 1); + mapping.put(sortedNodes.get(checkIndex), couldMatchWithConflict.get(checkIndex).get(currentTry.get(checkIndex)).getNode()); + for (int j = checkIndex + 1; j < sortedNodes.size(); ++j) { + currentTry.set(j, 0); + mapping.put(sortedNodes.get(j), couldMatchWithConflict.get(j).get(0).getNode()); + } + } + continue loop; + } + } + return mapping; // it ran through with no errors => success + } + return null; // nothing left to check => fail + } + + private ArrayList> removeImpossibleCandidates(ArrayList> couldMatch) { + ArrayList> cantMatch = new ArrayList>(); + for (int i = 0; i < couldMatch.size(); ++i) { + cantMatch.add(new ArrayList()); + } + // go through all candidates: + for (int i = 0; i < couldMatch.size(); ++i) { + if (couldMatch.get(i).size() == 1) { + // one node has only one candidate, all other nodes can't have this candidate + for (int j = 0; j < couldMatch.size(); ++j) { + if (i != j) { + // 'tell' all other nodes, they can't have this candidate: + cantMatch.get(j).add(couldMatch.get(i).get(0)); + } + } + } + } + // go through all candidatzes again: + for (int i = 0; i < couldMatch.size(); ++i) { + // remove all impossible candidates: + for (int j = 0; j < cantMatch.get(i).size(); ++j) { + couldMatch.get(i).remove(cantMatch.get(i).get(j)); + } + // if one node has no candidates anymore, return null + if (couldMatch.get(i).size() < 1) { + return null; + } + } + return couldMatch; + } + + @Override + public Graph normalized(Graph graph) { + return GraphEngine.getNormalizationFallback().normalized(graph); + } + +} diff --git a/src/main/java/org/fujaba/graphengine/isomorphismtools/heuristics/NodeWithConflict.java b/src/main/java/org/fujaba/graphengine/isomorphismtools/heuristics/NodeWithConflict.java new file mode 100644 index 0000000..a8a820b --- /dev/null +++ b/src/main/java/org/fujaba/graphengine/isomorphismtools/heuristics/NodeWithConflict.java @@ -0,0 +1,31 @@ +package org.fujaba.graphengine.isomorphismtools.heuristics; + +import org.fujaba.graphengine.graph.Node; + +public class NodeWithConflict implements Comparable { + + private Node node = null; + private int conflict = 0; + + public NodeWithConflict(Node node) { + this.node = node; + } + public Node getNode() { + return node; + } + public int getConflict() { + return conflict; + } + public void resetConflict() { + this.conflict = 0; + } + public void incrementConflict() { + ++this.conflict; + } + + @Override + public int compareTo(NodeWithConflict other) { + return this.getConflict() - other.getConflict(); + } + +} diff --git a/src/main/resources/data.json b/src/main/resources/data.json index a7020e6..b8ea241 100644 --- a/src/main/resources/data.json +++ b/src/main/resources/data.json @@ -1 +1 @@ -{"nodes":[{"id":0,"label":"{type\u003droad}","x":-0.013052619222005157,"y":-0.09914448613738104,"size":10,"color":"#00F"},{"id":1,"label":"{type\u003droad}","x":-0.038268343236508975,"y":-0.09238795325112867,"size":10,"color":"#000"},{"id":2,"label":"{type\u003droad}","x":-0.060876142900872066,"y":-0.07933533402912352,"size":10,"color":"#000"},{"id":3,"label":"{type\u003droad}","x":-0.07933533402912352,"y":-0.060876142900872066,"size":10,"color":"#000"},{"id":4,"label":"{type\u003droad}","x":-0.09238795325112867,"y":-0.03826834323650898,"size":10,"color":"#000"},{"id":5,"label":"{type\u003droad}","x":-0.09914448613738104,"y":-0.013052619222005171,"size":10,"color":"#000"},{"id":6,"label":"{type\u003droad}","x":-0.09914448613738105,"y":0.013052619222005138,"size":10,"color":"#000"},{"id":7,"label":"{type\u003droad}","x":-0.09238795325112867,"y":0.038268343236508975,"size":10,"color":"#000"},{"id":8,"label":"{type\u003droad}","x":-0.07933533402912352,"y":0.060876142900872066,"size":10,"color":"#000"},{"id":9,"label":"{type\u003droad}","x":-0.060876142900872086,"y":0.0793353340291235,"size":10,"color":"#000"},{"id":10,"label":"{type\u003droad}","x":-0.03826834323650899,"y":0.09238795325112867,"size":10,"color":"#000"},{"id":11,"label":"{pass\u003dtrue, type\u003dsignal}","x":-0.013052619222005157,"y":0.09914448613738104,"size":10,"color":"#F00"},{"id":12,"label":"{pass\u003dfalse, type\u003dsignal}","x":0.013052619222005176,"y":0.09914448613738104,"size":10,"color":"#F00"},{"id":13,"label":"{type\u003dcar, direction\u003deast}","x":0.03826834323650897,"y":0.09238795325112868,"size":10,"color":"#F00"},{"id":14,"label":"{type\u003dcar, direction\u003deast}","x":0.06087614290087203,"y":0.07933533402912354,"size":10,"color":"#F00"},{"id":15,"label":"{type\u003dcar, direction\u003dwest}","x":0.07933533402912349,"y":0.060876142900872086,"size":10,"color":"#F00"},{"id":16,"label":"{type\u003dcar, direction\u003dwest}","x":0.09238795325112865,"y":0.03826834323650903,"size":10,"color":"#F00"},{"id":17,"label":"{type\u003dcar, direction\u003dwest}","x":0.09914448613738104,"y":0.013052619222005162,"size":10,"color":"#F00"},{"id":18,"label":"{type\u003dcar, direction\u003deast}","x":0.09914448613738105,"y":-0.013052619222005128,"size":10,"color":"#F00"},{"id":19,"label":"{type\u003dcar, direction\u003deast}","x":0.09238795325112867,"y":-0.038268343236509,"size":10,"color":"#F00"},{"id":20,"label":"{type\u003dcar, direction\u003deast}","x":0.07933533402912352,"y":-0.06087614290087205,"size":10,"color":"#F00"},{"id":21,"label":"{type\u003dcar, direction\u003deast}","x":0.060876142900872086,"y":-0.07933533402912349,"size":10,"color":"#F00"},{"id":22,"label":"{type\u003dcar, direction\u003deast}","x":0.03826834323650904,"y":-0.09238795325112865,"size":10,"color":"#F00"},{"id":23,"label":"{type\u003dcar, direction\u003dwest}","x":0.01305261922200517,"y":-0.09914448613738104,"size":10,"color":"#F00"}],"edges":[{"id":24,"label":"car","source":0,"target":15,"size":10,"color":"#888"},{"id":25,"label":"car","source":1,"target":16,"size":10,"color":"#888"},{"id":26,"label":"west","source":1,"target":0,"size":10,"color":"#888"},{"id":27,"label":"east","source":2,"target":3,"size":10,"color":"#888"},{"id":28,"label":"car","source":2,"target":20,"size":10,"color":"#888"},{"id":29,"label":"west","source":2,"target":1,"size":10,"color":"#888"},{"id":30,"label":"east","source":3,"target":4,"size":10,"color":"#888"},{"id":31,"label":"car","source":3,"target":19,"size":10,"color":"#888"},{"id":32,"label":"west","source":3,"target":2,"size":10,"color":"#888"},{"id":33,"label":"east","source":4,"target":9,"size":10,"color":"#888"},{"id":34,"label":"car","source":4,"target":18,"size":10,"color":"#888"},{"id":35,"label":"west","source":4,"target":3,"size":10,"color":"#888"},{"id":36,"label":"car","source":5,"target":17,"size":10,"color":"#888"},{"id":37,"label":"west","source":5,"target":4,"size":10,"color":"#888"},{"id":38,"label":"signal","source":5,"target":12,"size":10,"color":"#888"},{"id":39,"label":"car","source":6,"target":23,"size":10,"color":"#888"},{"id":40,"label":"west","source":6,"target":5,"size":10,"color":"#888"},{"id":41,"label":"east","source":7,"target":8,"size":10,"color":"#888"},{"id":42,"label":"car","source":7,"target":22,"size":10,"color":"#888"},{"id":43,"label":"east","source":8,"target":2,"size":10,"color":"#888"},{"id":44,"label":"car","source":8,"target":21,"size":10,"color":"#888"},{"id":45,"label":"signal","source":8,"target":11,"size":10,"color":"#888"},{"id":46,"label":"east","source":9,"target":10,"size":10,"color":"#888"},{"id":47,"label":"car","source":9,"target":14,"size":10,"color":"#888"},{"id":48,"label":"car","source":10,"target":13,"size":10,"color":"#888"}]} \ No newline at end of file +{"nodes":[{"id":0,"label":"{type\u003droad}","x":-0.022252093395631438,"y":-0.09749279121818236,"size":10,"color":"#00F"},{"id":1,"label":"{type\u003droad}","x":-0.06234898018587335,"y":-0.07818314824680297,"size":10,"color":"#000"},{"id":2,"label":"{type\u003droad}","x":-0.09009688679024191,"y":-0.04338837391175582,"size":10,"color":"#000"},{"id":3,"label":"{type\u003droad}","x":-0.1,"y":-6.123233995736766E-18,"size":10,"color":"#000"},{"id":4,"label":"{type\u003droad}","x":-0.09009688679024191,"y":0.043388373911755804,"size":10,"color":"#000"},{"id":5,"label":"{type\u003droad}","x":-0.06234898018587336,"y":0.07818314824680297,"size":10,"color":"#000"},{"id":6,"label":"{pass\u003dtrue, type\u003dsignal}","x":-0.022252093395631452,"y":0.09749279121818236,"size":10,"color":"#F00"},{"id":7,"label":"{pass\u003dfalse, type\u003dsignal}","x":0.022252093395631427,"y":0.09749279121818236,"size":10,"color":"#F00"},{"id":8,"label":"{type\u003dcar, direction\u003deast}","x":0.0623489801858733,"y":0.07818314824680302,"size":10,"color":"#F00"},{"id":9,"label":"{type\u003dcar, direction\u003dwest}","x":0.0900968867902419,"y":0.04338837391175583,"size":10,"color":"#F00"},{"id":10,"label":"{type\u003dcar, direction\u003dwest}","x":0.1,"y":1.8369701987210297E-17,"size":10,"color":"#F00"},{"id":11,"label":"{type\u003dcar, direction\u003deast}","x":0.09009688679024193,"y":-0.0433883739117558,"size":10,"color":"#F00"},{"id":12,"label":"{type\u003dcar, direction\u003deast}","x":0.06234898018587337,"y":-0.07818314824680297,"size":10,"color":"#F00"},{"id":13,"label":"{type\u003dcar, direction\u003deast}","x":0.022252093395631466,"y":-0.09749279121818236,"size":10,"color":"#F00"}],"edges":[{"id":14,"label":"car","source":0,"target":9,"size":10,"color":"#888"},{"id":15,"label":"east","source":1,"target":2,"size":10,"color":"#888"},{"id":16,"label":"car","source":1,"target":12,"size":10,"color":"#888"},{"id":17,"label":"west","source":1,"target":0,"size":10,"color":"#888"},{"id":18,"label":"east","source":2,"target":5,"size":10,"color":"#888"},{"id":19,"label":"car","source":2,"target":11,"size":10,"color":"#888"},{"id":20,"label":"west","source":2,"target":1,"size":10,"color":"#888"},{"id":21,"label":"car","source":3,"target":10,"size":10,"color":"#888"},{"id":22,"label":"west","source":3,"target":2,"size":10,"color":"#888"},{"id":23,"label":"signal","source":3,"target":7,"size":10,"color":"#888"},{"id":24,"label":"east","source":4,"target":1,"size":10,"color":"#888"},{"id":25,"label":"car","source":4,"target":13,"size":10,"color":"#888"},{"id":26,"label":"signal","source":4,"target":6,"size":10,"color":"#888"},{"id":27,"label":"car","source":5,"target":8,"size":10,"color":"#888"}]} \ No newline at end of file diff --git a/src/test/java/org/fujaba/graphengine/unitTests/GraphTest.java b/src/test/java/org/fujaba/graphengine/unitTests/GraphTest.java index bb2b93b..0e3a2fb 100644 --- a/src/test/java/org/fujaba/graphengine/unitTests/GraphTest.java +++ b/src/test/java/org/fujaba/graphengine/unitTests/GraphTest.java @@ -7,7 +7,7 @@ import org.fujaba.graphengine.graph.Graph; import org.fujaba.graphengine.graph.Node; import org.fujaba.graphengine.isomorphismtools.IsomorphismHandler; -import org.fujaba.graphengine.isomorphismtools.IsomorphismHandlerCSP; +import org.fujaba.graphengine.isomorphismtools.IsomorphismHandlerCSPWithHeuristics; import org.fujaba.graphengine.isomorphismtools.IsomorphismHandlerCombinatorial; import org.fujaba.graphengine.isomorphismtools.IsomorphismHandlerSorting; import org.junit.Assert; @@ -367,7 +367,7 @@ public void testIsomorphismTestGraph() { IsomorphismHandler isomorphismHandler = null; // isomorphismHandler = GraphEngine.getMainIsomorphismHandler(); -// isomorphismHandler = new IsomorphismHandlerCSP(); +// isomorphismHandler = new IsomorphismHandlerCSPWithHeuristics(); // isomorphismHandler = new IsomorphismHandlerSorting(); isomorphismHandler = new IsomorphismHandlerCombinatorial(); long start = System.nanoTime(); diff --git a/src/test/java/org/fujaba/graphengine/unitTests/PatternTest.java b/src/test/java/org/fujaba/graphengine/unitTests/PatternTest.java index e428ac6..1a7ca12 100644 --- a/src/test/java/org/fujaba/graphengine/unitTests/PatternTest.java +++ b/src/test/java/org/fujaba/graphengine/unitTests/PatternTest.java @@ -9,7 +9,7 @@ import org.fujaba.graphengine.PatternEngine; import org.fujaba.graphengine.graph.Graph; import org.fujaba.graphengine.graph.Node; -import org.fujaba.graphengine.isomorphismtools.IsomorphismHandlerCSP; +import org.fujaba.graphengine.isomorphismtools.IsomorphismHandlerCSPWithHeuristics; import org.fujaba.graphengine.isomorphismtools.IsomorphismHandlerSorting; import org.fujaba.graphengine.pattern.PatternAttribute; import org.fujaba.graphengine.pattern.PatternEdge; @@ -263,9 +263,9 @@ public void testNegativePatternVariantsWithDifferentIsomorphismCheckApproaches() System.out.println("GraphEngine: " + duration + "ms"); begin = System.nanoTime(); - Assert.assertTrue(new IsomorphismHandlerCSP().isIsomorphTo(carGraph, carGraphChangedNodeOrder)); + Assert.assertTrue(new IsomorphismHandlerCSPWithHeuristics().isIsomorphTo(carGraph, carGraphChangedNodeOrder)); duration = (System.nanoTime() - begin) / 1e6; - System.out.println("IsomorphismHandlerCSP: " + duration + "ms"); + System.out.println("IsomorphismHandlerCSPWithHeuristics: " + duration + "ms"); begin = System.nanoTime(); Assert.assertTrue(new IsomorphismHandlerSorting().isIsomorphTo(carGraph, carGraphChangedNodeOrder)); diff --git a/src/test/java/org/fujaba/graphengine/unitTests/RoadworkExample.java b/src/test/java/org/fujaba/graphengine/unitTests/RoadworkExample.java index 87a8517..205a19b 100644 --- a/src/test/java/org/fujaba/graphengine/unitTests/RoadworkExample.java +++ b/src/test/java/org/fujaba/graphengine/unitTests/RoadworkExample.java @@ -1,12 +1,17 @@ package org.fujaba.graphengine.unitTests; import java.util.ArrayList; +import java.util.HashMap; import org.fujaba.graphengine.GraphEngine; import org.fujaba.graphengine.Match; import org.fujaba.graphengine.PatternEngine; import org.fujaba.graphengine.graph.Graph; import org.fujaba.graphengine.graph.Node; +import org.fujaba.graphengine.isomorphismtools.IsomorphismHandler; +import org.fujaba.graphengine.isomorphismtools.IsomorphismHandlerCSP; +import org.fujaba.graphengine.isomorphismtools.IsomorphismHandlerCSPWithHeuristics; +import org.fujaba.graphengine.isomorphismtools.IsomorphismHandlerCSPWithMoreHeuristics; import org.fujaba.graphengine.isomorphismtools.IsomorphismHandlerCombinatorial; import org.fujaba.graphengine.pattern.PatternAttribute; import org.fujaba.graphengine.pattern.PatternEdge; @@ -303,11 +308,89 @@ public void testRoadworkExample() { patterns.get(0).add(moveCarWestPattern()); long begin; + Graph reachabilityGraph; + + + GraphEngine.setMainIsomorphismHandler(new IsomorphismHandlerCSPWithMoreHeuristics()); + System.out.println("\nnew IsomorphismHandlerCSPWithMoreHeuristics()"); + + + + System.out.println("\n(minimal) starting to build reachability graph for RoadworkExample..."); + begin = System.nanoTime(); + reachabilityGraph = PatternEngine.calculateReachabilityGraph(getMinimalStartGraph(), patterns); + System.out.println("done building reachability graph for RoadworkExample after " + ((System.nanoTime() - begin) / 1e6) + " ms."); + System.out.println("== " + ((System.nanoTime() - begin) / 1e9) + " s"); + System.out.println("== " + ((System.nanoTime() - begin) / 1e9 / 60) + " m"); + System.out.println("== " + ((System.nanoTime() - begin) / 1e9 / 60 / 60) + " h"); + System.out.println(reachabilityGraph.getNodes().size() + " node" + (reachabilityGraph.getNodes().size() != 1 ? "s" : "") + " in the 'reachabilityGraph'"); + + + System.out.println("\n(minimal + 1) starting to build reachability graph for RoadworkExample..."); + begin = System.nanoTime(); + reachabilityGraph = PatternEngine.calculateReachabilityGraph(getMinimalPlusOneStartGraph(), patterns); + System.out.println("done building reachability graph for RoadworkExample after " + ((System.nanoTime() - begin) / 1e6) + " ms."); + System.out.println("== " + ((System.nanoTime() - begin) / 1e9) + " s"); + System.out.println("== " + ((System.nanoTime() - begin) / 1e9 / 60) + " m"); + System.out.println("== " + ((System.nanoTime() - begin) / 1e9 / 60 / 60) + " h"); + System.out.println(reachabilityGraph.getNodes().size() + " node" + (reachabilityGraph.getNodes().size() != 1 ? "s" : "") + " in the 'reachabilityGraph'"); + + +// System.out.println("\n(original) starting to build reachability graph for RoadworkExample..."); +// begin = System.nanoTime(); +// reachabilityGraph = PatternEngine.calculateReachabilityGraph(getStartGraph(), patterns); +// System.out.println("done building reachability graph for RoadworkExample after " + ((System.nanoTime() - begin) / 1e6) + " ms."); +// System.out.println("== " + ((System.nanoTime() - begin) / 1e9) + " s"); +// System.out.println("== " + ((System.nanoTime() - begin) / 1e9 / 60) + " m"); +// System.out.println("== " + ((System.nanoTime() - begin) / 1e9 / 60 / 60) + " h"); +// System.out.println(reachabilityGraph.getNodes().size() + " node" + (reachabilityGraph.getNodes().size() != 1 ? "s" : "") + " in the 'reachabilityGraph'"); + + + + GraphEngine.setMainIsomorphismHandler(new IsomorphismHandlerCSP()); + System.out.println("\nnew IsomorphismHandlerCSP()"); + + + + System.out.println("\n(minimal) starting to build reachability graph for RoadworkExample..."); + begin = System.nanoTime(); + reachabilityGraph = PatternEngine.calculateReachabilityGraph(getMinimalStartGraph(), patterns); + System.out.println("done building reachability graph for RoadworkExample after " + ((System.nanoTime() - begin) / 1e6) + " ms."); + System.out.println("== " + ((System.nanoTime() - begin) / 1e9) + " s"); + System.out.println("== " + ((System.nanoTime() - begin) / 1e9 / 60) + " m"); + System.out.println("== " + ((System.nanoTime() - begin) / 1e9 / 60 / 60) + " h"); + System.out.println(reachabilityGraph.getNodes().size() + " node" + (reachabilityGraph.getNodes().size() != 1 ? "s" : "") + " in the 'reachabilityGraph'"); + + + System.out.println("\n(minimal + 1) starting to build reachability graph for RoadworkExample..."); + begin = System.nanoTime(); + reachabilityGraph = PatternEngine.calculateReachabilityGraph(getMinimalPlusOneStartGraph(), patterns); + System.out.println("done building reachability graph for RoadworkExample after " + ((System.nanoTime() - begin) / 1e6) + " ms."); + System.out.println("== " + ((System.nanoTime() - begin) / 1e9) + " s"); + System.out.println("== " + ((System.nanoTime() - begin) / 1e9 / 60) + " m"); + System.out.println("== " + ((System.nanoTime() - begin) / 1e9 / 60 / 60) + " h"); + System.out.println(reachabilityGraph.getNodes().size() + " node" + (reachabilityGraph.getNodes().size() != 1 ? "s" : "") + " in the 'reachabilityGraph'"); + + +// System.out.println("\n(original) starting to build reachability graph for RoadworkExample..."); +// begin = System.nanoTime(); +// reachabilityGraph = PatternEngine.calculateReachabilityGraph(getStartGraph(), patterns); +// System.out.println("done building reachability graph for RoadworkExample after " + ((System.nanoTime() - begin) / 1e6) + " ms."); +// System.out.println("== " + ((System.nanoTime() - begin) / 1e9) + " s"); +// System.out.println("== " + ((System.nanoTime() - begin) / 1e9 / 60) + " m"); +// System.out.println("== " + ((System.nanoTime() - begin) / 1e9 / 60 / 60) + " h"); +// System.out.println(reachabilityGraph.getNodes().size() + " node" + (reachabilityGraph.getNodes().size() != 1 ? "s" : "") + " in the 'reachabilityGraph'"); + + + + GraphEngine.setMainIsomorphismHandler(new IsomorphismHandlerCSPWithHeuristics()); + System.out.println("\nnew IsomorphismHandlerCSPWithHeuristics()"); + System.out.println("\n(minimal) starting to build reachability graph for RoadworkExample..."); begin = System.nanoTime(); - Graph reachabilityGraph = PatternEngine.calculateReachabilityGraph(getMinimalStartGraph(), patterns); + reachabilityGraph = PatternEngine.calculateReachabilityGraph(getMinimalStartGraph(), patterns); System.out.println("done building reachability graph for RoadworkExample after " + ((System.nanoTime() - begin) / 1e6) + " ms."); System.out.println("== " + ((System.nanoTime() - begin) / 1e9) + " s"); System.out.println("== " + ((System.nanoTime() - begin) / 1e9 / 60) + " m");