/// <summary>
        /// Finds nodes connected by paths, longer than 1 link.
        /// </summary>
        /// <param name="diGraph"></param>
        /// <returns>A list of node-pairs, that are connected by paths, that are at least 2 links long.</returns>
        public static List <DiNodePair> FindLongPaths(Dgml.DirectedGraph diGraph)
        {
            if (diGraph == null)
            {
                throw new ArgumentNullException(nameof(diGraph));
            }

            if (diGraph.Nodes == null || diGraph.Links == null)
            {
                return(new List <DiNodePair>());
            }

            var diNeighbors = GetDiNeighbors(diGraph);

            List <DiNodePair> pathsOfLength1 = diGraph.Links
                                               .Select(l => new DiNodePair(l.Source, l.Target)).ToList();

            // Build the initial set of long paths using paths of length 2.
            // Paths of length 2 are created from paths of length 1, extending them with each possible di-neighbor from their end.
            IEnumerable <DiNodePair> longPaths = new List <DiNodePair>();
            int i = 0;

            foreach (DiNodePair pathOfLength1 in pathsOfLength1)
            {
                // Extend this path to each di-neighbor of the endpoint.
                var extendedLongPaths = diNeighbors[pathOfLength1.EndId]
                                        .Select(end => new DiNodePair(pathOfLength1.StartId, end))
                                        .ToList();
                var newExtendedLongPaths = extendedLongPaths.Except(longPaths).ToList();
                longPaths = longPaths.Union(newExtendedLongPaths).ToList();
            }

            // Keep extending the long paths until no more extension can be found.
            int  loopCount  = 0;
            bool anyChanges = true;

            while (anyChanges && loopCount++ < 100)
            {
                anyChanges = false;
                foreach (DiNodePair longPath in longPaths)
                {
                    // Extend this path to each di-neighbor of the endpoint.
                    var extendedLongPaths = diNeighbors[longPath.EndId]
                                            .Select(end => new DiNodePair(longPath.StartId, end))
                                            .ToList();
                    var newExtendedLongPaths = extendedLongPaths.Except(longPaths).ToList();
                    longPaths  = longPaths.Union(newExtendedLongPaths).ToList();
                    anyChanges = anyChanges || newExtendedLongPaths.Count() > 0;
                }
            }

            return(longPaths.ToList());
        }
        /// <summary>
        /// A shortcut is a single link that connects two nodes that are also connected by a less direct route.
        /// This method lists those shortcuts.
        /// </summary>
        /// <returns>A list of Links that are shortcuts of longer paths.</returns>
        public static IEnumerable <Dgml.DirectedGraphLink> GetShortcuts(Dgml.DirectedGraph diGraph)
        {
            if (diGraph.Nodes == null || diGraph.Links == null)
            {
                return(new List <Dgml.DirectedGraphLink>());
            }

            var longPaths = FindLongPaths(diGraph);

            return(diGraph.Links.Where(l => longPaths
                                       .Any(longPath => longPath.StartId == l.Source && longPath.EndId == l.Target)));
        }
示例#3
0
        public void Save(Dgml.DirectedGraph graph, string dgmlFilePath)
        {
            if (File.Exists(dgmlFilePath))
            {
                File.Delete(dgmlFilePath);
            }

            using (var fileStream = File.Open(dgmlFilePath, FileMode.CreateNew))
            {
                XmlSerializer serializer = new XmlSerializer(typeof(Dgml.DirectedGraph));
                serializer.Serialize(fileStream, graph);
            }
        }
        /// <summary>
        /// A shortcut is a link that connects two nodes that are also connected by a less direct route.
        /// This method finds and removes those shortcuts.
        /// Note: mutates the object in the argument. Also returns the mutated object.
        /// </summary>
        public static Dgml.DirectedGraph RemoveShortcuts(Dgml.DirectedGraph diGraph)
        {
            if (diGraph.Nodes == null || diGraph.Links == null)
            {
                return(diGraph);
            }

            var shortcuts = GetShortcuts(diGraph);

            diGraph.Links = diGraph.Links.Except(shortcuts).ToArray();

            return(diGraph);
        }
示例#5
0
        public Dgml.DirectedGraph Merge(IEnumerable <Dgml.DirectedGraph> graphs)
        {
            if (graphs == null || graphs.Count() == 0)
            {
                throw new ArgumentException("At least one source graph is needed for merging.");
            }

            Dgml.DirectedGraph result = new Dgml.DirectedGraph();

            var firstGraph = graphs.First();

            result.Categories     = firstGraph.Categories;
            result.GraphDirection = firstGraph.GraphDirection;
            result.Styles         = firstGraph.Styles;
            result.Properties     = firstGraph.Properties;

            foreach (var graph in graphs)
            {
                // LINQ Union removes duplicates
                result.Nodes = result.Nodes == null ? graph.Nodes : result.Nodes.Union(graph.Nodes).ToArray();
                result.Links = result.Links == null ? graph.Links : result.Links.Union(graph.Links).ToArray();
            }

            // Remove not needed nodes.
            result.Nodes = result.Nodes
                           .Where(n => n.Category != "Comment")
                           .Where(n => !n.Label.ToLower().StartsWith("azure."))
                           .Where(n => !n.Label.ToLower().StartsWith("microsoft."))
                           .Where(n => !n.Label.ToLower().StartsWith("newtonsoft."))
                           //.Where(n => !n.Label.ToLower().StartsWith("serilog"))
                           .Where(n => !n.Label.ToLower().StartsWith("system."))
                           .Where(n => n.Label != "Unused assemblies?")
                           .Where(n => n.Label != "netstandard")
                           .Distinct()
                           .ToArray();

            var nodeIds = result.Nodes.Select(n => n.Id);

            // Remove duplicate and detached links
            result.Links = result.Links
                           .Select(l => new Dgml.DirectedGraphLink {
                Source = l.Source, Target = l.Target
            })
                           .Distinct()
                           .Where(l => nodeIds.Contains(l.Source) && nodeIds.Contains(l.Target))
                           .ToArray();

            return(result);
        }
        /// <summary>
        /// Get a collection of directional neighbors.
        /// The result is a dictionary, where
        ///     key is the id of the source node
        ///     value is a list of drectional neighbor node ids.
        /// All source nodes are listed, even if they have no di-neighbors.
        /// </summary>
        public static Dictionary <string, List <string> > GetDiNeighbors(Dgml.DirectedGraph diGraph)
        {
            if (diGraph == null)
            {
                throw new ArgumentNullException(nameof(diGraph));
            }

            if (diGraph.Nodes == null || diGraph.Links == null)
            {
                return(new Dictionary <string, List <string> >());
            }

            var ret = diGraph.Nodes.ToDictionary(n => n.Id, n => new List <string>());

            foreach (Dgml.DirectedGraphLink link in diGraph.Links)
            {
                if (ret.ContainsKey(link.Source))
                {
                    ret[link.Source].Add(link.Target);
                }
            }

            return(ret);
        }