Skip to content

Commit

Permalink
#28 introduced hashing of graphs into reachability graph construction
Browse files Browse the repository at this point in the history
  • Loading branch information
hoechp committed Feb 26, 2017
1 parent 8185973 commit f8c3b4a
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 5 deletions.
26 changes: 26 additions & 0 deletions src/main/java/org/fujaba/graphengine/GraphEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -279,4 +279,30 @@ public static ArrayList<ArrayList<Node>> removeImpossibleCandidates(ArrayList<Ar
return couldMatch;
}

public static int generateHash(Graph g) {
int hash = ((Integer)g.getNodes().size()).hashCode();
for (Node n: g.getNodes()) {
for (String key: n.getEdges().keySet()) {
hash += key.hashCode();
hash += ((Integer)n.getEdges(key).size()).hashCode();
}
for (String key: n.getAttributes().keySet()) {
hash += key.hashCode();
Object value = n.getAttribute(key);
if (value instanceof Double) {
hash += ((Double)value).hashCode();
} else if (value instanceof Long) {
hash += ((Long)value).hashCode();
} else if (value instanceof Integer) {
hash += ((Integer)value).hashCode();
} else if (value instanceof Boolean) {
hash += ((Boolean)value).hashCode();
} else if (value instanceof String) {
hash += ((String)value).hashCode();
}
}
}
return hash;
}

}
135 changes: 131 additions & 4 deletions src/main/java/org/fujaba/graphengine/PatternEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public Evaluator load(Node node) {
* @param patterns a prioritized list of patterns. the first list is the highest priority-level and so on.
* @return returns the reachability graph, that was calculated
*/
public static Graph calculateReachabilityGraph(Graph graph, ArrayList<ArrayList<PatternGraph>> patterns) {
public static Graph calculateReachabilityGraphOld(Graph graph, ArrayList<ArrayList<PatternGraph>> patterns) {
/*
* ok right now i think im going for this priority concept, where i have an arraylist of 'priority-levels'
* inside each priority-level, there's a list of patterns to match.
Expand Down Expand Up @@ -86,17 +86,105 @@ public static Graph calculateReachabilityGraph(Graph graph, ArrayList<ArrayList<
* of unprocessed nodes. just as simple as that...
*/

if (GraphEngine.getMainIsomorphismHandler() instanceof IsomorphismHandlerSorting) {
return calculateReachabilityGraphWithNormalForm(graph, patterns);
}

// the first rg-node is the base-graph:
Graph rg = new Graph().addNode(new Node().setAttribute("graph", graph.toString()));
ArrayList<Graph> added = new ArrayList<Graph>(); // a list with graphs that were added
ArrayList<Graph> unprocessed = new ArrayList<Graph>(); // a list with currently unprocessed graphs
added.add(graph);
unprocessed.add(graph);

// as long as a single graph wasn't checked for successors, the search continues:
while (unprocessed.size() > 0) {
// looking for matches:
ArrayList<Match> matches = calculateReachabilityNodeMatches(unprocessed.get(0), patterns);
// look up the rg-node, that represents the unprocessed graph:
Node source = findGraphInReachabilityGraph(rg, unprocessed.get(0));
unprocessed.remove(0);
// now handle matches:
for (Match match: matches) {
// construct the graph, that's the result of this match:
Graph successor = applyMatch(match);
// check if the graph was previously added:
int index = indexOf(added, successor);
if (index != -1) {
// yes, the graph already did exist => just build edge to an existing node
Node target = findGraphInReachabilityGraph(rg, successor);
source.addEdge(match.getPattern().toString(), target); // new edge
} else {
// System.out.println("reached new state with '" + match.getPattern().getName() + "'"); // TODO: remove debug
// no, the graph didn't exist before => add a new node
Node target = new Node().setAttribute("graph", successor.toString()); // new node
rg.addNode(target);
source.addEdge(match.getPattern().toString(), target); // edge to new node
added.add(successor);
unprocessed.add(successor);
}
}
}
// done
return rg;
}

/**
* Calculates a reachability graph based on a graph of the initial situation
* and a prioritized list of patterns to match and apply on the graph and resulting graphs.
* @param graph the initial graph
* @param patterns a prioritized list of patterns. the first list is the highest priority-level and so on.
* @return returns the reachability graph, that was calculated
*/
public static Graph calculateReachabilityGraph(Graph graph, ArrayList<ArrayList<PatternGraph>> patterns) {
/*
* ok right now i think im going for this priority concept, where i have an arraylist of 'priority-levels'
* inside each priority-level, there's a list of patterns to match.
*
* the first level, where there's a match found inside is used -> children of that graph will be all graphs
* with the applied matches of that level and that level alone.
*
* with this approach im kind of very flexible on whatever priority concept really will be needed!
*/

/*
* the graph itself is made up like this:
*
* each node represents a graph and has an attribute 'graph' and as value the serialized graph.
* the edges represent the application of a certain pattern-graph, with its serialization as a node-name.
*
*/

/*
* the algorithm for calculating the graph itself is like this:
*
* first add the base-graph itself to the RG. and add it to a list with unprocessed nodes.
* from then on go though all unprocessed nodes and check for successors,
* add them according to the naming scheme to the RG and add em to the list of unprocessed nodes.
* and so on.
*
* just take care when finding successors, to always check if they were already existing,
* if they did, make the edge go to the previously added nodes in the EG and DON'T add them to the list
* of unprocessed nodes. just as simple as that...
*/

if (GraphEngine.getMainIsomorphismHandler() instanceof IsomorphismHandlerSorting) {
return calculateReachabilityGraphWithNormalForm(graph, patterns);
}

// the first rg-node is the base-graph:
Graph rg = new Graph().addNode(new Node().setAttribute("graph", graph.toString()));
ArrayList<Graph> added = new ArrayList<Graph>(); // a list with graphs that were added
ArrayList<Graph> unprocessed = new ArrayList<Graph>(); // a list with currently unprocessed graphs
added.add(graph);
unprocessed.add(graph);

HashMap<Integer, ArrayList<Integer>> hashMap = new HashMap<Integer, ArrayList<Integer>>(); // map hash-code of serialization to node-index within the RG
int rgNodeCount = 0;
ArrayList<Integer> firstList = new ArrayList<Integer>();
firstList.add(rgNodeCount++);
hashMap.put(GraphEngine.generateHash(graph), firstList); // add first hash mapped to index

// as long as a single graph wasn't checked for successors, the search continues:
while (unprocessed.size() > 0) {
// looking for matches:
Expand All @@ -109,14 +197,33 @@ public static Graph calculateReachabilityGraph(Graph graph, ArrayList<ArrayList<
// construct the graph, that's the result of this match:
Graph successor = applyMatch(match);
// check if the graph was previously added:
int index = indexOf(added, successor);
int index = -1;
int newHash = GraphEngine.generateHash(successor);
if (hashMap.containsKey(newHash)) {
for (Integer indexToTest: hashMap.get(newHash)) {
//Graph graphToTest = GraphEngine.getGson().fromJson((String)rg.getNodes().get(indexToTest).getAttribute("graph"), Graph.class);
Graph graphToTest = added.get(indexToTest);
if (GraphEngine.isIsomorphTo(successor, graphToTest)) {
index = indexToTest;
break;
}
}
}
// index = indexOf(added, successor);
if (index != -1) {
// yes, the graph already did exist => just build edge to an existing node
Node target = findGraphInReachabilityGraph(rg, successor);
Node target = rg.getNodes().get(index);
source.addEdge(match.getPattern().toString(), target); // new edge
} else {
// System.out.println("reached new state with '" + match.getPattern().getName() + "'"); // TODO: remove debug
// no, the graph didn't exist before => add a new node
if (hashMap.containsKey(newHash)) {
hashMap.get(newHash).add(rgNodeCount++);
} else {
ArrayList<Integer> newList = new ArrayList<Integer>();
newList.add(rgNodeCount++);
hashMap.put(newHash, newList);
}
Node target = new Node().setAttribute("graph", successor.toString()); // new node
rg.addNode(target);
source.addEdge(match.getPattern().toString(), target); // edge to new node
Expand All @@ -137,6 +244,11 @@ public static Graph calculateReachabilityGraphWithNormalForm(Graph graph, ArrayL
ArrayList<Graph> unprocessed = new ArrayList<Graph>(); // a list with currently unprocessed graphs
added.add(graph);
unprocessed.add(graph);
HashMap<Integer, ArrayList<Integer>> hashMap = new HashMap<Integer, ArrayList<Integer>>(); // map hash-code of serialization to node-index within the RG
int rgNodeCount = 0;
ArrayList<Integer> firstList = new ArrayList<Integer>();
firstList.add(rgNodeCount++);
hashMap.put(graph.toString().hashCode(), firstList); // add first hash mapped to index
// as long as a single graph wasn't checked for successors, the search continues:
while (unprocessed.size() > 0) {
// looking for matches:
Expand All @@ -151,13 +263,28 @@ public static Graph calculateReachabilityGraphWithNormalForm(Graph graph, ArrayL
// check if the graph was previously added:
successor = GraphEngine.getMainIsomorphismHandler().normalized(successor);
String serializedGraph = successor.toString();
int index = normalizedIndexOf(rg.getNodes(), serializedGraph);
int index = -1;
if (hashMap.containsKey(serializedGraph.hashCode())) { // check for hashes
for (int indexToTest: hashMap.get(serializedGraph.hashCode())) { // for each matched hash:
if (rg.getNodes().get(indexToTest).getAttribute("graph").equals(serializedGraph)) { // doing the isomorphism check
index = indexToTest;
break;
}
}
}
if (index != -1) {
// yes, the graph already did exist => just build edge to an existing node
Node target = rg.getNodes().get(index);
source.addEdge(match.getPattern().toString(), target); // new edge
} else {
// no, the graph didn't exist before => add a new node
if (hashMap.containsKey(serializedGraph.hashCode())) { // save the new hash
hashMap.get(serializedGraph.hashCode()).add(rgNodeCount++);
} else {
ArrayList<Integer> newList = new ArrayList<Integer>();
newList.add(rgNodeCount++);
hashMap.put(serializedGraph.hashCode(), newList);
}
Node target = new Node().setAttribute("graph", serializedGraph); // new node
rg.addNode(target);
source.addEdge(match.getPattern().toString(), target); // edge to new node
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,59 @@ Graph getStartGraph() {
n6.addEdge("signal", signalE);
g.addNode(signalW, signalE);

// Node map = new Node();
// map.setAttribute("type", "map");
// map.addEdge("roads", n1, n2, n3, n4, n5, n6, n7, s1, s2, s6, s7);
// g.addNode(map);

return g;
}
Graph getBiggerStartGraph() {
Graph g = new Graph();

Node n1 = new Node().setAttribute("type", "road");
Node n2 = new Node().setAttribute("type", "road");
Node n3 = new Node().setAttribute("type", "road");
Node n4 = new Node().setAttribute("type", "road");
Node n5 = new Node().setAttribute("type", "road");
Node n6 = new Node().setAttribute("type", "road");
Node n7 = new Node().setAttribute("type", "road");
Node n8 = new Node().setAttribute("type", "road");
Node n9 = new Node().setAttribute("type", "road");

Node s1 = new Node().setAttribute("type", "road");
Node s2 = new Node().setAttribute("type", "road");
Node s8 = new Node().setAttribute("type", "road");
Node s9 = new Node().setAttribute("type", "road");

g.addNode(n1, n2, n3, n4, n5, n6, n7, n8, n9,
s1, s2, s8, s9);

n9.addEdge("west", n8);
n8.addEdge("west", n7);
n7.addEdge("west", n6);
n6.addEdge("west", n5);
n5.addEdge("west", n4);
n4.addEdge("west", n3);
n3.addEdge("west", n2);
n2.addEdge("west", n1);

s1.addEdge("east", s2);
s2.addEdge("east", n3);
n3.addEdge("east", n4);
n4.addEdge("east", n5);
n5.addEdge("east", n6);
n6.addEdge("east", n7);
n7.addEdge("east", s8);
s8.addEdge("east", s9);

Node signalW = new Node().setAttribute("type", "signal").setAttribute("pass", false);
Node signalE = new Node().setAttribute("type", "signal").setAttribute("pass", false);

s2.addEdge("signal", signalW);
n8.addEdge("signal", signalE);
g.addNode(signalW, signalE);

// Node map = new Node();
// map.setAttribute("type", "map");
// map.addEdge("roads", n1, n2, n3, n4, n5, n6, n7, s1, s2, s6, s7);
Expand Down Expand Up @@ -355,6 +408,18 @@ private double testRoadworkExample(boolean debug, IsomorphismHandler ih, int fro
System.out.println(" " + reachabilityGraph.getNodes().size() + " node" + (reachabilityGraph.getNodes().size() != 1 ? "s" : "") + " in the 'reachabilityGraph'");
}
}
if (fromLevel <= 4 && toLevel >= 4) {
if (debug) {
System.out.println("\nbuild reachability graph for RoadworkExample (bigger map with 12 pieces of road)...");
}
begin = System.nanoTime();
reachabilityGraph = PatternEngine.calculateReachabilityGraph(getBiggerStartGraph(), patterns);
total += (System.nanoTime() - begin) / 1e6;
if (debug) {
System.out.println("-> done building reachability graph for RoadworkExample after " + ((System.nanoTime() - begin) / 1e9) + " s == " + ((System.nanoTime() - begin) / 1e9 / 60) + " m");
System.out.println(" " + reachabilityGraph.getNodes().size() + " node" + (reachabilityGraph.getNodes().size() != 1 ? "s" : "") + " in the 'reachabilityGraph'");
}
}

if (drawAlchemyJs) {
new GraphDumper(reachabilityGraph).dumpGraph("roadwork.html");
Expand Down Expand Up @@ -386,7 +451,7 @@ public void testRoadworkExample() {
toTest.add(new IsomorphismHandlerParallel());
toTest.add(new IsomorphismHandlerSorting());

boolean debug = false;
boolean debug = true;
int fromLevel = 2;
int toLevel = 2;
boolean drawSigmaJs = false;
Expand Down

0 comments on commit f8c3b4a

Please sign in to comment.