示例#1
0
        /// <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);
        }
示例#2
0
        /// <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);
        }
示例#3
0
        /// <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);
        }
示例#4
0
        /// <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);
        }
        private void refine()
        {
            var newPartition = new Dictionary <TNode, int>();

            foreach (var u in graph.Nodes)
            {
                if (owner[u] == this)
                {
                    var H = new HashSet <int>();
                    H.Add(Hash(graph.NodeLabel(u)));

                    // Compute the hashes for pairs (label[u, v], pId_k-1(v))
                    foreach (var eo in graph.Out(u))
                    {
                        var v        = graph.Target(eo);
                        var edgeHash = Hash(graph.EdgeLabel(eo), partition[v]);
                        if (!H.Contains(edgeHash))
                        {
                            H.Add(edgeHash);
                        }
                    }

                    // Combine the hashes using some associative-commutative operator
                    int hash = 0;
                    foreach (var edgeHash in H)
                    {
                        hash += edgeHash;
                    }

                    // Assign partition block to node
                    newPartition.Add(u, hash);

                    // Determine if u has changed
                    changed[u] = partition[u] != hash;
                }
                else
                {
                    newPartition.Add(u, partition[u]);
                }
            }

            partition = newPartition;
        }
示例#6
0
        /// <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);
        }
示例#7
0
        /// <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);
        }
示例#8
0
        private void refine()
        {
            var newPartition = new Dictionary <TNode, int>();

            partitionMap.Clear();
            int counter = 0;

            foreach (var u in graph.Nodes)
            {
                if (owner[u] == this)
                {
                    var S = new HashSet <Tuple <TLabel, int> >();
                    foreach (var eo in graph.Out(u))
                    {
                        var v = graph.Target(eo);
                        var t = Tuple.Create(graph.EdgeLabel(eo), partition[v]);
                        if (!S.Contains(t))
                        {
                            S.Add(t);
                        }
                    }

                    var signature = Tuple.Create(graph.NodeLabel(u), S);

                    if (!partitionMap.ContainsKey(signature))
                    {
                        partitionMap.Add(signature, counter++);
                    }

                    // Assign partition block to node
                    newPartition.Add(u, partitionMap[signature]);
                }
            }

            partition = newPartition;
        }
        /// <summary>
        /// Estimate the (unbounded) bisimulation partition of a graph.
        /// Uses a hash function to determine partition block signature equivalence.
        /// </summary>
        /// <returns></returns>
        public IDictionary <TNode, int> EstimateBisimulationReduction()
        {
            // List of partitions; the k+1-th entry in the list corresponds to the k-th bisimulation partition
            var partitions = new List <Dictionary <TNode, int> >();

            stopwatch.Reset();
            stopwatch.Start();
            {
                // Base (k = 0); add empty partition
                partitions.Add(new Dictionary <TNode, int>());

                // Assign block to each node; depending on the node's label
                foreach (var node in graph.Nodes)
                {
                    var label = graph.NodeLabel(node);
                    partitions[0].Add(node, Utils.Hash(label));
                }

                // Step (k > 0); repeat until the previous partition is no longer refined
                int k = 0;
                do
                {
                    // Add empty partition
                    k += 1;
                    partitions.Add(new Dictionary <TNode, int>());

                    foreach (var u in graph.Nodes)
                    {
                        var H = new HashSet <int>();
                        H.Add(Utils.Hash(graph.NodeLabel(u)));

                        // Compute the hashes for pairs (label[u, v], pId_k-1(v))
                        foreach (var eo in graph.Out(u))
                        {
                            var v        = graph.Target(eo);
                            var edgeHash = Utils.Hash(graph.EdgeLabel(eo), partitions[k - 1][v]);
                            if (!H.Contains(edgeHash))
                            {
                                H.Add(edgeHash);
                            }
                        }

                        // Combine the hashes using some associative-commutative operator
                        int hash = 0;
                        foreach (var edgeHash in H)
                        {
                            hash += edgeHash;
                        }

                        // Assign partition block to node
                        partitions[k].Add(u, hash);
                    }
                } while (partitions[k].Values.Distinct().Count() > partitions[k - 1].Values.Distinct().Count());

                // Remove last partition because it is equivalent to the second last partition
                partitions.RemoveAt(partitions.Count - 1);
            }
            stopwatch.Stop();

            return(partitions[partitions.Count - 1]);
        }
示例#10
0
        /// <summary>
        /// KerninghanLin algorithm which refines a partition.
        /// Equalizes block sizes and swaps nodes between partition blocks to minimize the edge cut.
        /// </summary>
        /// <typeparam name="TNode">Type of node.</typeparam>
        /// <typeparam name="TLabel">Type of label.</typeparam>
        /// <param name="graph">The graph of the partition.</param>
        /// <param name="partition">The partition to refine.</param>
        /// <param name="K">Maximum number of iterations.</param>
        public static void KernighanLin <TNode, TLabel>(MultiDirectedGraph <TNode, TLabel> graph, Dictionary <TNode, int> partition, int K)
        {
            // Compute ED and ID of each node
            var ED = new Dictionary <TNode, int>();
            var ID = new Dictionary <TNode, int>();
            Func <TNode, int> D = node => ED[node] - ID[node];

            foreach (var node in graph.Nodes)
            {
                ED.Add(node, 0);
                ID.Add(node, 0);
            }

            foreach (var edge in graph.Edges)
            {
                var s = graph.Source(edge);
                var t = graph.Target(edge);

                if (partition[s] == partition[t])
                {
                    ID[s] += 1;
                    ID[t] += 1;
                }
                else
                {
                    ED[s] += 1;
                    ED[t] += 1;
                }
            }

            // Stick nodes into sets of their partition block
            var inverted = Utils.Invert(partition);
            var AI       = inverted.Keys.First();
            var BI       = inverted.Keys.Last();

            // Swaps a node from its original partition block to the other
            Action <TNode> swap = node =>
            {
                foreach (var edge in graph.Out(node).Concat(graph.In(node)))
                {
                    var neighbor = graph.Target(edge);

                    if (node.Equals(neighbor))
                    {
                        continue;
                    }

                    if (partition[node] == partition[neighbor])
                    {
                        // Will be in other block now
                        ID[neighbor] -= 1;
                        ID[node]     -= 1;
                        ED[neighbor] += 1;
                        ED[node]     += 1;
                    }
                    else
                    {
                        // Will be in same block now
                        ID[neighbor] += 1;
                        ID[node]     += 1;
                        ED[neighbor] -= 1;
                        ED[node]     -= 1;
                    }
                }

                if (partition[node] == AI)
                {
                    partition[node] = BI;
                    inverted[AI].Remove(node);
                    inverted[BI].Add(node);
                }
                else
                {
                    partition[node] = AI;
                    inverted[AI].Add(node);
                    inverted[BI].Remove(node);
                }
            };

            // Equalize block sizes
            while (Math.Abs(inverted[AI].Count - inverted[BI].Count) > 1)
            {
                if (inverted[AI].Count > inverted[BI].Count)
                {
                    // Move from A to B
                    var a = inverted[AI].MaxBy(node => D(node));
                    swap(a);
                }
                else
                {
                    // Move from B to A
                    var b = inverted[BI].MaxBy(node => D(node));
                    swap(b);
                }
            }

            // Keep performing positive swaps
            int n = Math.Min(inverted[AI].Count, inverted[BI].Count);

            for (int i = 0; i < K; i++)
            {
                bool hasGained = false;
                var  AA        = new HashSet <TNode>(inverted[AI]);
                var  BB        = new HashSet <TNode>(inverted[BI]);

                for (int j = 0; j < n; j++)
                {
                    var a    = AA.MaxBy(node => D(node));
                    var b    = BB.MaxBy(node => D(node));
                    int gain = D(a) + D(b);

                    if (graph.HasEdge(a, b))
                    {
                        gain -= 2;
                    }

                    if (graph.HasEdge(b, a))
                    {
                        gain -= 2;
                    }

                    if (gain > 0)
                    {
                        hasGained = true;
                        swap(a);
                        swap(b);
                    }

                    AA.Remove(a);
                    BB.Remove(b);
                }

                if (!hasGained)
                {
                    break;
                }
            }
        }