/// <summary> /// Generates a graph with stars of nodes as close to the desired properties as possible. /// Under k-bisimulation it is guaranteed that there are p / (s + 1) * (s + 1) partition blocks. /// It is also guaranteed that the number of nodes is at least n. /// Hint: choose p as multiple of s + 1, and choose n as multiple of p. /// </summary> /// <param name="n">Desired number of nodes.</param> /// <param name="p">Desired number of partition blocks.</param> /// <param name="s">Degree of each star.</param> /// <returns></returns> public static MultiDirectedGraph <int, int> GenerateStars(int n, int p, int s) { // Create empty graph and label provider var graph = new MultiDirectedGraph <int, int>(); graph.Name = "Synthetic_" + s + "_Stars_" + p + "_" + n; // Node counter for uniqueness int node = 0; // Number of stars of size s + 1 to satisfy p partition requirement int c = p / (s + 1); // Keep adding stars while we lack nodes while (graph.NumNodes < n) { // Add c stars for (int i = 0; i < c; i++) { // Add center node graph.AddNode(node, 0); node += 1; // Add child nodes for (int j = 1; j <= s; j++) { graph.AddNode(node, i * (s + 1) + j); graph.AddEdge(node - j, node, 0); node += 1; } } } return(graph); }
/// <summary> /// Generates a graph with chains of nodes as close to the desired properties as possible. /// Under k-bisimulation it is guaranteed that there are p / (k + 1) * (k + 1) partition blocks. /// It is also guaranteed that the number of nodes is at least n. /// Hint: choose p as multiple of k + 1, and choose n as multiple of p. /// </summary> /// <param name="n">Desired number of nodes.</param> /// <param name="p">Desired number of partition blocks.</param> /// <param name="k">Depth parameter for bisimulation.</param> /// <returns></returns> public static MultiDirectedGraph <int, int> GenerateChains(int n, int p, int k) { // Create empty graph and label provider var graph = new MultiDirectedGraph <int, int>(); graph.Name = "Synthetic_Chains_" + k + "_" + p + "_" + n; // Node counter for uniqueness int node = 0; // Number of chains of length k + 1 to satisfy p partition requirement int c = p / (k + 1); // Keep adding chains while we lack nodes while (graph.NumNodes < n) { // Add c chains for (int i = 0; i < c; i++) { // Add initial node graph.AddNode(node, i); node += 1; // Add subsequent nodes with edges for (int j = 1; j <= k; j++) { graph.AddNode(node, i); graph.AddEdge(node - 1, node, i); node += 1; } } } return(graph); }
public static void Foo() { // Process log generator petrinet dot conversion var inPath = Program.Input("In path?", string.Copy); var outPath = Program.Input("Out path?", string.Copy); var graph = new MultiDirectedGraph <int, int>(); var nodeMap = new Dictionary <string, int>(); int counter = 0; var lines = File.ReadAllLines(inPath); foreach (var line in lines) { var tokens = line.Split(new char[] { ' ', '\t', '[', ']' }, StringSplitOptions.RemoveEmptyEntries); if (tokens.Length <= 1) { continue; } if (tokens[0][0] == 't') { if (!nodeMap.ContainsKey(tokens[0])) { nodeMap.Add(tokens[0], counter++); graph.AddNode(nodeMap[tokens[0]], 0); } } if (tokens[0][0] == 'p') { if (!nodeMap.ContainsKey(tokens[0])) { nodeMap.Add(tokens[0], counter++); graph.AddNode(nodeMap[tokens[0]], 1); } } if (tokens[1] == "->") { var u = nodeMap[tokens[0]]; var v = nodeMap[tokens[2]]; graph.AddEdge(u, v); } } GraphConverter.SaveToGraphML(graph, outPath); }
/// <summary> /// /// </summary> /// <param name="n"></param> /// <param name="p"></param> /// <returns></returns> public static MultiDirectedGraph <int, int> ErdosRenyi(int n, double p) { // Create empty graph and label provider var graph = new MultiDirectedGraph <int, int>(); graph.Name = "Synthetic_ErdosRenyi_" + n + "_" + p; // Add n nodes for (int i = 0; i < n; i++) { graph.AddNode(i, 0); } // Add edges with probability p for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (StaticRandom.NextDouble() <= p) { graph.AddEdge(i, j, 0); } } } return(graph); }
public static void Bar() { // Stanford graphs conversion var inPath = Program.Input("In path?", string.Copy); var outPath = Program.Input("Out path?", string.Copy); var graph = new MultiDirectedGraph <int, int>(); var lines = File.ReadAllLines(inPath); foreach (var line in lines) { var tokens = line.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); if (tokens[0] != "#") { int source = int.Parse(tokens[0]); int target = int.Parse(tokens[1]); if (!graph.HasNode(source)) { graph.AddNode(source); } if (!graph.HasNode(target)) { graph.AddNode(target); } if (!graph.HasEdge(source, target) && !graph.HasEdge(target, source)) { graph.AddEdge(source, target); } } } GraphConverter.SaveToGraphML(graph, outPath); }
public static void Bla() { // Merge graphs in a folder with a single source node and sink node string path = Program.Input("Please enter the path to folder with graph files", string.Copy); var outPath = Program.Input("Out path?", string.Copy); string[] filePaths = Directory.GetFiles(path, "*.xml"); var finalGraph = new MultiDirectedGraph<int, int>(); var finalSources = new List<int>(); var finalSinks = new List<int>(); int count = 0; foreach (var filePath in filePaths) { var graph = GraphLoader.LoadGraphML(filePath, int.Parse, int.Parse); foreach (var node in graph.Nodes) { finalGraph.AddNode(node + count, graph.NodeLabel(node)); } foreach (var edge in graph.Edges) { var s = graph.Source(edge); var t = graph.Target(edge); var l = graph.EdgeLabel(edge); finalGraph.AddEdge(s + count, t + count, l); } var sources = graph.Nodes.Where(u => graph.In(u).Count() == 0); var sinks = graph.Nodes.Where(u => graph.Out(u).Count() == 0); if (sources.Count() != 1 || sinks.Count() != 1) { throw new Exception(); } finalSources.Add(sources.First() + count); finalSinks.Add(sinks.First() + count); count += graph.NumNodes; } finalGraph.MergeNodes(finalSources); finalGraph.MergeNodes(finalSinks); GraphConverter.SaveToGraphML(finalGraph, outPath); }
public static void Bla() { // Merge graphs in a folder with a single source node and sink node string path = Program.Input("Please enter the path to folder with graph files", string.Copy); var outPath = Program.Input("Out path?", string.Copy); string[] filePaths = Directory.GetFiles(path, "*.xml"); var finalGraph = new MultiDirectedGraph <int, int>(); var finalSources = new List <int>(); var finalSinks = new List <int>(); int count = 0; foreach (var filePath in filePaths) { var graph = GraphLoader.LoadGraphML(filePath, int.Parse, int.Parse); foreach (var node in graph.Nodes) { finalGraph.AddNode(node + count, graph.NodeLabel(node)); } foreach (var edge in graph.Edges) { var s = graph.Source(edge); var t = graph.Target(edge); var l = graph.EdgeLabel(edge); finalGraph.AddEdge(s + count, t + count, l); } var sources = graph.Nodes.Where(u => graph.In(u).Count() == 0); var sinks = graph.Nodes.Where(u => graph.Out(u).Count() == 0); if (sources.Count() != 1 || sinks.Count() != 1) { throw new Exception(); } finalSources.Add(sources.First() + count); finalSinks.Add(sinks.First() + count); count += graph.NumNodes; } finalGraph.MergeNodes(finalSources); finalGraph.MergeNodes(finalSinks); GraphConverter.SaveToGraphML(finalGraph, outPath); }
public static void Bar() { // Stanford graphs conversion var inPath = Program.Input("In path?", string.Copy); var outPath = Program.Input("Out path?", string.Copy); var graph = new MultiDirectedGraph<int, int>(); var lines = File.ReadAllLines(inPath); foreach (var line in lines) { var tokens = line.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); if (tokens[0] != "#") { int source = int.Parse(tokens[0]); int target = int.Parse(tokens[1]); if (!graph.HasNode(source)) { graph.AddNode(source); } if (!graph.HasNode(target)) { graph.AddNode(target); } if (!graph.HasEdge(source, target) && !graph.HasEdge(target, source)) { graph.AddEdge(source, target); } } } GraphConverter.SaveToGraphML(graph, outPath); }
/// <summary> /// /// </summary> /// <param name="n"></param> /// <param name="p"></param> /// <param name="k"></param> /// <returns></returns> public static MultiDirectedGraph <int, int> GenerateTrees(int n, int p, int k) { // Create empty graph and label provider var graph = new MultiDirectedGraph <int, int>(); graph.Name = "Synthetic_Trees_" + k + "_" + p + "_" + n; // Find the degree necessary for a k-depth tree to achieve at least p number of nodes int degree = 1; while ((int)Math.Pow(degree, k + 1) - 1 < p) { degree += 1; } int branchSize = (int)Math.Pow(degree, k + 1) - 1; // Keep adding trees while we lack nodes for (int treeOffset = 0; treeOffset < n; treeOffset += branchSize) { int partitionSize = k + 1; // Add nodes for (int i = 0; i < branchSize; i++) { graph.AddNode(treeOffset + i); if (partitionSize < p && Math.Log(i + 1, degree) % 1 != 0) { graph.SetNodeLabel(treeOffset + i, i); partitionSize += 1; } else { graph.SetNodeLabel(treeOffset + i, branchSize); } } // Add edges for (int i = 0; i < branchSize / degree; i++) { for (int j = 1; j <= degree; j++) { graph.AddEdge(treeOffset + i, treeOffset + degree * i + j, 0); } } } return(graph); }
/// <summary> /// Collapse a graph which contains parallel edges into a graph without parallel edges. /// Node labels are all set to 1. /// Edge labels indicate how many parallel edges there originally were from the source to the target. /// </summary> /// <typeparam name="TNode">Type of node.</typeparam> /// <typeparam name="TLabel">Type of label.</typeparam> /// <param name="graph">Graph to collapse.</param> /// <returns>A copy of the original graph with node labels set to 1 and edge labels indicating how often that edge occurred in the original graph.</returns> public static MultiDirectedGraph <TNode, int> Collapse <TNode, TLabel>(MultiDirectedGraph <TNode, TLabel> graph) { var edgeWeights = new Dictionary <Tuple <TNode, TNode>, int>(); var transformed = new MultiDirectedGraph <TNode, int>(); // Copy nodes foreach (var u in graph.Nodes) { transformed.AddNode(u, 1); } // Count edges from u to v foreach (var u in graph.Nodes) { foreach (var eo in graph.Out(u)) { var v = graph.Target(eo); var t = Tuple.Create(u, v); if (!edgeWeights.ContainsKey(t)) { edgeWeights.Add(t, 0); } edgeWeights[t] += 1; } } // Add weighted edges to the transformed graph foreach (var kvp in edgeWeights) { var u = kvp.Key.Item1; var v = kvp.Key.Item2; var w = kvp.Value; transformed.AddEdge(u, v, w); } return(transformed); }
/// <summary> /// Computes a reduced graph under some bisimulation equivalence relation. /// The input graph must be partitioned by the partitioner modulo said equivalence relation. /// </summary> /// <typeparam name="TNode">Node type.</typeparam> /// <typeparam name="TLabel">Label type.</typeparam> /// <param name="graph">Input graph.</param> /// <param name="labels">Labels of graph.</param> /// <param name="partitioner">Function which partitions the graph modulo some bisimulation equivalence relation.</param> /// <returns>A reduced graph where each partition block is a node and edges are reconstructued such that bisimulation equivalence is maintained.</returns> public static MultiDirectedGraph <int, TLabel> ReducedGraph <TNode, TLabel>(MultiDirectedGraph <TNode, TLabel> graph, Func <IDictionary <TNode, int> > partitioner) { var reduced = new MultiDirectedGraph <int, TLabel>(); var partition = partitioner(); var inverted = Utils.Invert(partition); // Add a node for each partition block foreach (var kvp in inverted) { var block = kvp.Key; var nodes = kvp.Value.ToArray(); // var someNode = Utils.Shuffled(nodes).First(); var someNode = nodes.First(); reduced.AddNode(block, graph.NodeLabel(someNode)); } // Add the edge going from each partition block to another foreach (var kvp in inverted) { var block = kvp.Key; var nodes = kvp.Value.ToArray(); // var someSource = Utils.Shuffled(nodes).First(); var someSource = nodes.First(); foreach (var eo in graph.Out(someSource)) { var someTarget = graph.Target(eo); var label = graph.EdgeLabel(eo); if (!reduced.HasEdge(block, partition[someTarget], label)) { reduced.AddEdge(block, partition[someTarget], label); } } } return(reduced); }
/// <summary> /// Coarsen a graph based on random matching. /// When two nodes collapse, their weights are added up. /// The weights on the edges of two collapsed nodes are added up as well. /// </summary> /// <param name="graph">Graph to coarsen.</param> /// <param name="e">Small value which stops the coarsening if the coarser graph is too much like the finer graph.</param> /// <param name="M">Minimum number of nodes the coarse graph should have.</param> /// <param name="maxWeight">Maximum weight a single collapsed node is allowed to have.</param> /// <returns>The coarsened graph along with a list of projections used to obtain the coarsened graph.</returns> public static Tuple <MultiDirectedGraph <int, int>, List <Dictionary <int, int> > > Coarsen(MultiDirectedGraph <int, int> graph, double e, int M, int maxWeight) { // Coarsen the graph incrementally var projections = new List <Dictionary <int, int> >(); var fineGraph = graph; var coarseGraph = graph; do { fineGraph = coarseGraph; coarseGraph = new MultiDirectedGraph <int, int>(); var partition = RandomMatching(fineGraph, maxWeight); var inverted = Utils.Invert(partition); var edgeWeights = new Dictionary <Tuple <int, int>, int>(); projections.Add(partition); // Add node for each block foreach (var match in inverted) { int block = match.Key; var nodes = match.Value; int w = 0; foreach (var u in nodes) { w += fineGraph.NodeLabel(u); } coarseGraph.AddNode(block, w); } // Sum edge weights, removing parallel edges foreach (var match in inverted) { int sourceBlock = match.Key; var nodes = match.Value; foreach (var u in nodes) { foreach (var eo in fineGraph.Out(u)) { var v = fineGraph.Target(eo); var targetBlock = partition[v]; int w = fineGraph.EdgeLabel(eo); var t = Tuple.Create(sourceBlock, targetBlock); if (!edgeWeights.ContainsKey(t)) { edgeWeights.Add(t, 0); } edgeWeights[t] += w; } } } // Add edges foreach (var kvp in edgeWeights) { var u = kvp.Key.Item1; var v = kvp.Key.Item2; var w = kvp.Value; coarseGraph.AddEdge(u, v, w); } } while ((double)fineGraph.NumNodes / (double)coarseGraph.NumNodes > 1.0 + e && coarseGraph.NumNodes > M); return(Tuple.Create(coarseGraph, projections)); }
/// <summary> /// Measures the weighted k-bisimulation partition equivalence between two graphs. /// </summary> /// <typeparam name="TNode"></typeparam> /// <typeparam name="TLabel"></typeparam> /// <param name="G1"></param> /// <param name="G2"></param> /// <param name="L1"></param> /// <param name="L2"></param> /// <param name="k">A tuple indicating how many nodes of G1 and G2 are in partition blocks that are shared between G1 and G2.</param> /// <returns></returns> public static Tuple <int, int> WeightedBisimulationEquivalence <TNode, TLabel>(MultiDirectedGraph <TNode, TLabel> G1, MultiDirectedGraph <TNode, TLabel> G2, int k) { // Create new empty graph and label provider var G = new MultiDirectedGraph <Tuple <int, TNode>, TLabel>(); // Add nodes of G1 foreach (var node in G1.Nodes.Select(node => Tuple.Create(1, node))) { G.AddNode(node, G1.NodeLabel(node.Item2)); } // Add nodes of G2 foreach (var node in G2.Nodes.Select(node => Tuple.Create(2, node))) { G.AddNode(node, G2.NodeLabel(node.Item2)); } // Add edges of G1 foreach (var edge in G1.Edges) { var s = Tuple.Create(1, G1.Source(edge)); var t = Tuple.Create(1, G1.Target(edge)); G.AddEdge(s, t, G1.EdgeLabel(edge)); } // Add edges of G2 foreach (var edge in G2.Edges) { var s = Tuple.Create(2, G2.Source(edge)); var t = Tuple.Create(2, G2.Target(edge)); G.AddEdge(s, t, G2.EdgeLabel(edge)); } // Perform bisimulation reduction var partitioner = new GraphPartitioner <Tuple <int, TNode>, TLabel>(G); var partition = partitioner.BoundedExactBisimulationReduction(k); // Partition blocks of G1 and G2 HashSet <int> P1 = new HashSet <int>(); HashSet <int> P2 = new HashSet <int>(); foreach (var node in G.Nodes) { int block = partition[node]; switch (node.Item1) { case 1: if (!P1.Contains(block)) { P1.Add(block); } break; case 2: if (!P2.Contains(block)) { P2.Add(block); } break; } } int s1 = 0; int s2 = 0; foreach (var node in G.Nodes) { if (P1.Contains(partition[node]) && P2.Contains(partition[node])) { switch (node.Item1) { case 1: s1 += 1; break; case 2: s2 += 1; break; } } } return(Tuple.Create(s1, s2)); }
public static void Foo() { // Process log generator petrinet dot conversion var inPath = Program.Input("In path?", string.Copy); var outPath = Program.Input("Out path?", string.Copy); var graph = new MultiDirectedGraph<int, int>(); var nodeMap = new Dictionary<string, int>(); int counter = 0; var lines = File.ReadAllLines(inPath); foreach (var line in lines) { var tokens = line.Split(new char[] { ' ', '\t', '[', ']' }, StringSplitOptions.RemoveEmptyEntries); if (tokens.Length <= 1) { continue; } if (tokens[0][0] == 't') { if (!nodeMap.ContainsKey(tokens[0])) { nodeMap.Add(tokens[0], counter++); graph.AddNode(nodeMap[tokens[0]], 0); } } if (tokens[0][0] == 'p') { if (!nodeMap.ContainsKey(tokens[0])) { nodeMap.Add(tokens[0], counter++); graph.AddNode(nodeMap[tokens[0]], 1); } } if (tokens[1] == "->") { var u = nodeMap[tokens[0]]; var v = nodeMap[tokens[2]]; graph.AddEdge(u, v); } } GraphConverter.SaveToGraphML(graph, outPath); }
/// <summary> /// Load a GraphML document. /// </summary> /// <typeparam name="TNode">Type of the nodes.</typeparam> /// <typeparam name="TLabel">Type of the labels.</typeparam> /// <param name="path">Path to the GraphML file.</param> /// <param name="nodeParser">Function which converts a string to TNode.</param> /// <param name="labelParser">Function which converts a string to TLabel.</param> /// <returns>The graph and label provider representing the loaded GraphML document.</returns> public static MultiDirectedGraph <TNode, TLabel> LoadGraphML <TNode, TLabel>(string path, Func <string, TNode> nodeParser, Func <string, TLabel> labelParser) { // Read GraphML document XmlReaderSettings settings = new XmlReaderSettings(); settings.ValidationType = ValidationType.Schema; settings.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation; using (XmlReader reader = XmlReader.Create(path, settings)) { var document = XDocument.Load(reader); var keys = document.Root.Elements().Where(element => element.Name.LocalName == "key"); var graphs = document.Root.Elements().Where(element => element.Name.LocalName == "graph"); var xmlGraph = graphs.First(); var defaultLabel = default(TLabel); // Go through keys foreach (var key in keys) { // Id of the key var id = (string)key.Attribute("id"); switch (id) { case "label": defaultLabel = labelParser(key.Elements().Where(element => element.Name.LocalName == "default").First().Value); break; } } // Read graph attributes string graphName = (string)xmlGraph.Attribute("id"); bool isDirected = (string)xmlGraph.Attribute("edgedefault") == "directed"; // Construct empty graph and label provider // TODO: undirected graph if isDirected is false (for now only use directed graphs) var graph = new MultiDirectedGraph <TNode, TLabel>(); graph.Name = graphName; var nodes = xmlGraph.Elements().Where(element => element.Name.LocalName == "node"); var edges = xmlGraph.Elements().Where(element => element.Name.LocalName == "edge"); // Go through each node foreach (var xmlNode in nodes) { string id = (string)xmlNode.Attribute("id"); var node = nodeParser(id); var label = defaultLabel; var data = xmlNode.Elements().Where(element => element.Name.LocalName == "data"); foreach (var datum in data) { string key = (string)datum.Attribute("key"); switch (key) { case "label": label = labelParser(datum.Value); break; } } graph.AddNode(node, label); } // Go through each edge foreach (var xmlEdge in edges) { string xmlSource = (string)xmlEdge.Attribute("source"); string xmlTarget = (string)xmlEdge.Attribute("target"); var source = nodeParser(xmlSource); var target = nodeParser(xmlTarget); var label = defaultLabel; var data = xmlEdge.Elements().Where(element => element.Name.LocalName == "data"); foreach (var datum in data) { string key = (string)datum.Attribute("key"); switch (key) { case "label": label = labelParser(datum.Value); break; } } graph.AddEdge(source, target, label); // TODO: replace temporary fix for undirected graphs if (!isDirected) { graph.AddEdge(target, source, label); } } return(graph); } }
/// <summary> /// /// </summary> /// <param name="D"></param> /// <param name="b"></param> /// <returns></returns> public static MultiDirectedGraph <int, int> GenerateNiceDAG(int D, int b) { // Create empty graph and label provider var graph = new MultiDirectedGraph <int, int>(); graph.Name = "Synthetic_DAG_" + D + "_" + b; // Define parent function Func <int, int> parent = node => { // Assume node > 0 (not the root node) return(graph.In(node).First()); }; // Define level function Func <int, int> level = Utils.Y <int, int>(fix => node => { if (node == 0) { // Root node return(0); } else { return(1 + fix(parent(node))); } }); // Create initial tree int counter = 0; graph.AddNode(counter, 0); counter += 1; while (graph.Nodes.Select(node => level(node)).Max() < D) { int max = graph.Nodes.Select(node => level(node)).Max(); var lowest = graph.Nodes.Where(node => level(node) == max).ToArray(); foreach (var node in lowest) { int k = StaticRandom.Next(b + 1); for (int i = 0; i < k; i++) { graph.AddNode(counter, 0); graph.AddEdge(node, counter, i); // graph.AddEdge(node, counter, 0); counter += 1; } } } // Transform tree to DAG with nicer partition block distribution var copy = graph.Clone(); var partitioner = new GraphPartitioner <int, int>(graph); var partition = partitioner.BoundedExactBisimulationReduction(D); var partitionInverted = Utils.Invert(partition); var blocks = partition.Values.Distinct(); var blockSizes = Utils.Distribution(partition.Values); int blockMax = blockSizes.Values.Max(); foreach (var block in blocks) { int size = blockSizes[block]; var nodes = new List <int>(partitionInverted[block]); for (int i = size; i < blockMax; i++) { // Replicate a random node in this partition block int k = StaticRandom.Next(size); var v = nodes[k]; // Replicate the node graph.AddNode(counter, graph.NodeLabel(v)); // Replicate its incoming edges foreach (var ei in copy.In(v)) { var u = graph.Source(ei); graph.AddEdge(u, counter, graph.EdgeLabel(ei)); } // Replicate its outgoing edges foreach (var eo in copy.Out(v)) { var w = graph.Target(eo); graph.AddEdge(counter, w, graph.EdgeLabel(eo)); } counter += 1; } } return(graph); }