/// <summary> /// Sample using a random walk with teleport technique. /// </summary> /// <typeparam name="TNode"></typeparam> /// <typeparam name="TLabel"></typeparam> /// <param name="graph"></param> /// <param name="n">Upper bound on the number of nodes to sample.</param> /// <param name="p">Probability to teleport to a random node after visiting a node.</param> /// <returns></returns> public static HashSet <TNode> RandomWalkTeleport <TNode, TLabel>(this MultiDirectedGraph <TNode, TLabel> graph, int n, double p) { var V = new HashSet <TNode>(); var nodes = graph.Nodes.ToArray(); n = Math.Min(n, graph.NumNodes); // Initial teleport var v = nodes[StaticRandom.Next(nodes.Length)]; while (n > 0) { if (!V.Contains(v)) { V.Add(v); n -= 1; } double t = StaticRandom.NextDouble(); var outNeighbors = graph.Out(v).Select(eo => graph.Target(eo)).ToArray(); if (t < p || outNeighbors.Length <= 0) { // Teleport v = nodes[StaticRandom.Next(nodes.Length)]; } else { v = outNeighbors[StaticRandom.Next(outNeighbors.Length)]; } } return(V); }
/// <summary> /// Compute distances to all target nodes from a single source node. /// </summary> /// <typeparam name="TNode"></typeparam> /// <param name="graph"></param> /// <param name="source"></param> /// <returns></returns> public static Dictionary <TNode, int> SingleSourceDistances <TNode, TLabel>(MultiDirectedGraph <TNode, TLabel> graph, TNode source) { var distance = new Dictionary <TNode, int>(); foreach (var v in graph.Nodes) { distance.Add(v, int.MaxValue); } var Q = new Queue <TNode>(); var V = new HashSet <TNode>(); Q.Enqueue(source); V.Add(source); distance[source] = 0; // Breadth-first walk while (Q.Count > 0) { var s = Q.Dequeue(); foreach (var eo in graph.Out(s)) { var t = graph.Target(eo); if (!V.Contains(t)) { Q.Enqueue(t); V.Add(t); distance[t] = distance[s] + 1; } } } return(distance); }
/// <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); }
/// <summary> /// Perform random matching partitioning. /// Input graph must be weighted on nodes. /// Nodes are only matched if they share an edge. /// </summary> /// <param name="graph">Graph with weighted nodes.</param> /// <param name="maxWeight">Maximum weight a matched pair is allowed to have.</param> /// <returns>A random matching of the nodes of the graph.</returns> public static Dictionary <int, int> RandomMatching(MultiDirectedGraph <int, int> graph, int maxWeight) { int counter = 0; var matches = new Dictionary <int, int>(); // Visit nodes randomly var nodes = graph.Nodes.ToArray(); nodes.Shuffle(); foreach (var u in nodes) { if (!matches.ContainsKey(u)) { // Node u in unmatched, match it with one of the unmatched neighbors var unmatchedNeighbors = graph.Neighbors(u).Where(v => !u.Equals(v) && !matches.ContainsKey(v) && graph.NodeLabel(u) + graph.NodeLabel(v) <= maxWeight).ToArray(); // Only match if such a neighbor exists if (unmatchedNeighbors.Length > 0) { var v = Utils.Shuffled(unmatchedNeighbors).First(); matches.Add(u, counter); matches.Add(v, counter); } else { matches.Add(u, counter); } counter += 1; } } return(matches); }
/// <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); }
/// <summary> /// Saves a graph to a file in the GraphML format. /// </summary> /// <typeparam name="TNode"></typeparam> /// <typeparam name="TLabel"></typeparam> /// <param name="graph"></param> /// <param name="labels"></param> public static void SaveToGraphML <TNode, TLabel>(MultiDirectedGraph <TNode, TLabel> graph, string path) { List <string> lines = new List <string>(); lines.Add("<?xml version=\"1.0\" encoding=\"utf-8\" ?>"); lines.Add("<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd\">"); lines.Add("\t<key id=\"label\" for=\"all\" attr.name=\"label\" attr.type=\"string\">"); lines.Add("\t\t<default>0</default>"); lines.Add("\t</key>"); lines.Add("\t<graph id=\"" + graph.Name + "\" edgedefault=\"" + (graph.IsDirected ? "directed" : "undirected") + "\">"); foreach (var node in graph.Nodes) { lines.Add("\t\t<node id=\"" + node + "\">"); lines.Add("\t\t\t<data key=\"label\">" + graph.NodeLabel(node) + "</data>"); lines.Add("\t\t</node>"); } foreach (var edge in graph.Edges) { var s = graph.Source(edge); var t = graph.Target(edge); lines.Add("\t\t<edge source=\"" + s + "\" target=\"" + t + "\">"); lines.Add("\t\t\t<data key=\"label\">" + graph.EdgeLabel(edge) + "</data>"); lines.Add("\t\t</edge>"); } lines.Add("\t</graph>"); lines.Add("</graphml>"); File.WriteAllLines(path, lines); }
/// <summary> /// Weighted coverage and correctness vs sample fraction. /// </summary> /// <param name="graph"></param> /// <param name="labels"></param> /// <param name="samplerName"></param> /// <param name="sampler"></param> /// <param name="k"></param> /// <returns></returns> public static Experiment WeightedBisimulationMetrics <TNode, TLabel>(MultiDirectedGraph <TNode, TLabel> graph, string samplerName, Func <double, MultiDirectedGraph <TNode, TLabel> > sampler, int k) { double[] percentages = new double[] { 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0 }; var experiment = new Experiment(3) { Labels = new string[] { "Sample fraction", "Weighted coverage", "Weighted correctness" }, Meta = new string[] { "Weighted", graph.Name, samplerName, k + "-bisimulation" }, F = i => { double p = percentages[Convert.ToInt32(i)] / 100.0; var sample = sampler(p); var counts = GraphMetrics.WeightedBisimulationEquivalence(graph, sample, k); // Weighted coverage double wr = (double)counts.Item1 / (double)graph.NumNodes; // Weighted correctness double wp = (double)counts.Item2 / (double)sample.NumNodes; return(new double[] { p, wr, wp }); }, }; experiment.Run(0, percentages.Length - 1, 1, 10); return(experiment); }
/// <summary> /// Coverage and correctness vs sample fraction. /// </summary> /// <param name="graph"></param> /// <param name="labels"></param> /// <param name="samplerName"></param> /// <param name="sampler"></param> /// <param name="k"></param> /// <returns></returns> public static Experiment StandardBisimulationMetrics <TNode, TLabel>(MultiDirectedGraph <TNode, TLabel> graph, string samplerName, Func <double, MultiDirectedGraph <TNode, TLabel> > sampler, int k) { double[] percentages = new double[] { 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0 }; var experiment = new Experiment(3) { Labels = new string[] { "Sample fraction", "Coverage", "Correctness" }, Meta = new string[] { "Standard", graph.Name, samplerName, k + "-bisimulation" }, F = i => { double p = percentages[Convert.ToInt32(i)] / 100.0; var sample = sampler(p); var counts = GraphMetrics.BisimulationEquivalence(graph, sample, k); // Graph block count double N1 = counts.Item1; // Sample block count double N2 = counts.Item2; // Shared block count double NS = N1 + N2 - (double)counts.Item3; double coverage = NS / N1; double correctness = NS / N2; return(new double[] { p, coverage, correctness }); }, }; experiment.Run(0, percentages.Length - 1, 1, 10); return(experiment); }
/// <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> /// Compute the partition block size distribution. /// </summary> /// <param name="graph"></param> /// <param name="labels"></param> /// <param name="k"></param> /// <returns></returns> public static Experiment PartitionBlockDistribution <TNode, TLabel>(MultiDirectedGraph <TNode, TLabel> graph) { var partitioner = new GraphPartitioner <TNode, TLabel>(graph); var partitions = partitioner.MultilevelExactBisimulationReduction(); int k_max = partitions.Count - 1; var sizes = Utils.Distribution(partitions[k_max].Values).Values.ToArray(); Array.Sort(sizes); Array.Reverse(sizes); var experiment = new Experiment(2) { Labels = new string[] { "Partition block", "Number of nodes" }, Meta = new string[] { "Blocks", graph.Name, }, F = i => { int index = Convert.ToInt32(i); int size = sizes[index]; return(new double[] { index + 1, size }); }, }; experiment.Run(0, sizes.Length - 1, 1, 1); return(experiment); }
/// <summary> /// Takes the first n nodes, ordered ascendingly by degree. /// </summary> /// <typeparam name="TNode"></typeparam> /// <param name="graph"></param> /// <param name="n">Upper bound on the number of nodes to sample.</param> /// <returns></returns> public static IEnumerable <TNode> LowDegreeFirst <TNode, TLabel>(this MultiDirectedGraph <TNode, TLabel> graph, int n) { // Sort nodes by their degree in ascending order TNode[] nodes = Utils.Shuffled(graph.Nodes.ToArray()).ToArray(); Array.Sort(nodes, (n1, n2) => graph.Degree(n1).CompareTo(graph.Degree(n2))); // Take lowest degree nodes first return(nodes.Take(Math.Min(n, graph.NumNodes))); }
/// <summary> /// Splits the nodes of a graph into P blocks by exploring. /// </summary> /// <typeparam name="TNode">Type of node.</typeparam> /// <typeparam name="TLabel">Type of label.</typeparam> /// <param name="graph">Graph to partition the nodes of.</param> /// <param name="P">Number of partition blocks.</param> public static Dictionary <TNode, int> ExploreSplit <TNode, TLabel>(MultiDirectedGraph <TNode, TLabel> graph, int P) { int n = graph.NumNodes; // Bucket size int B = (n + P - 1) / P; var V = new HashSet <TNode>(); var Q = new AiroQueue <TNode>(); var nodes = graph.Nodes.ToArray(); Utils.Shuffle(nodes); int seedIndex = 0; var partition = new Dictionary <TNode, int>(); Action <TNode> bucketize = node => { partition.Add(node, (n - 1) / B); }; // Walk while we need to add nodes while (n > 0) { if (Q.Count <= 0) { // Find next seed node, from an undiscovered connected component, and resume from there while (V.Contains(nodes[seedIndex])) { seedIndex += 1; } var seed = nodes[seedIndex]; // Add seed to queue and set of nodes Q.Put(seed); V.Add(seed); bucketize(seed); n -= 1; } var u = Q.Take(); var N = graph.Neighbors(u); foreach (var v in N) { if (!V.Contains(v) && n > 0) { Q.Put(v); V.Add(v); bucketize(v); n -= 1; } } } return(partition); }
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); }
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); }
/// <summary> /// Do a BFS sample with random seed nodes. Only use each edge label once for each outgoing edge of a node. /// </summary> /// <typeparam name="TNode"></typeparam> /// <param name="graph"></param> /// <param name="n">Upper bound on the number of nodes to sample.</param> /// <returns></returns> public static IEnumerable <TNode> DistinctLabelsSB <TNode, TLabel>(this MultiDirectedGraph <TNode, TLabel> graph, int n) { var Q = new Queue <TNode>(); var V = new HashSet <TNode>(); var nodes = graph.Nodes.ToArray(); // Possible improvement: order seed nodes by degree Utils.Shuffle(nodes); int seedIndex = 0; // Breadth-first walk while we need to add nodes while (n > 0) { if (Q.Count <= 0) { // Find next seed node, from an undiscovered connected component, and resume from there while (V.Contains(nodes[seedIndex])) { seedIndex += 1; // If we ran out of nodes early if (seedIndex == nodes.Length) { return(V); } } var seed = nodes[seedIndex]; // Add seed to queue and set of nodes Q.Enqueue(seed); V.Add(seed); n -= 1; } // Select by edge label, and target node label var u = Q.Dequeue(); var N = graph.Out(u).GroupBy(eo => Tuple.Create(graph.EdgeLabel(eo), graph.NodeLabel(graph.Target(eo)))).Select(group => graph.Target(group.First())); foreach (var v in N) { if (!V.Contains(v) && n > 0) { Q.Enqueue(v); V.Add(v); n -= 1; } } } return(V); }
/// <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> /// Sample nodes by walking the graph. The walk is done using a generic queue. /// </summary> /// <typeparam name="TNode"></typeparam> /// <typeparam name="TLabel"></typeparam> /// <typeparam name="TQueue"></typeparam> /// <param name="graph"></param> /// <param name="n"></param> /// <returns></returns> public static HashSet <TNode> QueuedSampler <TNode, TLabel, TQueue>(this MultiDirectedGraph <TNode, TLabel> graph, int n) where TQueue : GenericQueue <TNode>, new() { var V = new HashSet <TNode>(); var Q = new TQueue(); var nodes = graph.Nodes.ToArray(); Utils.Shuffle(nodes); int seedIndex = 0; // Walk while we need to add nodes while (n > 0) { if (Q.Count <= 0) { // Find next seed node, from an undiscovered connected component, and resume from there while (V.Contains(nodes[seedIndex])) { seedIndex += 1; // If we ran out of nodes early if (seedIndex == nodes.Length) { return(V); } } var seed = nodes[seedIndex]; // Add seed to queue and set of nodes Q.Put(seed); V.Add(seed); n -= 1; } var u = Q.Take(); var N = graph.Out(u).Select(eo => graph.Target(eo)); foreach (var v in N) { if (!V.Contains(v) && n > 0) { Q.Put(v); V.Add(v); n -= 1; } } } return(V); }
/// <summary> /// Splits the nodes of a graph into P blocks using a multilevel approach. /// </summary> /// <typeparam name="TNode">Type of node.</typeparam> /// <typeparam name="TLabel">Type of label.</typeparam> /// <param name="graph"></param> /// <param name="P">Number of partition blocks.</param> /// <param name="e">Coarsening stop condition.</param> /// <param name="M">Coarsening stop condition.</param> /// <param name="K">KernighanLin number of iterations.</param> public static Dictionary <TNode, int> MetisSplit <TNode, TLabel>(MultiDirectedGraph <TNode, TLabel> graph, int P, double e, int M, int K) { // Collapse the graph int counter = 0; var mapNodes = new Dictionary <TNode, int>(); var transformed = Collapse(graph).Clone(node => { if (!mapNodes.ContainsKey(node)) { mapNodes.Add(node, counter); counter += 1; } return(mapNodes[node]); }); // Coarsen the graph int n = graph.NumNodes; var coarsened = Coarsen(transformed, e, M, n / 2); var coarseGraph = coarsened.Item1; var projections = coarsened.Item2; // Partition the coarse graph var partition = Balance(coarseGraph.Nodes, node => coarseGraph.NodeLabel(node), 8); // Unproject the partition of the coarse graph projections.Add(partition); var finePartition = new Dictionary <TNode, int>(); foreach (var node in graph.Nodes) { int p = mapNodes[node]; foreach (var projection in projections) { p = projection[p]; } finePartition.Add(node, p); } // Refine the partition KernighanLin(graph, finePartition, K); return(finePartition); }
/// <summary> /// Measures the cut of a partition of a graph. /// </summary> /// <typeparam name="TNode"></typeparam> /// <typeparam name="TLabel"></typeparam> /// <param name="graph"></param> /// <param name="partition"></param> /// <returns></returns> public static int Cut <TNode, TLabel>(MultiDirectedGraph <TNode, TLabel> graph, IDictionary <TNode, int> partition) { int cut = 0; foreach (var edge in graph.Edges) { var s = graph.Source(edge); var t = graph.Target(edge); if (partition[s] != partition[t]) { cut += 1; } } return(cut); }
/// <summary> /// Randomly splits the nodes of a graph into P blocks. /// </summary> /// <typeparam name="TNode">Type of node.</typeparam> /// <typeparam name="TLabel">Type of label.</typeparam> /// <param name="graph">Graph to partition the nodes of.</param> /// <param name="P">Number of partition blocks.</param> public static Dictionary <TNode, int> RandomSplit <TNode, TLabel>(MultiDirectedGraph <TNode, TLabel> graph, int P) { var partition = new Dictionary <TNode, int>(); var nodes = graph.Nodes.ToArray(); nodes.Shuffle(); int p = 0; for (int i = 0; i < nodes.Length; i++) { partition.Add(nodes[i], p); p += 1; p %= P; } return(partition); }
/// <summary> /// Compute distances. /// </summary> /// <typeparam name="TNode"></typeparam> /// <param name="graph"></param> /// <returns></returns> public static Experiment DistanceProbabilityMassFunction <TNode, TLabel>(MultiDirectedGraph <TNode, TLabel> graph) { var distribution = new Dictionary <int, BigInteger>(); object @lock = new object(); Parallel.ForEach(graph.Nodes, node => { var distances = Utils.SingleSourceDistances(graph, node).Values.Where(distance => distance > 0 && distance < int.MaxValue); lock (@lock) { Utils.UpdateDistribution(distribution, distances); } }); double count = 0.0; foreach (var value in distribution.Values) { count += (double)value; } Experiment experiment = new Experiment(2) { Labels = new string[] { "Distance", "Probability" }, Meta = new string[] { "Distance-PMF", graph.Name }, F = d => { int distance = Convert.ToInt32(d); if (distribution.ContainsKey(distance)) { return(new double[] { d, (double)distribution[distance] / count }); } else { return(new double[] { d, 0.0 }); } }, }; experiment.Run(distribution.Keys.Min(), distribution.Keys.Max(), 1, 1); return(experiment); }
/// <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> /// Count the number of partition blocks in a graph. /// </summary> /// <typeparam name="TNode"></typeparam> /// <typeparam name="TLabel"></typeparam> /// <param name="graph"></param> /// <param name="labels"></param> /// <returns></returns> public static Experiment BisimulationPartitionSize <TNode, TLabel>(MultiDirectedGraph <TNode, TLabel> graph) { var partitioner = new GraphPartitioner <TNode, TLabel>(graph); var partitions = partitioner.MultilevelExactBisimulationReduction(); Experiment experiment = new Experiment(2) { Labels = new string[] { "k", "Number of blocks" }, Meta = new string[] { "Partition-size", graph.Name }, F = i => { var k = Convert.ToInt32(i); var partitionCount = partitions[k].Values.Distinct().Count(); return(new double[] { k, partitionCount }); }, }; experiment.Run(0, partitions.Count - 1, 1, 1); return(experiment); // experiment.Save(@"..\..\..\..\Analytics\BisimBlocks-" + graph.Name.ToLower() + ".tsv"); }
/// <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); }
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> /// Sample edges randomly uniformly. /// </summary> /// <typeparam name="TNode"></typeparam> /// <param name="graph"></param> /// <param name="m">Upper bound on the number of edges to sample.</param> /// <returns></returns> public static IEnumerable <TNode> RE <TNode, TLabel>(this MultiDirectedGraph <TNode, TLabel> graph, int m) { var edges = Utils.Shuffled(graph.Edges.ToArray()).Take(Math.Min(m, graph.NumEdges)).ToArray(); var nodes = new HashSet <TNode>(); foreach (var edge in edges) { var s = graph.Source(edge); var t = graph.Target(edge); if (!nodes.Contains(s)) { nodes.Add(s); } if (!nodes.Contains(t)) { nodes.Add(t); } } return(nodes); }
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="graph"></param> /// <param name="partition"></param> /// <returns></returns> public static ExactBisimulationWorker <TNode, TLabel>[] CreateWorkers(MultiDirectedGraph <TNode, TLabel> graph, IDictionary <TNode, int> partition) { var mapping = new Dictionary <int, int>(); int counter = 0; foreach (var kvp in partition) { if (!mapping.ContainsKey(kvp.Value)) { mapping.Add(kvp.Value, counter); counter += 1; } } var workers = new ExactBisimulationWorker <TNode, TLabel> [counter]; for (int i = 0; i < counter; i++) { workers[i] = new ExactBisimulationWorker <TNode, TLabel>(); workers[i].setGraph(graph); } var owner = new Dictionary <TNode, ExactBisimulationWorker <TNode, TLabel> >(); foreach (var node in graph.Nodes) { owner.Add(node, workers[mapping[partition[node]]]); } for (int i = 0; i < counter; i++) { workers[i].setOwner(owner); } return(workers); }
/// <summary> /// Constructor. /// </summary> /// <param name="graph"></param> public GraphPartitioner(MultiDirectedGraph <TNode, TLabel> graph) { this.graph = graph; }
/// <summary> /// /// </summary> /// <param name="graph"></param> private void setGraph(MultiDirectedGraph <TNode, TLabel> graph) { this.graph = graph; }
/// <summary> /// Compute distances to all target nodes from all source nodes. /// </summary> /// <typeparam name="TNode"></typeparam> /// <param name="graph"></param> /// <returns></returns> public static Dictionary <TNode, Dictionary <TNode, int> > AllPairsDistances <TNode, TLabel>(MultiDirectedGraph <TNode, TLabel> graph) { var distance = new Dictionary <TNode, Dictionary <TNode, int> >(); foreach (var source in graph.Nodes) { distance.Add(source, SingleSourceDistances(graph, source)); } return(distance); }
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); }