From 01a4b84edf9c8562869d00f47b0e04d190c9b3ab Mon Sep 17 00:00:00 2001 From: miettinen Date: Fri, 22 Nov 2013 14:07:45 +0000 Subject: [PATCH] Added algorithm to find loops in graphs. Added a utility class to use that for Sysdyn Resources. (refs #3012) git-svn-id: https://www.simantics.org/svn/simantics/sysdyn/trunk@28362 ac1ea38d-2e2b-0410-8846-a27921b304fc --- .../ui/elements/SysdynElementFactory.java | 5 +- .../sysdyn/ui/utils/ConfigurationUtils.java | 146 +++++++++ org.simantics.sysdyn/META-INF/MANIFEST.MF | 1 + .../elementaryCycles/AdjacencyList.java | 46 +++ .../ElementaryCyclesSearch.java | 167 +++++++++++ .../sysdyn/elementaryCycles/SCCResult.java | 34 +++ .../StrongConnectedComponents.java | 280 ++++++++++++++++++ .../sysdyn/elementaryCycles/TestCycles.java | 68 +++++ .../sysdyn/elementaryCycles/license.txt | 11 + 9 files changed, 757 insertions(+), 1 deletion(-) create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ConfigurationUtils.java create mode 100644 org.simantics.sysdyn/src/org/simantics/sysdyn/elementaryCycles/AdjacencyList.java create mode 100644 org.simantics.sysdyn/src/org/simantics/sysdyn/elementaryCycles/ElementaryCyclesSearch.java create mode 100644 org.simantics.sysdyn/src/org/simantics/sysdyn/elementaryCycles/SCCResult.java create mode 100644 org.simantics.sysdyn/src/org/simantics/sysdyn/elementaryCycles/StrongConnectedComponents.java create mode 100644 org.simantics.sysdyn/src/org/simantics/sysdyn/elementaryCycles/TestCycles.java create mode 100644 org.simantics.sysdyn/src/org/simantics/sysdyn/elementaryCycles/license.txt diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements/SysdynElementFactory.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements/SysdynElementFactory.java index 497f3de0..6f7c9043 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements/SysdynElementFactory.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements/SysdynElementFactory.java @@ -82,7 +82,10 @@ public abstract class SysdynElementFactory extends SyncElementFactory { @Override public void load(ReadGraph graph, final ICanvasContext canvas, final IDiagram diagram, final Resource element, final IElement e) throws DatabaseException { ElementUtils.setText(e, getText(graph, element)); - + // Just testing loop finding. + //ModelingResources mr = ModelingResources.getInstance(graph); + //Resource component = graph.getPossibleObject(element, mr.ElementToComponent); + //ConfigurationUtils.getLoops(graph, component); getVisualProperties(graph, element, e); AffineTransform at = DiagramGraphUtil.getAffineTransform(graph, element); diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ConfigurationUtils.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ConfigurationUtils.java new file mode 100644 index 00000000..28db93d2 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ConfigurationUtils.java @@ -0,0 +1,146 @@ +package org.simantics.sysdyn.ui.utils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; + +import org.simantics.databoard.Bindings; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.exception.ServiceException; +import org.simantics.db.request.Read; +import org.simantics.layer0.Layer0; +import org.simantics.sysdyn.SysdynResource; +import org.simantics.sysdyn.elementaryCycles.ElementaryCyclesSearch; +import org.simantics.ui.SimanticsUI; + +/** + * Utils for configurations + * @author Tuomas Miettinen + * + */ +public class ConfigurationUtils { + + private static class ElementaryLoopItem { + public int mapping; + ArrayList dependencies; + + public ElementaryLoopItem(int mapping, ArrayList dependencies) { + this.mapping = mapping; + this.dependencies = dependencies; + } + } + + public static List getLoops(final Resource r) { + List loops = Collections.emptyList(); + try { + loops = SimanticsUI.getSession().syncRequest(new Read>() { + + @Override + public List perform(ReadGraph graph) + throws DatabaseException { + return getLoops(graph, r); + } + }); + } catch (DatabaseException e) { + e.printStackTrace(); + } + return loops; + } + + @SuppressWarnings("rawtypes") + public static List getLoops(ReadGraph g, Resource r) throws DatabaseException, ServiceException { + Layer0 l0 = Layer0.getInstance(g); + SysdynResource sr = SysdynResource.getInstance(g); + List loops = Collections.emptyList(); + Resource configuration = g.getPossibleObject(r, l0.PartOf); + if (configuration == null) + return loops; + + loops = new ArrayList(); + + Collection variables = g.getObjects(configuration, l0.ConsistsOf); + ArrayList shadows = new ArrayList(); + + // Leave only independent variables and shadows (and modules) + Iterator it = variables.iterator(); + while (it.hasNext()) { + Resource v = it.next(); + if (g.isInstanceOf(v, sr.IndependentVariable)) { + } else if (g.isInstanceOf(v, sr.Shadow)) { + shadows.add(v); + it.remove(); + /*} else if (|| g.isInstanceOf(v, sr.Module)) { + */ + } else { + it.remove(); + } + } + + HashMap elementaryLoopItems = new HashMap(); + + int variableCount = variables.size(); + Resource nodes[] = new Resource[variableCount]; + boolean adjMatrix[][] = new boolean[variableCount][variableCount]; + + // Add independent variables to map and array + int k = 0; + for (Resource variable : variables) { + ArrayList dependingVariables = new ArrayList(); + Collection dependencies = g.getObjects(variable, sr.Variable_isTailOf); + for (Resource dependency : dependencies) { + Resource head = g.getPossibleObject(dependency, sr.Variable_HasHead); + if (!g.isInstanceOf(head, sr.Module)) { + dependingVariables.add(head); + } + } + elementaryLoopItems.put(variable, new ElementaryLoopItem(k, dependingVariables)); + nodes[k++] = variable; + } + + // Add dependencies of shadow variables for their original variables + for (Resource shadow : shadows) { + Resource original = g.getPossibleObject(shadow, sr.Shadow_original); + ArrayList dependingVariables = new ArrayList(); + Collection dependencies = g.getObjects(shadow, sr.Variable_isTailOf); + for (Resource dependency : dependencies) { + Resource head = g.getPossibleObject(dependency, sr.Variable_HasHead); + if (!g.isInstanceOf(head, sr.Module)) { + dependingVariables.add(head); + } + } + elementaryLoopItems.get(original).dependencies.addAll(dependingVariables); + } + + // Fill the adjacent matrix + for (int j = 0; j < nodes.length; ++j) { + ArrayList dependingVariables = elementaryLoopItems.get(nodes[j]).dependencies; + for (Resource v : dependingVariables) { + adjMatrix[j][elementaryLoopItems.get(v).mapping] = true; + } + } + + // Debug print the result + ElementaryCyclesSearch ecs = new ElementaryCyclesSearch(adjMatrix, nodes); + List cycles = ecs.getElementaryCycles(); + for (int i = 0; i < cycles.size(); i++) { + List cycle = (List) cycles.get(i); + for (int j = 0; j < cycle.size(); j++) { + Resource node = (Resource) cycle.get(j); + if (j < cycle.size() - 1) { + System.out.print(g.getPossibleRelatedValue(node, l0.HasName, Bindings.STRING) + " -> "); + } else { + System.out.print(g.getPossibleRelatedValue(node, l0.HasName, Bindings.STRING)); + } + } + System.out.print("\n"); + } + + return loops; + } + +} diff --git a/org.simantics.sysdyn/META-INF/MANIFEST.MF b/org.simantics.sysdyn/META-INF/MANIFEST.MF index 2fffaed1..5eaad95f 100644 --- a/org.simantics.sysdyn/META-INF/MANIFEST.MF +++ b/org.simantics.sysdyn/META-INF/MANIFEST.MF @@ -37,6 +37,7 @@ Require-Bundle: org.simantics.objmap;bundle-version="0.1.0", fi.semantum.sysdyn.solver;bundle-version="0.1.0" Export-Package: org.simantics.sysdyn, org.simantics.sysdyn.adapter, + org.simantics.sysdyn.elementaryCycles, org.simantics.sysdyn.expressionParser, org.simantics.sysdyn.manager, org.simantics.sysdyn.mdlImport, diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/elementaryCycles/AdjacencyList.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/elementaryCycles/AdjacencyList.java new file mode 100644 index 00000000..47c784c2 --- /dev/null +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/elementaryCycles/AdjacencyList.java @@ -0,0 +1,46 @@ +package org.simantics.sysdyn.elementaryCycles; + +import java.util.Vector; + + +/** + * Calculates the adjacency-list for a given adjacency-matrix. + * + * + * @author Frank Meyer, web@normalisiert.de + * @version 1.0, 26.08.2006 + * + */ +public class AdjacencyList { + /** + * Calculates a adjacency-list for a given array of an adjacency-matrix. + * + * @param adjacencyMatrix array with the adjacency-matrix that represents + * the graph + * @return int[][]-array of the adjacency-list of given nodes. The first + * dimension in the array represents the same node as in the given + * adjacency, the second dimension represents the indicies of those nodes, + * that are direct successornodes of the node. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static int[][] getAdjacencyList(boolean[][] adjacencyMatrix) { + int[][] list = new int[adjacencyMatrix.length][]; + + for (int i = 0; i < adjacencyMatrix.length; i++) { + Vector v = new Vector(); + for (int j = 0; j < adjacencyMatrix[i].length; j++) { + if (adjacencyMatrix[i][j]) { + v.add(new Integer(j)); + } + } + + list[i] = new int[v.size()]; + for (int j = 0; j < v.size(); j++) { + Integer in = (Integer) v.get(j); + list[i][j] = in.intValue(); + } + } + + return list; + } +} diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/elementaryCycles/ElementaryCyclesSearch.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/elementaryCycles/ElementaryCyclesSearch.java new file mode 100644 index 00000000..641682f4 --- /dev/null +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/elementaryCycles/ElementaryCyclesSearch.java @@ -0,0 +1,167 @@ +package org.simantics.sysdyn.elementaryCycles; + +import java.util.List; +import java.util.Vector; + + + +/** + * Searchs all elementary cycles in a given directed graph. The implementation + * is independent from the concrete objects that represent the graphnodes, it + * just needs an array of the objects representing the nodes the graph + * and an adjacency-matrix of type boolean, representing the edges of the + * graph. It then calculates based on the adjacency-matrix the elementary + * cycles and returns a list, which contains lists itself with the objects of the + * concrete graphnodes-implementation. Each of these lists represents an + * elementary cycle.

+ * + * The implementation uses the algorithm of Donald B. Johnson for the search of + * the elementary cycles. For a description of the algorithm see:
+ * Donald B. Johnson: Finding All the Elementary Circuits of a Directed Graph. + * SIAM Journal on Computing. Volumne 4, Nr. 1 (1975), pp. 77-84.

+ * + * The algorithm of Johnson is based on the search for strong connected + * components in a graph. For a description of this part see:
+ * Robert Tarjan: Depth-first search and linear graph algorithms. In: SIAM + * Journal on Computing. Volume 1, Nr. 2 (1972), pp. 146-160.
+ * + * @author Frank Meyer, web_at_normalisiert_dot_de + * @version 1.2, 22.03.2009 + * + */ +@SuppressWarnings("rawtypes") +public class ElementaryCyclesSearch { + /** List of cycles */ + private List cycles = null; + + /** Adjacency-list of graph */ + private int[][] adjList = null; + + /** Graphnodes */ + private Object[] graphNodes = null; + + /** Blocked nodes, used by the algorithm of Johnson */ + private boolean[] blocked = null; + + /** B-Lists, used by the algorithm of Johnson */ + private Vector[] B = null; + + /** Stack for nodes, used by the algorithm of Johnson */ + private Vector stack = null; + + /** + * Constructor. + * + * @param matrix adjacency-matrix of the graph + * @param graphNodes array of the graphnodes of the graph; this is used to + * build sets of the elementary cycles containing the objects of the original + * graph-representation + */ + public ElementaryCyclesSearch(boolean[][] matrix, Object[] graphNodes) { + this.graphNodes = graphNodes; + this.adjList = AdjacencyList.getAdjacencyList(matrix); + } + + /** + * Returns List::List::Object with the Lists of nodes of all elementary + * cycles in the graph. + * + * @return List::List::Object with the Lists of the elementary cycles. + */ + public List getElementaryCycles() { + this.cycles = new Vector(); + this.blocked = new boolean[this.adjList.length]; + this.B = new Vector[this.adjList.length]; + this.stack = new Vector(); + StrongConnectedComponents sccs = new StrongConnectedComponents(this.adjList); + int s = 0; + + while (true) { + SCCResult sccResult = sccs.getAdjacencyList(s); + if (sccResult != null && sccResult.getAdjList() != null) { + Vector[] scc = sccResult.getAdjList(); + s = sccResult.getLowestNodeId(); + for (int j = 0; j < scc.length; j++) { + if ((scc[j] != null) && (scc[j].size() > 0)) { + this.blocked[j] = false; + this.B[j] = new Vector(); + } + } + + this.findCycles(s, s, scc); + s++; + } else { + break; + } + } + + return this.cycles; + } + + /** + * Calculates the cycles containing a given node in a strongly connected + * component. The method calls itself recursivly. + * + * @param v + * @param s + * @param adjList adjacency-list with the subgraph of the strongly + * connected component s is part of. + * @return true, if cycle found; false otherwise + */ + @SuppressWarnings("unchecked") + private boolean findCycles(int v, int s, Vector[] adjList) { + boolean f = false; + this.stack.add(new Integer(v)); + this.blocked[v] = true; + + for (int i = 0; i < adjList[v].size(); i++) { + int w = ((Integer) adjList[v].get(i)).intValue(); + // found cycle + if (w == s) { + Vector cycle = new Vector(); + for (int j = 0; j < this.stack.size(); j++) { + int index = ((Integer) this.stack.get(j)).intValue(); + cycle.add(this.graphNodes[index]); + } + this.cycles.add(cycle); + f = true; + } else if (!this.blocked[w]) { + if (this.findCycles(w, s, adjList)) { + f = true; + } + } + } + + if (f) { + this.unblock(v); + } else { + for (int i = 0; i < adjList[v].size(); i++) { + int w = ((Integer) adjList[v].get(i)).intValue(); + if (!this.B[w].contains(new Integer(v))) { + this.B[w].add(new Integer(v)); + } + } + } + + this.stack.remove(new Integer(v)); + return f; + } + + /** + * Unblocks recursivly all blocked nodes, starting with a given node. + * + * @param node node to unblock + */ + private void unblock(int node) { + this.blocked[node] = false; + Vector Bnode = this.B[node]; + while (Bnode.size() > 0) { + Integer w = (Integer) Bnode.get(0); + Bnode.remove(0); + if (this.blocked[w.intValue()]) { + this.unblock(w.intValue()); + } + } + } +} + diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/elementaryCycles/SCCResult.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/elementaryCycles/SCCResult.java new file mode 100644 index 00000000..c8787cd4 --- /dev/null +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/elementaryCycles/SCCResult.java @@ -0,0 +1,34 @@ +package org.simantics.sysdyn.elementaryCycles; + +import java.util.HashSet; +import java.util.Set; +import java.util.Vector; + +@SuppressWarnings("rawtypes") +public class SCCResult { + private Set nodeIDsOfSCC = null; + private Vector[] adjList = null; + private int lowestNodeId = -1; + + @SuppressWarnings("unchecked") + public SCCResult(Vector[] adjList, int lowestNodeId) { + this.adjList = adjList; + this.lowestNodeId = lowestNodeId; + this.nodeIDsOfSCC = new HashSet(); + if (this.adjList != null) { + for (int i = this.lowestNodeId; i < this.adjList.length; i++) { + if (this.adjList[i].size() > 0) { + this.nodeIDsOfSCC.add(new Integer(i)); + } + } + } + } + + public Vector[] getAdjList() { + return adjList; + } + + public int getLowestNodeId() { + return lowestNodeId; + } +} diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/elementaryCycles/StrongConnectedComponents.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/elementaryCycles/StrongConnectedComponents.java new file mode 100644 index 00000000..8c966d31 --- /dev/null +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/elementaryCycles/StrongConnectedComponents.java @@ -0,0 +1,280 @@ +package org.simantics.sysdyn.elementaryCycles; + + +import java.util.Vector; + +/** + * This is a helpclass for the search of all elementary cycles in a graph + * with the algorithm of Johnson. For this it searches for strong connected + * components, using the algorithm of Tarjan. The constructor gets an + * adjacency-list of a graph. Based on this graph, it gets a nodenumber s, + * for which it calculates the subgraph, containing all nodes + * {s, s + 1, ..., n}, where n is the highest nodenumber in the original + * graph (e.g. it builds a subgraph with all nodes with higher or same + * nodenumbers like the given node s). It returns the strong connected + * component of this subgraph which contains the lowest nodenumber of all + * nodes in the subgraph.

+ * + * For a description of the algorithm for calculating the strong connected + * components see:
+ * Robert Tarjan: Depth-first search and linear graph algorithms. In: SIAM + * Journal on Computing. Volume 1, Nr. 2 (1972), pp. 146-160.
+ * For a description of the algorithm for searching all elementary cycles in + * a directed graph see:
+ * Donald B. Johnson: Finding All the Elementary Circuits of a Directed Graph. + * SIAM Journal on Computing. Volumne 4, Nr. 1 (1975), pp. 77-84.

+ * + * @author Frank Meyer, web_at_normalisiert_dot_de + * @version 1.1, 22.03.2009 + * + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +public class StrongConnectedComponents { + /** Adjacency-list of original graph */ + private int[][] adjListOriginal = null; + + /** Adjacency-list of currently viewed subgraph */ + private int[][] adjList = null; + + /** Helpattribute for finding scc's */ + private boolean[] visited = null; + + /** Helpattribute for finding scc's */ + private Vector stack = null; + + /** Helpattribute for finding scc's */ + private int[] lowlink = null; + + /** Helpattribute for finding scc's */ + private int[] number = null; + + /** Helpattribute for finding scc's */ + private int sccCounter = 0; + + /** Helpattribute for finding scc's */ + private Vector currentSCCs = null; + + /** + * Constructor. + * + * @param adjList adjacency-list of the graph + */ + public StrongConnectedComponents(int[][] adjList) { + this.adjListOriginal = adjList; + } + + /** + * This method returns the adjacency-structure of the strong connected + * component with the least vertex in a subgraph of the original graph + * induced by the nodes {s, s + 1, ..., n}, where s is a given node. Note + * that trivial strong connected components with just one node will not + * be returned. + * + * @param node node s + * @return SCCResult with adjacency-structure of the strong + * connected component; null, if no such component exists + */ + public SCCResult getAdjacencyList(int node) { + this.visited = new boolean[this.adjListOriginal.length]; + this.lowlink = new int[this.adjListOriginal.length]; + this.number = new int[this.adjListOriginal.length]; + this.visited = new boolean[this.adjListOriginal.length]; + this.stack = new Vector(); + this.currentSCCs = new Vector(); + + this.makeAdjListSubgraph(node); + + for (int i = node; i < this.adjListOriginal.length; i++) { + if (!this.visited[i]) { + this.getStrongConnectedComponents(i); + Vector nodes = this.getLowestIdComponent(); + if (nodes != null && !nodes.contains(new Integer(node)) && !nodes.contains(new Integer(node + 1))) { + return this.getAdjacencyList(node + 1); + } else { + Vector[] adjacencyList = this.getAdjList(nodes); + if (adjacencyList != null) { + for (int j = 0; j < this.adjListOriginal.length; j++) { + if (adjacencyList[j].size() > 0) { + return new SCCResult(adjacencyList, j); + } + } + } + } + } + } + + return null; + } + + /** + * Builds the adjacency-list for a subgraph containing just nodes + * >= a given index. + * + * @param node Node with lowest index in the subgraph + */ + private void makeAdjListSubgraph(int node) { + this.adjList = new int[this.adjListOriginal.length][0]; + + for (int i = node; i < this.adjList.length; i++) { + Vector successors = new Vector(); + for (int j = 0; j < this.adjListOriginal[i].length; j++) { + if (this.adjListOriginal[i][j] >= node) { + successors.add(new Integer(this.adjListOriginal[i][j])); + } + } + if (successors.size() > 0) { + this.adjList[i] = new int[successors.size()]; + for (int j = 0; j < successors.size(); j++) { + Integer succ = (Integer) successors.get(j); + this.adjList[i][j] = succ.intValue(); + } + } + } + } + + /** + * Calculates the strong connected component out of a set of scc's, that + * contains the node with the lowest index. + * + * @return Vector::Integer of the scc containing the lowest nodenumber + */ + private Vector getLowestIdComponent() { + int min = this.adjList.length; + Vector currScc = null; + + for (int i = 0; i < this.currentSCCs.size(); i++) { + Vector scc = (Vector) this.currentSCCs.get(i); + for (int j = 0; j < scc.size(); j++) { + Integer node = (Integer) scc.get(j); + if (node.intValue() < min) { + currScc = scc; + min = node.intValue(); + } + } + } + + return currScc; + } + + /** + * @return Vector[]::Integer representing the adjacency-structure of the + * strong connected component with least vertex in the currently viewed + * subgraph + */ + private Vector[] getAdjList(Vector nodes) { + Vector[] lowestIdAdjacencyList = null; + + if (nodes != null) { + lowestIdAdjacencyList = new Vector[this.adjList.length]; + for (int i = 0; i < lowestIdAdjacencyList.length; i++) { + lowestIdAdjacencyList[i] = new Vector(); + } + for (int i = 0; i < nodes.size(); i++) { + int node = ((Integer) nodes.get(i)).intValue(); + for (int j = 0; j < this.adjList[node].length; j++) { + int succ = this.adjList[node][j]; + if (nodes.contains(new Integer(succ))) { + lowestIdAdjacencyList[node].add(new Integer(succ)); + } + } + } + } + + return lowestIdAdjacencyList; + } + + /** + * Searchs for strong connected components reachable from a given node. + * + * @param root node to start from. + */ + private void getStrongConnectedComponents(int root) { + this.sccCounter++; + this.lowlink[root] = this.sccCounter; + this.number[root] = this.sccCounter; + this.visited[root] = true; + this.stack.add(new Integer(root)); + + for (int i = 0; i < this.adjList[root].length; i++) { + int w = this.adjList[root][i]; + if (!this.visited[w]) { + this.getStrongConnectedComponents(w); + this.lowlink[root] = Math.min(lowlink[root], lowlink[w]); + } else if (this.number[w] < this.number[root]) { + if (this.stack.contains(new Integer(w))) { + lowlink[root] = Math.min(this.lowlink[root], this.number[w]); + } + } + } + + // found scc + if ((lowlink[root] == number[root]) && (stack.size() > 0)) { + int next = -1; + Vector scc = new Vector(); + + do { + next = ((Integer) this.stack.get(stack.size() - 1)).intValue(); + this.stack.remove(stack.size() - 1); + scc.add(new Integer(next)); + } while (this.number[next] > this.number[root]); + + // simple scc's with just one node will not be added + if (scc.size() > 1) { + this.currentSCCs.add(scc); + } + } + } + + public static void main(String[] args) { + boolean[][] adjMatrix = new boolean[10][]; + + for (int i = 0; i < 10; i++) { + adjMatrix[i] = new boolean[10]; + } + + /*adjMatrix[0][1] = true; + adjMatrix[1][2] = true; + adjMatrix[2][0] = true; + adjMatrix[2][4] = true; + adjMatrix[1][3] = true; + adjMatrix[3][6] = true; + adjMatrix[6][5] = true; + adjMatrix[5][3] = true; + adjMatrix[6][7] = true; + adjMatrix[7][8] = true; + adjMatrix[7][9] = true; + adjMatrix[9][6] = true;*/ + + adjMatrix[0][1] = true; + adjMatrix[1][2] = true; + adjMatrix[2][0] = true; adjMatrix[2][6] = true; + adjMatrix[3][4] = true; + adjMatrix[4][5] = true; adjMatrix[4][6] = true; + adjMatrix[5][3] = true; + adjMatrix[6][7] = true; + adjMatrix[7][8] = true; + adjMatrix[8][6] = true; + + adjMatrix[6][1] = true; + + int[][] adjList = AdjacencyList.getAdjacencyList(adjMatrix); + StrongConnectedComponents scc = new StrongConnectedComponents(adjList); + for (int i = 0; i < adjList.length; i++) { + System.out.print("i: " + i + "\n"); + SCCResult r = scc.getAdjacencyList(i); + if (r != null) { + Vector[] al = scc.getAdjacencyList(i).getAdjList(); + for (int j = i; j < al.length; j++) { + if (al[j].size() > 0) { + System.out.print("j: " + j); + for (int k = 0; k < al[j].size(); k++) { + System.out.print(" _" + al[j].get(k).toString()); + } + System.out.print("\n"); + } + } + System.out.print("\n"); + } + } + } +} diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/elementaryCycles/TestCycles.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/elementaryCycles/TestCycles.java new file mode 100644 index 00000000..e846272e --- /dev/null +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/elementaryCycles/TestCycles.java @@ -0,0 +1,68 @@ +package org.simantics.sysdyn.elementaryCycles; + + +import java.util.List; + + +/** + * Testfile for elementary cycle search. + * + * @author Frank Meyer + * + */ +public class TestCycles { + + /** + * @param args + */ + @SuppressWarnings("rawtypes") + public static void main(String[] args) { + String nodes[] = new String[10]; + boolean adjMatrix[][] = new boolean[10][10]; + + for (int i = 0; i < 10; i++) { + nodes[i] = "Node " + i; + } + + adjMatrix[0][1] = true; + adjMatrix[1][2] = true; + adjMatrix[2][0] = true; + adjMatrix[2][4] = true; + adjMatrix[1][3] = true; + adjMatrix[3][6] = true; + adjMatrix[6][5] = true; + adjMatrix[5][3] = true; + adjMatrix[6][7] = true; + adjMatrix[7][8] = true; + adjMatrix[7][9] = true; + adjMatrix[9][6] = true; + /* + adjMatrix[0][1] = true; + adjMatrix[1][2] = true; + adjMatrix[2][0] = true; adjMatrix[2][6] = true; + adjMatrix[3][4] = true; + adjMatrix[4][5] = true; adjMatrix[4][6] = true; + adjMatrix[5][3] = true; + adjMatrix[6][7] = true; + adjMatrix[7][8] = true; + adjMatrix[8][6] = true; + + adjMatrix[6][1] = true; +*/ + ElementaryCyclesSearch ecs = new ElementaryCyclesSearch(adjMatrix, nodes); + List cycles = ecs.getElementaryCycles(); + for (int i = 0; i < cycles.size(); i++) { + List cycle = (List) cycles.get(i); + for (int j = 0; j < cycle.size(); j++) { + String node = (String) cycle.get(j); + if (j < cycle.size() - 1) { + System.out.print(node + " -> "); + } else { + System.out.print(node); + } + } + System.out.print("\n"); + } + } + +} diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/elementaryCycles/license.txt b/org.simantics.sysdyn/src/org/simantics/sysdyn/elementaryCycles/license.txt new file mode 100644 index 00000000..0ed76f9c --- /dev/null +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/elementaryCycles/license.txt @@ -0,0 +1,11 @@ +(BSD-2 license) + +Copyright (c) 2012, Frank Meyer +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file -- 2.47.1