├── GraphSharp ├── Common │ ├── Usings.cs │ ├── Interfaces │ │ ├── IWeighted.cs │ │ ├── IColored.cs │ │ ├── IClonable{}.cs │ │ ├── IPositioned.cs │ │ ├── IForest.cs │ │ ├── IPath.cs │ │ └── IStatesHandler.cs │ └── Implementations │ │ ├── UnmanagedColor.cs │ │ ├── PrimForest.cs │ │ ├── ObjectPool.cs │ │ ├── Components.cs │ │ ├── KruskalForest.cs │ │ ├── UnionFind.cs │ │ ├── ByteStatesHandler.cs │ │ └── Path.cs ├── Exceptions │ ├── GraphConverterException.cs │ ├── NodeNotFoundException.cs │ ├── FailedToSolveMaxFlowException.cs │ ├── EdgeNotFoundException.cs │ ├── WrongCyclesPairException.cs │ ├── WrongGraphColoringException.cs │ ├── GraphDataIntegrityException.cs │ ├── WrongNodeSetException.cs │ └── NotLineGraphException.cs ├── Algorithms │ ├── GraphOperations │ │ ├── MakeDirected.cs │ │ ├── RemoveNodes.cs │ │ ├── MakeUndirected.cs │ │ ├── LineGraph.cs │ │ ├── RemoveIsolatedNodes.cs │ │ ├── ReverseEdges.cs │ │ ├── CreateNodes.cs │ │ ├── RemoveEdges.cs │ │ ├── FindLowLinkValues.cs │ │ ├── RemoveUndirectedEdges.cs │ │ ├── GetComplement.cs │ │ ├── ConnectAsHamiltonianCycle.cs │ │ ├── ConnectNodes.cs │ │ ├── Induce.cs │ │ ├── ConnectNodeToNodes.cs │ │ ├── Isolate.cs │ │ ├── IndependentSetFinder.cs │ │ ├── TopologicalSort.cs │ │ ├── DistanceMatrix.cs │ │ ├── FindComponents.cs │ │ ├── MakeComplete.cs │ │ ├── ColorNodes.cs │ │ ├── MakeSources.cs │ │ ├── ContractEdge.cs │ │ ├── ConnectRandomly.cs │ │ ├── TransitiveClosureOnRadius.cs │ │ ├── PlanarRender.cs │ │ ├── FindSourceRepresentativeSet.cs │ │ ├── Arrange.cs │ │ ├── DelaunayTriangulation.cs │ │ ├── FindEccentricity.cs │ │ ├── FindCyclesBasis.cs │ │ ├── FindLocalClusteringCoefficients.cs │ │ ├── MinCut.cs │ │ ├── Reindex.cs │ │ ├── FindStronglyConnectedComponents.cs │ │ ├── FindShortestPaths.cs │ │ └── FindArticulationPoints.cs │ ├── Coloring │ │ ├── GreedyColoring.cs │ │ ├── QuickGraphColoring.cs │ │ ├── RLFColoring.cs │ │ ├── ColoringAlgorithmBase.cs │ │ ├── DSaturColoring.cs │ │ └── ColoringResult.cs │ ├── AlgorithmBase.cs │ ├── IndependentSet │ │ ├── IndependentSetResult.cs │ │ ├── MaximalIndependentSetAlgorithmBase.cs │ │ └── BallardMyerIndependentSet.cs │ ├── KruskalAlgorithm.cs │ ├── MathUtils.cs │ ├── LowLinkValuesFinder copy │ └── LowLinkValuesFinder.cs ├── Nodes │ ├── NodeComparer.cs │ ├── INode.cs │ ├── Node.cs │ └── NodePropertiesMap.cs ├── Edges │ ├── EdgeComparer.cs │ ├── IEdge.cs │ ├── Edge.cs │ └── EdgePropertiesMap.cs ├── Visitors │ ├── Interfaces │ │ ├── IVisitorWithSteps.cs │ │ └── IVisitor{TNode,TEdge}.cs │ ├── BaseClasses │ │ ├── VisitorWithPropagator.cs │ │ ├── EdgeSelect.cs │ │ └── VisitorBase.cs │ └── Implementations │ │ ├── AnyPathFinder.cs │ │ ├── ActionVisitor{TNode,TEdge}.cs │ │ ├── SourceCreator.cs │ │ ├── DijkstrasAlgorithm.cs │ │ └── ShortestPathLengthFinderAlgorithms.cs ├── GraphStructures │ ├── Implementations │ │ ├── DefaultGraph.cs │ │ ├── GraphStructureOperation.cs │ │ ├── BaseEdgeSource.cs │ │ ├── GraphConfiguration.cs │ │ ├── GraphStructure.cs │ │ └── ImmutableGraphOperation.cs │ ├── Interfaces │ │ ├── INodeSource.cs │ │ ├── IEdgeSource.cs │ │ ├── IImmutableNodeSource.cs │ │ ├── IGraphConfiguration.cs │ │ ├── IImmutableGraph.cs │ │ ├── IImmutableEdgeSource.cs │ │ └── IGraph{}.cs │ └── Extensions │ │ ├── ICollectionExtensions.cs │ │ ├── EdgeNodeExtensions.cs │ │ ├── DictionaryExtensions.cs │ │ └── GraphOperationUtills.cs ├── Propagators │ ├── Interfaces │ │ └── IPropagator.cs │ ├── BaseClasses │ │ └── UsedNodeStates.cs │ └── Implementations │ │ ├── Propagator.cs │ │ └── ParallelPropagator.cs ├── GraphDrawer │ └── IShapeDrawer.cs ├── GraphSharp.sln ├── Adapters │ └── EdgeAdapter.cs └── GraphSharp.csproj ├── SimplestExample ├── NotoSans-Bold.ttf ├── SimplestExample.csproj └── Program.cs ├── GraphSharp.Tests ├── ExtensionsTests.cs ├── helpers │ ├── NodesComparer.cs │ └── NodeEqualityComparer.cs ├── Common.cs ├── Operations │ ├── BaseTest.cs │ ├── ColoringTests.cs │ └── PathFindersTests.cs ├── Models │ ├── TestGraphConfiguration.cs │ └── TestVisitor.cs ├── GraphSharp.Tests.csproj ├── GraphSharp.Tests.sln └── ManualTestData.cs ├── .gitignore ├── GraphSharp.GoogleOrTools ├── GraphSharp.GoogleOrTools.csproj ├── GraphSharp.GoogleOrTools.sln ├── Extensions.cs ├── MinCostFlowResult.cs ├── Coloring.cs └── TSP.cs ├── LICENSE └── GraphSharp.sln /GraphSharp/Common/Usings.cs: -------------------------------------------------------------------------------- 1 | global using RentedArraySharp; 2 | global using GraphSharp.Common; -------------------------------------------------------------------------------- /SimplestExample/NotoSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kemsekov/GraphSharp/HEAD/SimplestExample/NotoSans-Bold.ttf -------------------------------------------------------------------------------- /GraphSharp/Common/Interfaces/IWeighted.cs: -------------------------------------------------------------------------------- 1 | namespace GraphSharp.Common; 2 | /// 3 | public interface IWeighted 4 | { 5 | /// 6 | /// Weight of given element 7 | /// 8 | double Weight { get; set; } 9 | } -------------------------------------------------------------------------------- /GraphSharp.Tests/ExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using GraphSharp.Graphs; 6 | using Xunit; 7 | 8 | namespace GraphSharp.Tests; 9 | public class ExtensionsTests 10 | { 11 | 12 | } -------------------------------------------------------------------------------- /GraphSharp/Common/Interfaces/IColored.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | namespace GraphSharp.Common; 3 | 4 | /// 5 | public interface IColored 6 | { 7 | /// 8 | /// Color assigned to given element 9 | /// 10 | Color Color { get; set; } 11 | } -------------------------------------------------------------------------------- /GraphSharp/Common/Interfaces/IClonable{}.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace GraphSharp.Common; 3 | 4 | /// 5 | public interface ICloneable : ICloneable 6 | where T : ICloneable 7 | { 8 | /// 9 | /// Clones object 10 | /// 11 | new T Clone(); 12 | object ICloneable.Clone() => Clone(); 13 | } 14 | -------------------------------------------------------------------------------- /GraphSharp/Exceptions/GraphConverterException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace GraphSharp.Exceptions; 3 | 4 | /// 5 | /// Occurs when any graph converter failed 6 | /// 7 | public class GraphConverterException : Exception 8 | { 9 | /// 10 | public GraphConverterException(string message) : base(message) 11 | { 12 | } 13 | } -------------------------------------------------------------------------------- /GraphSharp/Common/Interfaces/IPositioned.cs: -------------------------------------------------------------------------------- 1 | using MathNet.Numerics.LinearAlgebra.Single; 2 | 3 | namespace GraphSharp.Common; 4 | 5 | /// 6 | /// Contains vector that describes position 7 | /// 8 | public interface IPositioned{ 9 | /// 10 | /// Object position in n-dimensional space 11 | /// 12 | public Vector Position{get;} 13 | } -------------------------------------------------------------------------------- /GraphSharp/Exceptions/NodeNotFoundException.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | namespace GraphSharp.Exceptions; 3 | 4 | /// 5 | /// Occurs required node is not found 6 | /// 7 | public class NodeNotFoundException : KeyNotFoundException 8 | { 9 | /// 10 | public NodeNotFoundException(string message) : base(message) 11 | { 12 | } 13 | } -------------------------------------------------------------------------------- /GraphSharp/Exceptions/FailedToSolveMaxFlowException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace GraphSharp.Exceptions; 3 | 4 | /// 5 | /// Executed when algorithms fails to find max flow 6 | /// 7 | public class FailedToSolveMaxFlowException : Exception{ 8 | 9 | /// 10 | public FailedToSolveMaxFlowException(string message) : base(message) 11 | { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /GraphSharp/Exceptions/EdgeNotFoundException.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | namespace GraphSharp.Exceptions; 3 | 4 | /// 5 | /// Exception that occurs when requested edge is not found 6 | /// 7 | public class EdgeNotFoundException : KeyNotFoundException 8 | { 9 | /// 10 | public EdgeNotFoundException(string message) : base(message) 11 | { 12 | } 13 | } -------------------------------------------------------------------------------- /GraphSharp.Tests/helpers/NodesComparer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | 7 | namespace GraphSharp.Tests.helpers 8 | { 9 | public class NodesComparer : IComparer 10 | { 11 | public int Compare(INode x, INode y) 12 | { 13 | return x.Id-y.Id; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /GraphSharp/Exceptions/WrongCyclesPairException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace GraphSharp.Exceptions; 3 | 4 | /// 5 | /// Occurs when you feed two linear-dependent cycles in cycle combiner 6 | /// 7 | public class WrongCyclesPairException : Exception 8 | { 9 | /// 10 | public WrongCyclesPairException(string message) : base(message) 11 | { 12 | 13 | } 14 | } -------------------------------------------------------------------------------- /GraphSharp/Exceptions/WrongGraphColoringException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace GraphSharp.Exceptions; 3 | 4 | /// 5 | /// Exception that occurs when graph coloring is invalid(two adjacent edges have same color) 6 | /// 7 | public class WrongGraphColoringException : Exception 8 | { 9 | /// 10 | public WrongGraphColoringException(string message) : base(message) 11 | { 12 | 13 | } 14 | } -------------------------------------------------------------------------------- /GraphSharp/Exceptions/GraphDataIntegrityException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace GraphSharp.Exceptions; 3 | 4 | /// 5 | /// Occurs when there is inconsistency in nodes and / or edges in a graph.
6 | /// When graph's internal structure is invalid. 7 | ///
8 | public class GraphDataIntegrityException : Exception 9 | { 10 | /// 11 | public GraphDataIntegrityException(string message) : base(message) 12 | { 13 | } 14 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.project 6 | **/.settings 7 | **/.toolstarget 8 | **/.vs 9 | **/.vscode 10 | **/*.*proj.user 11 | **/*.dbmdl 12 | **/*.jfm 13 | **/azds.yaml 14 | **/bin 15 | **/charts 16 | **/docker-compose* 17 | **/compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | **/publish 25 | **/log.txt 26 | **/*.jpg 27 | README.md 28 | -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/MakeDirected.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | namespace GraphSharp.Graphs; 3 | 4 | public partial class GraphOperation 5 | where TNode : INode 6 | where TEdge : IEdge 7 | { 8 | /// 9 | /// Randomly makes every connection between two nodes directed. 10 | /// 11 | public GraphOperation MakeDirected() 12 | { 13 | Edges.MakeDirected(); 14 | return this; 15 | } 16 | } -------------------------------------------------------------------------------- /GraphSharp/Exceptions/WrongNodeSetException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace GraphSharp.Exceptions; 7 | /// 8 | /// Occurs when node id not equals to index that used to store it. 9 | /// 10 | public class WrongNodeSetException : Exception 11 | { 12 | /// 13 | public WrongNodeSetException(string msg) : base(msg) 14 | { 15 | 16 | } 17 | } -------------------------------------------------------------------------------- /GraphSharp.Tests/helpers/NodeEqualityComparer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | 5 | namespace GraphSharp.Tests.Helpers 6 | { 7 | public class NodeEqualityComparer : IEqualityComparer 8 | { 9 | public bool Equals(INode x, INode y) 10 | { 11 | return x.Id==y.Id; 12 | } 13 | 14 | public int GetHashCode([DisallowNull] INode obj) 15 | { 16 | return obj.Id; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/RemoveNodes.cs: -------------------------------------------------------------------------------- 1 | namespace GraphSharp.Graphs; 2 | 3 | public partial class GraphOperation 4 | where TNode : INode 5 | where TEdge : IEdge 6 | { 7 | /// 8 | /// Isolate and removes specified nodes 9 | /// 10 | public GraphOperation RemoveNodes(params int[] nodes) 11 | { 12 | Isolate(nodes); 13 | 14 | foreach (var n in nodes) 15 | { 16 | Nodes.Remove(n); 17 | } 18 | 19 | return this; 20 | } 21 | } -------------------------------------------------------------------------------- /GraphSharp/Nodes/NodeComparer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | namespace GraphSharp; 4 | 5 | /// 6 | /// Node comparer that uses to compare two nodes. 7 | /// 8 | public class NodeComparer : IComparer 9 | where TNode : INode 10 | { 11 | /// 12 | public int Compare(TNode? x, TNode? y) 13 | { 14 | if (x is null || y is null) throw new NullReferenceException("Cannot compare nodes that null!"); 15 | return x.CompareTo(y); 16 | } 17 | } -------------------------------------------------------------------------------- /GraphSharp.Tests/Common.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace GraphSharp.Tests; 7 | public static class Common 8 | { 9 | public static void Shuffle(this IList list, Random rng = null) 10 | { 11 | rng ??= new Random(); 12 | int n = list.Count; 13 | while (n > 1) 14 | { 15 | n--; 16 | int k = rng.Next(n + 1); 17 | T value = list[k]; 18 | list[k] = list[n]; 19 | list[n] = value; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/MakeUndirected.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace GraphSharp.Graphs; 3 | 4 | public partial class GraphOperation 5 | where TNode : INode 6 | where TEdge : IEdge 7 | { 8 | /// 9 | /// Makes every connection between two nodes bidirectional by adding missing edges. 10 | /// 11 | public GraphOperation MakeBidirected(Action? onCreatedEdge = null) 12 | { 13 | Edges.MakeBidirected((a,b)=>Configuration.CreateEdge(Nodes[a],Nodes[b]),onCreatedEdge); 14 | return this; 15 | } 16 | } -------------------------------------------------------------------------------- /GraphSharp/Edges/EdgeComparer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | namespace GraphSharp; 4 | 5 | /// 6 | /// Edge comparer that uses to compare two edges 7 | /// 8 | public class EdgeComparer : IComparer 9 | where TNode : INode 10 | where TEdge : IEdge 11 | { 12 | /// 13 | public int Compare(TEdge? x, TEdge? y) 14 | { 15 | if (x is null || y is null) throw new NullReferenceException("Cannot compare null edges!"); 16 | return x.CompareTo(y); 17 | } 18 | } -------------------------------------------------------------------------------- /GraphSharp/Common/Implementations/UnmanagedColor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace GraphSharp; 8 | /// 9 | /// Unmanaged ARGB color structure 10 | /// 11 | public struct UnmanagedColor 12 | { 13 | /// 14 | /// Corresponding color element 15 | /// 16 | public byte A,R,G,B; 17 | /// 18 | /// Color converter 19 | /// 20 | public static explicit operator Color(UnmanagedColor c) => Color.FromArgb(c.A,c.R,c.G,c.B); 21 | } -------------------------------------------------------------------------------- /GraphSharp/Common/Interfaces/IForest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace GraphSharp.Common; 4 | 5 | /// 6 | /// Forest interface 7 | /// 8 | public interface IForest{ 9 | /// 10 | /// Determine whatever given two nodes in same component 11 | /// 12 | public bool InSameComponent(int nodeId1, int nodeId2); 13 | /// 14 | /// Edges of forest 15 | /// 16 | IEnumerable Forest{get;} 17 | /// Degree of given node in a forest, or -1 if not found 18 | public int Degree(int nodeId); 19 | 20 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/LineGraph.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using GraphSharp.Common; 3 | 4 | namespace GraphSharp.Graphs; 5 | 6 | public partial class ImmutableGraphOperation 7 | where TNode : INode 8 | where TEdge : IEdge 9 | { 10 | /// 11 | /// Builds line graph out of current graph.
12 | /// Line graph is a graph, where edges became nodes and two nodes connected if 13 | /// their underlying edges have a common node. 14 | ///
15 | public LineGraph LineGraph() 16 | { 17 | return new LineGraph(StructureBase); 18 | } 19 | } -------------------------------------------------------------------------------- /GraphSharp.Tests/Operations/BaseTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using GraphSharp.Graphs; 7 | using GraphSharp.Tests.Models; 8 | using MathNet.Numerics.LinearAlgebra.Single; 9 | using Xunit; 10 | 11 | namespace GraphSharp.Tests.Operations 12 | { 13 | public class BaseTest 14 | { 15 | 16 | public IGraph _Graph; 17 | 18 | public BaseTest() 19 | { 20 | this._Graph = new Graph(new TestGraphConfiguration(new Random())); 21 | _Graph.Do.CreateNodes(1000); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /GraphSharp/Exceptions/NotLineGraphException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | namespace GraphSharp.Exceptions; 6 | 7 | /// 8 | /// Exception that occurs when some graph needs to be line graph for execution of algorithm, 9 | /// but given graph is not a line graph 10 | /// 11 | public class NotLineGraphException : Exception 12 | { 13 | /// 14 | /// 15 | public NotLineGraphException() : base("Given graph is not a line graph") 16 | { 17 | } 18 | /// 19 | /// 20 | public NotLineGraphException(string msg) : base(msg){} 21 | } -------------------------------------------------------------------------------- /GraphSharp.Tests/Models/TestGraphConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | 4 | using GraphSharp.Graphs; 5 | using MathNet.Numerics.LinearAlgebra.Single; 6 | 7 | namespace GraphSharp.Tests.Models 8 | { 9 | public class TestGraphConfiguration : GraphConfiguration 10 | { 11 | public TestGraphConfiguration(Random rand) : 12 | base( 13 | rand ?? new Random(), 14 | (n1,n2)=>new Edge(n1,n2){Weight=(n1.MapProperties().Position-n2.MapProperties().Position).L2Norm()}, 15 | id=> new Node(id).With(p=>p.Position = DenseVector.Create(2,i=>rand.NextSingle()))) 16 | {} 17 | } 18 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/RemoveIsolatedNodes.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | namespace GraphSharp.Graphs; 3 | 4 | public partial class GraphOperation 5 | where TNode : INode 6 | where TEdge : IEdge 7 | { 8 | /// 9 | /// Removes isolated nodes 10 | /// 11 | public GraphOperation RemoveIsolatedNodes() 12 | { 13 | var toRemove = 14 | Nodes 15 | .Where(x => Edges.IsIsolated(x.Id)) 16 | .Select(x => x.Id) 17 | .ToArray(); 18 | 19 | foreach (var n in toRemove) 20 | { 21 | Nodes.Remove(n); 22 | } 23 | 24 | return this; 25 | } 26 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/ReverseEdges.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | namespace GraphSharp.Graphs; 3 | 4 | public partial class GraphOperation 5 | where TNode : INode 6 | where TEdge : IEdge 7 | { 8 | /// 9 | /// Reverse every edge connection ==> like swap(edge.Source,edge.Target) 10 | /// 11 | public GraphOperation ReverseEdges() 12 | { 13 | foreach (var e in Edges.ToList()) 14 | { 15 | Edges.Remove(e); 16 | var tmp = e.SourceId; 17 | e.SourceId = e.TargetId; 18 | e.TargetId = tmp; 19 | Edges.Add(e); 20 | } 21 | return this; 22 | } 23 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/CreateNodes.cs: -------------------------------------------------------------------------------- 1 | namespace GraphSharp.Graphs; 2 | 3 | public partial class GraphOperation 4 | where TNode : INode 5 | where TEdge : IEdge 6 | { 7 | /// 8 | /// Clears graph and creates some count of nodes. 9 | /// 10 | /// Count of nodes to create 11 | public GraphOperation CreateNodes(int nodesCount) 12 | { 13 | this.StructureBase.Clear(); 14 | //create nodes 15 | for (int i = 0; i < nodesCount; i++) 16 | { 17 | var node = Configuration.CreateNode(i); 18 | Nodes.Add(node); 19 | } 20 | return this; 21 | } 22 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/RemoveEdges.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | namespace GraphSharp.Graphs; 4 | 5 | public partial class GraphOperation 6 | where TNode : INode 7 | where TEdge : IEdge 8 | { 9 | /// 10 | /// Removes all edges that satisfies predicate. 11 | /// 12 | public GraphOperation RemoveEdges(Predicate toRemove) 13 | { 14 | var edgesToRemove = 15 | Edges.Where(x => toRemove(x)) 16 | .Select(x => (x.SourceId, x.TargetId)) 17 | .ToArray(); 18 | 19 | foreach (var e in edgesToRemove) 20 | Edges.Remove(e.Item1, e.Item2); 21 | 22 | return this; 23 | } 24 | } -------------------------------------------------------------------------------- /GraphSharp/Visitors/Interfaces/IVisitorWithSteps.cs: -------------------------------------------------------------------------------- 1 | namespace GraphSharp.Visitors; 2 | /// 3 | /// Visitor that counts it's steps and completion state 4 | /// 5 | public interface IVisitorWithSteps : IVisitor 6 | where TEdge : IEdge 7 | { 8 | /// 9 | /// Indicates if algorithm is done and do not need any further executions 10 | /// 11 | bool Done {get;} 12 | /// 13 | /// Count of how many steps executed so far 14 | /// 15 | /// 16 | int Steps{get;} 17 | /// 18 | /// Whatever given algorithm did anything in last iteration 19 | /// 20 | public bool DidSomething{get;set;} 21 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/FindLowLinkValues.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using GraphSharp.Common; 3 | using Unchase.Satsuma.Core.Extensions; 4 | 5 | namespace GraphSharp.Graphs; 6 | 7 | public partial class ImmutableGraphOperation 8 | where TNode : INode 9 | where TEdge : IEdge 10 | { 11 | /// 12 | /// Finds low link values for nodes. Can be used to get strongly connected components 13 | /// 14 | /// Array where index is node id and value is low link value. When value is -1 it means that there is not node with given index. 15 | public RentedArray FindLowLinkValues() 16 | { 17 | return new LowLinkValuesFinder(Nodes.Cast(),Edges).FindLowLinkValues(); 18 | } 19 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/RemoveUndirectedEdges.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | namespace GraphSharp.Graphs; 3 | 4 | public partial class GraphOperation 5 | where TNode : INode 6 | where TEdge : IEdge 7 | { 8 | /// 9 | /// Removes bidirected edges. 10 | /// 11 | public GraphOperation RemoveBidirectedEdges() 12 | { 13 | foreach (var n in Nodes) 14 | { 15 | var edges = Edges.OutEdges(n.Id).ToArray(); 16 | foreach (var edge in edges) 17 | { 18 | if (Edges.Remove(edge.TargetId, edge.SourceId)) 19 | { 20 | Edges.Remove(edge); 21 | } 22 | } 23 | } 24 | return this; 25 | } 26 | } -------------------------------------------------------------------------------- /GraphSharp/GraphStructures/Implementations/DefaultGraph.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace GraphSharp.Graphs; 3 | 4 | /// 5 | /// Default graph implementation. Uses and as node and edge types 6 | /// 7 | public class Graph : Graph 8 | { 9 | /// 10 | /// Initialize new graph 11 | /// 12 | public Graph() : base(id => new(id), (n1, n2) => new(n1, n2)) 13 | { 14 | } 15 | /// 16 | /// Initialize new graph 17 | /// 18 | /// How to create node 19 | /// How to create edge 20 | public Graph(Func createNode, Func createEdge) : base(createNode, createEdge) 21 | { 22 | 23 | } 24 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/GetComplement.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace GraphSharp.Graphs; 5 | 6 | public partial class ImmutableGraphOperation 7 | where TNode : INode 8 | where TEdge : IEdge 9 | { 10 | /// 11 | /// Computes graph complement(including self-edges) 12 | /// 13 | public IList GetComplement(){ 14 | var n = Nodes.Count(); 15 | var result = new List(n*(n-1)); 16 | foreach(var n1 in Nodes){ 17 | foreach(var n2 in Nodes){ 18 | if(Edges.Contains(n1.Id,n2.Id)) continue; 19 | var toAdd = Configuration.CreateEdge(n1,n2); 20 | result.Add(toAdd); 21 | } 22 | } 23 | return result; 24 | } 25 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/ConnectAsHamiltonianCycle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using MathNet.Numerics.LinearAlgebra.Single; 4 | 5 | namespace GraphSharp.Graphs; 6 | 7 | public partial class GraphOperation 8 | where TNode : INode 9 | where TEdge : IEdge 10 | { 11 | /// 12 | /// Connects graph nodes as hamiltonian cycle 13 | /// 14 | public GraphOperation ConnectAsHamiltonianCycle(Func getPos) 15 | { 16 | var tsp = TspCheapestLinkOnPositions(getPos); 17 | tsp = TspSmallRandomOpt2(tsp.Tour,tsp.TourCost,(n1,n2)=>(getPos(n1)-getPos(n2)).L2Norm()); 18 | tsp.Tour.Aggregate((n1,n2)=>{ 19 | Edges.Add(Configuration.CreateEdge(n1,n2)); 20 | return n2; 21 | }).ToString(); 22 | return this; 23 | } 24 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/ConnectNodes.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | namespace GraphSharp.Graphs; 3 | 4 | public partial class GraphOperation 5 | where TNode : INode 6 | where TEdge : IEdge 7 | { 8 | /// 9 | /// Randomly create edgesCount of edges for each node. 10 | /// 11 | /// How much edges each node need 12 | public GraphOperation ConnectNodes(int edgesCount) 13 | { 14 | var availableNodes = Nodes.Select(x => x.Id).ToList(); 15 | edgesCount = edgesCount > Nodes.Count ? Nodes.Count : edgesCount; 16 | 17 | foreach (var node in Nodes) 18 | { 19 | int startIndex = Configuration.Rand.Next(availableNodes.Count); 20 | ConnectNodeToNodes(node, startIndex, edgesCount, availableNodes); 21 | } 22 | return this; 23 | } 24 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/Induce.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using GraphSharp.Common; 4 | 5 | namespace GraphSharp.Graphs; 6 | 7 | public partial class ImmutableGraphOperation 8 | where TNode : INode 9 | where TEdge : IEdge 10 | { 11 | /// 12 | /// Get induced subgraph from this graph structure.
13 | /// Induced graph is a subgraph of graph such that all edges connecting any pair of nodes from subgraph also in subgraph 14 | ///
15 | /// Nodes to induce 16 | /// Induced subgraph of current graph 17 | public Graph Induce(IEnumerable nodes){ 18 | var result = new Graph(Configuration); 19 | result.SetSources(nodes: nodes.Select(id=>Nodes[id]),edges:Edges.InducedEdges(nodes)); 20 | return result; 21 | } 22 | } -------------------------------------------------------------------------------- /SimplestExample/SimplestExample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | Always 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /GraphSharp/Algorithms/Coloring/GreedyColoring.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | namespace GraphSharp.Graphs; 3 | 4 | /// 5 | /// Linear-time greedy graph nodes coloring algorithm.
6 | ///
7 | public class GreedyColoring : ColoringAlgorithmBase 8 | where TNode : INode 9 | where TEdge : IEdge 10 | { 11 | /// 12 | public GreedyColoring(IImmutableNodeSource nodes, IImmutableEdgeSource edges) : base(nodes, edges) 13 | { 14 | } 15 | /// 16 | public override ColoringResult Color() 17 | { 18 | var order = Nodes.OrderBy(x => -Edges.Neighbors(x.Id).Count()); 19 | // var order = Nodes; 20 | 21 | var colors = ArrayPoolStorage.RentArray(Nodes.MaxNodeId + 1); 22 | 23 | foreach (var n in order) 24 | { 25 | AssignColor(n.Id, colors); 26 | } 27 | 28 | return new(colors); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /GraphSharp/GraphStructures/Interfaces/INodeSource.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | namespace GraphSharp.Graphs; 3 | /// 4 | /// Node source. 5 | /// 6 | public interface INodeSource : IImmutableNodeSource, ICollection 7 | { 8 | /// 9 | /// Removes node by it's id 10 | /// 11 | /// True if removed, else false 12 | bool Remove(int nodeId); 13 | /// 14 | /// Get node by it's id. Assign node by id 15 | /// 16 | new TNode this[int nodeId] { get; set; } 17 | /// 18 | /// Changes node Id by moving it 19 | /// 20 | /// True if moved successfully, else false 21 | bool Move(TNode node, int newId); 22 | /// 23 | /// Changes node Id by moving it 24 | /// 25 | /// true if moved successfully, else false 26 | bool Move(int nodeId, int newId); 27 | } -------------------------------------------------------------------------------- /GraphSharp.GoogleOrTools/GraphSharp.GoogleOrTools.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | enable 13 | true 14 | Library 15 | net6.0 16 | MIT 17 | 3.1.3 18 | Kemsekov.GraphSharp.GoogleOrTools 19 | True 20 | $(Version) 21 | Kemsekov 22 | https://github.com/Kemsekov/GraphSharp 23 | 6 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /GraphSharp/Nodes/INode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using GraphSharp.Common; 5 | namespace GraphSharp; 6 | 7 | /// 8 | /// Node interface. Each node used with graph and must be inherited from this interface. 9 | /// 10 | public interface INode : IComparable, ICloneable, System.IEquatable 11 | { 12 | /// 13 | /// Get or set node property 14 | /// 15 | object this[string propertyName]{get;set;} 16 | /// 17 | /// Node properties 18 | /// 19 | IDictionary Properties { get; } 20 | /// 21 | /// Unique identifier for node 22 | /// 23 | int Id { get; set; } 24 | int IComparable.CompareTo(INode? other) 25 | { 26 | other = other ?? throw new NullReferenceException("Cannot compare node that is null"); 27 | return Id - other.Id; 28 | } 29 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/ConnectNodeToNodes.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | namespace GraphSharp.Graphs; 3 | 4 | public partial class GraphOperation 5 | where TNode : INode 6 | where TEdge : IEdge 7 | { 8 | /// 9 | /// Create some count of random edges for given node. Help function 10 | /// 11 | private void ConnectNodeToNodes(TNode node, int startIndex, int edgesCount, IList source) 12 | { 13 | lock (node) 14 | for (int i = 0; i < edgesCount; i++) 15 | { 16 | int index = (startIndex + i) % source.Count; 17 | var targetId = source[index]; 18 | if (node.Id == targetId) 19 | { 20 | startIndex++; 21 | i--; 22 | continue; 23 | } 24 | var target = Nodes[targetId]; 25 | 26 | Edges.Add(Configuration.CreateEdge(node, target)); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/Isolate.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using GraphSharp.Common; 3 | 4 | namespace GraphSharp.Graphs; 5 | 6 | public partial class GraphOperation 7 | where TNode : INode 8 | where TEdge : IEdge 9 | { 10 | /// 11 | /// Isolates nodes. Removes all incoming and outcoming edges from each node that satisfies predicate. It is faster to pass many nodes in single call of this function than make many calls of this function on each node. 12 | /// 13 | public GraphOperation Isolate(params int[] nodes) 14 | { 15 | using var toIsolate = ArrayPoolStorage.RentArray(Nodes.MaxNodeId + 1); 16 | foreach (var n in nodes) 17 | toIsolate[n] = 1; 18 | var toRemove = 19 | Edges.Where(x => toIsolate[x.SourceId] == 1 || toIsolate[x.TargetId] == 1) 20 | .ToArray(); 21 | 22 | foreach (var e in toRemove) 23 | { 24 | Edges.Remove(e); 25 | } 26 | return this; 27 | } 28 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/IndependentSetFinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace GraphSharp.Graphs; 4 | 5 | public partial class ImmutableGraphOperation 6 | where TNode : INode 7 | where TEdge : IEdge 8 | { 9 | /// 10 | /// Finds maximal independent set.
11 | /// Altered implementation of this algorithm:
12 | ///
13 | /// 14 | /// You may need to find independent set from some subset of nodes. Use this to control it. 15 | /// Only nodes that pass a condition can be added to independent set 16 | /// 17 | /// Nodes from maximal independent set 18 | public IndependentSetResult FindMaximalIndependentSet(Predicate condition) 19 | { 20 | using var alg = new BallardMyerIndependentSet(Nodes,Edges,condition); 21 | var result = alg.Find(); 22 | return result; 23 | } 24 | } -------------------------------------------------------------------------------- /GraphSharp/GraphStructures/Interfaces/IEdgeSource.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | namespace GraphSharp.Graphs; 3 | 4 | /// 5 | /// Represents edges storage object 6 | /// 7 | public interface IEdgeSource : IImmutableEdgeSource, ICollection 8 | { 9 | /// 10 | /// Removes all edges that equals to by default .
11 | /// This method of removal allows to remove some of parallel edges, which 12 | /// are not equal to each other. 13 | /// Meanwhile other Remove methods will remove all parallel edges. 14 | ///
15 | new bool Remove(TEdge edge); 16 | bool ICollection.Remove(TEdge item) => Remove(item); 17 | /// 18 | /// Removes all edges that directs sourceId -> targetId (including parallel edges) 19 | /// 20 | bool Remove(int sourceId, int targetId); 21 | /// 22 | /// Clears a memory from empty(unused) edges 23 | /// 24 | void Trim(); 25 | } -------------------------------------------------------------------------------- /GraphSharp/Propagators/Interfaces/IPropagator.cs: -------------------------------------------------------------------------------- 1 | 2 | using GraphSharp.Graphs; 3 | using GraphSharp.Visitors; 4 | namespace GraphSharp.Propagators; 5 | 6 | /// 7 | /// Algorithm that uses to do graph exploration and modification 8 | /// in a specific way designed by implementations 9 | /// 10 | public interface IPropagator 11 | where TEdge : IEdge 12 | { 13 | /// 14 | /// Will push forward (deeper) in a graph execution of an algorithm 15 | /// 16 | void Propagate(); 17 | /// 18 | /// Change current propagator visit position. 19 | /// Assigns to given nodes state to be visited in next iteration. 20 | /// 21 | void SetPosition(params int[] nodeIndices); 22 | /// 23 | /// Sets new graph edges and visitor. 24 | /// Clears all node states for current propagator and resets them to default settings. 25 | /// 26 | void Reset(IImmutableEdgeSource edges, IVisitor visitor, int maxNodeId = -1); 27 | } -------------------------------------------------------------------------------- /GraphSharp/GraphDrawer/IShapeDrawer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using MathNet.Numerics.LinearAlgebra.Single; 5 | 6 | namespace GraphSharp.GraphDrawer; 7 | 8 | /// 9 | /// Interface for basic drawing operations. 10 | /// 11 | public interface IShapeDrawer 12 | { 13 | /// 14 | /// Draws text on given position, with given size and color 15 | /// 16 | void DrawText(string text, Vector position, Color color, double size); 17 | /// 18 | /// Fills ellipse on given position, with given with and height and color 19 | /// 20 | void FillEllipse(Vector position, double width, double height, Color color); 21 | /// 22 | /// Draws a line between two points with given thickness and color 23 | /// 24 | void DrawLine(Vector start, Vector end, Color color, double thickness); 25 | /// 26 | /// Clears whole window with 27 | /// 28 | void Clear(Color color); 29 | } -------------------------------------------------------------------------------- /GraphSharp/Propagators/BaseClasses/UsedNodeStates.cs: -------------------------------------------------------------------------------- 1 | using GraphSharp.Propagators; 2 | 3 | namespace GraphSharp.Common; 4 | /// 5 | /// Contains all states used by 6 | /// 7 | public struct UsedNodeStates 8 | { 9 | /// 10 | /// None state for node. 11 | /// 12 | public const byte None = 0; 13 | /// 14 | /// In this state node is in check for visit in next iteration of any algorithm 15 | /// 16 | public const byte ToVisit = 1; 17 | /// 18 | /// In this state node is required to visit for current iteration 19 | /// 20 | public const byte Visited = 2; 21 | /// 22 | /// In this state on each iteration "in edges" of node is chosen as next generation 23 | /// 24 | public const byte IterateByInEdges = 4; 25 | /// 26 | /// In this state on each iteration "out edges" of node is chosen as next generation 27 | /// 28 | public const byte IterateByOutEdges = 8; 29 | } -------------------------------------------------------------------------------- /GraphSharp.Tests/Models/TestVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using GraphSharp.Common; 6 | using GraphSharp.Graphs; 7 | 8 | using GraphSharp.Propagators; 9 | using GraphSharp.Visitors; 10 | 11 | namespace GraphSharp.Tests.Models 12 | { 13 | public class TestVisitor : VisitorWithPropagator 14 | { 15 | 16 | public override PropagatorBase Propagator{get;} 17 | public TestVisitor(IGraph graph) 18 | { 19 | this.Propagator = new ParallelPropagator(graph.Edges,this,graph.Nodes.MaxNodeId); 20 | } 21 | 22 | protected override void StartImpl() 23 | { 24 | } 25 | 26 | protected override bool SelectImpl(EdgeSelect edge) 27 | { 28 | return true; 29 | } 30 | 31 | protected override void VisitImpl(int node) 32 | { 33 | } 34 | 35 | protected override void EndImpl() 36 | { 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/TopologicalSort.cs: -------------------------------------------------------------------------------- 1 | using GraphSharp.Visitors; 2 | namespace GraphSharp.Graphs; 3 | 4 | public partial class ImmutableGraphOperation 5 | where TNode : INode 6 | where TEdge : IEdge 7 | { 8 | /// 9 | /// Do topological sort on the graph. 10 | /// 11 | /// Points that will be starting points for topological sort. If empty will assign them to a sources nodes. If will not find sources nodes then will throw. 12 | /// Object that contains List of layers where each layer is a list of nodes at a certain layer of topological sort.
13 | /// Use to change X coordinates to a certain value defined by sorting.
14 | public TopologicalSorter TopologicalSort(params int[] startNodes) 15 | { 16 | var alg = new TopologicalSorter(StructureBase,startNodes); 17 | while (!alg.Done) 18 | { 19 | alg.Propagate(); 20 | } 21 | return alg; 22 | } 23 | } -------------------------------------------------------------------------------- /GraphSharp/GraphStructures/Interfaces/IImmutableNodeSource.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | namespace GraphSharp.Graphs; 3 | /// 4 | /// Immutable nodes source 5 | /// 6 | public interface IImmutableNodeSource : IEnumerable 7 | { 8 | /// 9 | /// Max id value of all nodes. If there is no nodes, returns -1. 10 | /// 11 | int MaxNodeId { get; } 12 | /// 13 | /// Min id value of all nodes. If there is no nodes, returns -1. 14 | /// 15 | int MinNodeId { get; } 16 | /// 17 | /// Get node by it's id. Assign node by id 18 | /// 19 | TNode this[int nodeId] { get; } 20 | /// 21 | /// Tries to get node by it's id 22 | /// 23 | /// Node id 24 | /// Retrieved node 25 | /// True if found, else false 26 | bool TryGetNode(int nodeId, out TNode? node); 27 | /// True if found node with given id, else false 28 | bool Contains(int nodeId); 29 | } -------------------------------------------------------------------------------- /GraphSharp/Common/Implementations/PrimForest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using GraphSharp.Graphs; 4 | 5 | namespace GraphSharp.Common; 6 | 7 | /// 8 | /// Prim's algorithm result 9 | /// 10 | public class PrimForest : IForest 11 | where TEdge : IEdge 12 | { 13 | Lazy> Components; 14 | 15 | IEdgeSource EdgeSource { get; } 16 | /// 17 | /// Creates new PrimForest instance 18 | /// 19 | public PrimForest(IEdgeSource edges, Func> getComponents) 20 | { 21 | Components = new(getComponents); 22 | this.EdgeSource = edges; 23 | } 24 | /// 25 | public IEnumerable Forest => EdgeSource; 26 | /// 27 | 28 | public int Degree(int nodeId) 29 | { 30 | return EdgeSource.Degree(nodeId); 31 | } 32 | /// 33 | 34 | public bool InSameComponent(int nodeId1, int nodeId2) 35 | { 36 | return Components.Value.InSameComponent(nodeId1,nodeId2); 37 | } 38 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/Coloring/QuickGraphColoring.cs: -------------------------------------------------------------------------------- 1 | using GraphSharp.Adapters; 2 | using QuikGraph.Algorithms.VertexColoring; 3 | namespace GraphSharp.Graphs; 4 | 5 | /// 6 | /// QuikGraph's coloring algorithm 7 | /// 8 | public class QuickGraphColoring : ColoringAlgorithmBase 9 | where TNode : INode 10 | where TEdge : IEdge 11 | { 12 | /// 13 | public QuickGraphColoring(IImmutableNodeSource nodes, IImmutableEdgeSource edges) : base(nodes, edges) 14 | { 15 | } 16 | /// 17 | public override ColoringResult Color() 18 | { 19 | 20 | var quikGraph = new ToQuikGraphAdapter(Nodes, Edges); 21 | var coloring = new VertexColoringAlgorithm>(quikGraph); 22 | coloring.Compute(); 23 | var result = ArrayPoolStorage.RentArray(Nodes.MaxNodeId + 1); 24 | for (int i = 0; i < result.Length; i++) 25 | { 26 | if (coloring.Colors[i] is int color) 27 | result[i] = color; 28 | } 29 | return new(result); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /GraphSharp/Visitors/Interfaces/IVisitor{TNode,TEdge}.cs: -------------------------------------------------------------------------------- 1 | namespace GraphSharp.Visitors; 2 | /// 3 | /// Contains method to control execution of algorithms on a graph. 4 | /// 5 | public interface IVisitor 6 | where TEdge : IEdge 7 | { 8 | /// 9 | /// First function to call in each algorithm step. 10 | /// 11 | void Start(); 12 | /// 13 | /// Method to process and filter edges that will be passed forward for execution 14 | /// 15 | /// Edge to process 16 | /// True if we need to step into given edge, else false 17 | bool Select(EdgeSelect edge); 18 | /// 19 | /// Method to process nodes that was passed by selected edges. 20 | /// 21 | /// Node id to process 22 | void Visit(int node); 23 | /// 24 | /// After each select and visit iterations are done this method will do clean-up 25 | /// or states changes or any specific task at the end of algorithm step 26 | /// 27 | void End(); 28 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/DistanceMatrix.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using MathNet.Numerics.LinearAlgebra.Single; 6 | using QuikGraph; 7 | 8 | namespace GraphSharp.Graphs; 9 | 10 | public partial class ImmutableGraphOperation 11 | where TNode : INode 12 | where TEdge : IEdge 13 | { 14 | /// 15 | /// Computes distance matrix from graph nodes, using distance metric. 16 | /// 17 | /// How to compute distances between nodes 18 | /// Matrix where each (i,j) element corresponds to distance between node under index i and node under index j 19 | public T[,] DistanceMatrix(Func distance) 20 | { 21 | var nodes = Nodes.ToList(); 22 | var distances = new T[Nodes.MaxNodeId+1,Nodes.MaxNodeId+1]; 23 | Parallel.ForEach(nodes,n1=>{ 24 | foreach(var n2 in nodes){ 25 | distances[n1.Id,n2.Id]=distance(n1,n2); 26 | } 27 | }); 28 | return distances; 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Vlad Kemsekov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /GraphSharp/Common/Interfaces/IPath.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace GraphSharp.Common; 7 | /// 8 | /// Path finding algorithm result 9 | /// 10 | public interface IPath : IList 11 | { 12 | /// 13 | /// Type of path. Can be directed or undirected. Also directness can be defined on out edges or on in edges. 14 | /// 15 | PathType PathType{get;} 16 | /// 17 | /// A list of nodes, which order defines a path 18 | /// 19 | IList Path{get;} 20 | /// 21 | /// Path cost 22 | /// 23 | double Cost{get;} 24 | } 25 | 26 | /// 27 | /// Path finder 28 | /// 29 | public interface IPathFinder{ 30 | /// 31 | /// Type of path. Can be directed or undirected. Also directness can be defined on out edges or on in edges. 32 | /// 33 | PathType PathType{get;} 34 | /// Path between two given nodes 35 | IPath GetPath(int node1, int node2); 36 | } -------------------------------------------------------------------------------- /GraphSharp.Tests/GraphSharp.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | false 5 | 6 | 7 | 8 | 9 | 10 | runtime; build; native; contentfiles; analyzers; buildtransitive 11 | all 12 | 13 | 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | all 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /GraphSharp/Edges/IEdge.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using GraphSharp.Common; 5 | using GraphSharp.Extensions; 6 | namespace GraphSharp; 7 | 8 | /// 9 | /// Interface for all edges. 10 | /// 11 | public interface IEdge : IComparable, ICloneable 12 | { 13 | /// 14 | /// Get or set edge property 15 | /// 16 | object this[string propertyName]{get;set;} 17 | /// 18 | /// Edge properties 19 | /// 20 | IDictionary Properties{get;} 21 | /// 22 | /// Id of a source node of this edge 23 | /// 24 | int SourceId { get; set; } 25 | /// 26 | /// Id of a target node of this edge 27 | /// 28 | int TargetId { get; set; } 29 | int IComparable.CompareTo(IEdge? other) 30 | { 31 | if (other is null) 32 | return 1; 33 | var d1 = SourceId - other.SourceId; 34 | var d2 = TargetId - other.TargetId; 35 | if (d1 == 0) return d2; 36 | return d1; 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/FindComponents.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using GraphSharp.Common; 4 | namespace GraphSharp.Graphs; 5 | 6 | public partial class ImmutableGraphOperation 7 | where TNode : INode 8 | where TEdge : IEdge 9 | { 10 | /// 11 | /// Finds all unconnected components of a graph 12 | /// 13 | /// Class that can be used to get components and determine of two nodes in the same component 14 | public ComponentsResult FindComponents() 15 | { 16 | UnionFind u = new(Nodes.MaxNodeId + 1); 17 | foreach (var n in Nodes) 18 | u.MakeSet(n.Id); 19 | foreach (var e in Edges) 20 | u.UnionSet(e.SourceId, e.TargetId); 21 | 22 | var result = new Dictionary>(u.SetsCount+1); 23 | foreach(var n in Nodes){ 24 | var set = u.FindSet(n.Id); 25 | if(result.TryGetValue(set,out var list)) 26 | list.Add(n); 27 | else 28 | result[set] = new List(){n}; 29 | } 30 | return new (result.Values.ToArray(), u); 31 | } 32 | } -------------------------------------------------------------------------------- /GraphSharp/GraphSharp.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.002.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GraphSharp", "GraphSharp.csproj", "{541C0104-983B-4C24-8CF2-37463FAEB025}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {541C0104-983B-4C24-8CF2-37463FAEB025}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {541C0104-983B-4C24-8CF2-37463FAEB025}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {541C0104-983B-4C24-8CF2-37463FAEB025}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {541C0104-983B-4C24-8CF2-37463FAEB025}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {3B6739B5-FCE9-4708-BEAC-4238661BDFA2} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /GraphSharp/Propagators/Implementations/Propagator.cs: -------------------------------------------------------------------------------- 1 | using GraphSharp.Common; 2 | using GraphSharp.Graphs; 3 | using GraphSharp.Visitors; 4 | namespace GraphSharp.Propagators; 5 | 6 | /// 7 | /// Single threaded implementation.
8 | /// 9 | ///
10 | public class Propagator : PropagatorBase 11 | where TEdge : IEdge 12 | { 13 | /// 14 | public Propagator(IImmutableEdgeSource edges,IVisitor visitor, int maxNodeId = -1) : base(edges, visitor,maxNodeId) 15 | { 16 | } 17 | /// 18 | protected override void PropagateNodes() 19 | { 20 | byte state = 0; 21 | for (int nodeId = 0; nodeId < NodeStates.Length; ++nodeId) 22 | { 23 | state = NodeStates.GetState(nodeId); 24 | if (ByteStatesHandler.IsInState(UsedNodeStates.ToVisit,state)) 25 | PropagateNode(nodeId,state); 26 | }; 27 | for (int nodeId = 0; nodeId < NodeStates.Length; ++nodeId) 28 | { 29 | if (NodeStates.IsInState(UsedNodeStates.Visited,nodeId)) 30 | Visitor.Visit(nodeId); 31 | }; 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /GraphSharp/GraphStructures/Implementations/GraphStructureOperation.cs: -------------------------------------------------------------------------------- 1 | using GraphSharp.Common; 2 | using GraphSharp.Propagators; 3 | using GraphSharp.Visitors; 4 | 5 | namespace GraphSharp.Graphs; 6 | 7 | /// 8 | /// Contains graph algorithms. 9 | /// 10 | public partial class GraphOperation : ImmutableGraphOperation 11 | where TNode : INode 12 | where TEdge : IEdge 13 | { 14 | /// 15 | /// Source graph 16 | /// 17 | public new IGraph StructureBase{get;} 18 | /// 19 | /// Graph nodes 20 | /// 21 | public new INodeSource Nodes => StructureBase.Nodes; 22 | /// 23 | /// Graph edges 24 | /// 25 | public new IEdgeSource Edges => StructureBase.Edges; 26 | /// 27 | /// Graph configuration 28 | /// 29 | public new IGraphConfiguration Configuration => StructureBase.Configuration; 30 | /// 31 | public GraphOperation(IGraph structureBase) : base(structureBase) 32 | { 33 | StructureBase = structureBase; 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /GraphSharp/GraphStructures/Interfaces/IGraphConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace GraphSharp.Graphs; 3 | 4 | /// 5 | /// A set of methods and properties that used in graph 6 | /// 7 | public interface IGraphConfiguration 8 | { 9 | /// 10 | /// that used to implement's any logic when some algorithm requires random values 11 | /// 12 | public Random Rand { get; set; } 13 | /// 14 | /// Creates a edges source that works as storage for edges 15 | /// 16 | IEdgeSource CreateEdgeSource(); 17 | /// 18 | /// Creates a nodes source that works as storage for nodes 19 | /// 20 | INodeSource CreateNodeSource(); 21 | /// 22 | /// Method that used to create instance of from it's as argument 23 | /// 24 | TNode CreateNode(int nodeId); 25 | /// 26 | /// Method that used to create new from two nodes of type 27 | /// 28 | TEdge CreateEdge(TNode source, TNode target); 29 | } -------------------------------------------------------------------------------- /GraphSharp.Tests/GraphSharp.Tests.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.002.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GraphSharp.Tests", "GraphSharp.Tests.csproj", "{AB5FF099-EA61-428E-A5A1-0C782330619C}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {AB5FF099-EA61-428E-A5A1-0C782330619C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {AB5FF099-EA61-428E-A5A1-0C782330619C}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {AB5FF099-EA61-428E-A5A1-0C782330619C}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {AB5FF099-EA61-428E-A5A1-0C782330619C}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {3653BAF8-2702-4652-A304-A853B174EB58} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /GraphSharp/GraphStructures/Interfaces/IImmutableGraph.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace GraphSharp.Graphs; 7 | /// 8 | /// Immutable graph 9 | /// 10 | public interface IImmutableGraph 11 | where TNode : INode 12 | where TEdge : IEdge 13 | { 14 | /// 15 | /// Graph nodes 16 | /// 17 | IImmutableNodeSource Nodes { get; } 18 | /// 19 | /// Graph edges 20 | /// 21 | IImmutableEdgeSource Edges { get; } 22 | /// 23 | /// Graph configuration 24 | /// 25 | IGraphConfiguration Configuration { get; } 26 | /// 27 | /// Graph operations object that required to perform operations on a graph. Contains a lot of methods to do various tasks. 28 | /// 29 | ImmutableGraphOperation Do { get; } 30 | /// 31 | /// Graph converter. If you need to convert current graph to different representations or initialize current graph from different representations then look at this objects methods. 32 | /// 33 | ImmutableGraphConverters Converter { get; } 34 | } -------------------------------------------------------------------------------- /GraphSharp/Adapters/EdgeAdapter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using GraphSharp.Graphs; 3 | 4 | namespace GraphSharp.Adapters; 5 | 6 | /// 7 | /// Adapter for edges from GraphSharp to work as edges from QuikGraph 8 | /// 9 | public struct EdgeAdapter : QuikGraph.IEdge, QuikGraph.IUndirectedEdge 10 | where TEdge : IEdge 11 | { 12 | /// 13 | public int Source => GraphSharpEdge.SourceId; 14 | /// 15 | public int Target => GraphSharpEdge.TargetId; 16 | /// 17 | /// Original GraphSharp's edge 18 | /// 19 | public TEdge GraphSharpEdge { get; } 20 | /// 21 | /// Creates a new edge adapter out of GraphSharp edge 22 | /// 23 | /// 24 | public EdgeAdapter(TEdge edge) 25 | { 26 | GraphSharpEdge = edge; 27 | } 28 | /// 29 | public override bool Equals(object? obj) 30 | { 31 | if (obj is EdgeAdapter e) 32 | { 33 | return e.GraphSharpEdge.Equals(GraphSharpEdge); 34 | } 35 | return base.Equals(obj); 36 | } 37 | /// 38 | public override int GetHashCode() 39 | { 40 | return GraphSharpEdge.GetHashCode(); 41 | } 42 | } -------------------------------------------------------------------------------- /GraphSharp/GraphStructures/Interfaces/IImmutableEdgeSource.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | namespace GraphSharp.Graphs; 3 | 4 | /// 5 | /// Represents edges storage object 6 | /// 7 | public interface IImmutableEdgeSource : IEnumerable 8 | { 9 | /// 10 | /// Whatever parallel edges allowed 11 | /// 12 | bool AllowParallelEdges{get;} 13 | /// All out edges 14 | IEnumerable OutEdges(int sourceId); 15 | /// All in edges 16 | IEnumerable InEdges(int targetId); 17 | /// Both in and out edges. If you need to get both of edges this method will be faster. 18 | (IEnumerable OutEdges, IEnumerable InEdges) BothEdges(int nodeId); 19 | /// A list of combined in and out edges 20 | IEnumerable InOutEdges(int nodeId); 21 | /// 22 | /// Get first found edge with same and 23 | /// 24 | TEdge this[int sourceId, int targetId] { get; } 25 | /// 26 | /// Get first found edge with same and 27 | /// 28 | TEdge this[INode source, INode target] { get; } 29 | } -------------------------------------------------------------------------------- /GraphSharp/GraphSharp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | enable 4 | true 5 | Library 6 | net6.0 7 | MIT 8 | 3.1.2 9 | Kemsekov.GraphSharp 10 | True 11 | $(Version) 12 | Kemsekov 13 | https://github.com/Kemsekov/GraphSharp 14 | 6 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/MakeComplete.cs: -------------------------------------------------------------------------------- 1 | namespace GraphSharp.Graphs; 2 | 3 | public partial class GraphOperation 4 | where TNode : INode 5 | where TEdge : IEdge 6 | { 7 | /// 8 | /// Ensures that graph is complete by adding missing edges. Produces bidirected graph. 9 | /// 10 | public GraphOperation MakeComplete(){ 11 | foreach(var n1 in Nodes){ 12 | foreach(var n2 in Nodes){ 13 | if(n1.Id==n2.Id) continue; 14 | if(Edges.Contains(n1.Id,n2.Id)) continue; 15 | var toAdd = Configuration.CreateEdge(n1,n2); 16 | Edges.Add(toAdd); 17 | } 18 | } 19 | return this; 20 | } 21 | /// 22 | /// Ensures that subgraph containing given nodes is a complete graph. Creates a clique out of given nodes. 23 | /// 24 | public GraphOperation MakeComplete(params int[] nodes){ 25 | foreach(var n1 in nodes){ 26 | foreach(var n2 in nodes){ 27 | if(n1==n2) continue; 28 | if(Edges.Contains(n1,n2)) continue; 29 | var toAdd = Configuration.CreateEdge(Nodes[n1],Nodes[n2]); 30 | Edges.Add(toAdd); 31 | } 32 | } 33 | return this; 34 | } 35 | } -------------------------------------------------------------------------------- /GraphSharp/Common/Implementations/ObjectPool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | 5 | namespace GraphSharp.Common; 6 | /// 7 | /// Objects pool that helps to store many objects and reuse them when need 8 | /// 9 | public class ObjectPool 10 | { 11 | private readonly ConcurrentBag _objects; 12 | private readonly Func _objectGenerator; 13 | /// 14 | /// Creates a new instance of object pool 15 | /// 16 | /// Function to create object 17 | public ObjectPool(Func objectGenerator) 18 | { 19 | _objectGenerator = objectGenerator ?? throw new ArgumentNullException(nameof(objectGenerator)); 20 | _objects = new ConcurrentBag(); 21 | } 22 | /// 23 | /// Retrieves a object from pool 24 | /// 25 | public T Get() => _objects.TryTake(out T? item) ? item : _objectGenerator(); 26 | /// 27 | /// Returns a back to pool 28 | /// 29 | public void Return(T item) => _objects.Add(item); 30 | /// 31 | /// Returns many objects back to pool 32 | /// 33 | public void Return(IEnumerable items){ 34 | foreach(var i in items) 35 | Return(i); 36 | } 37 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/ColorNodes.cs: -------------------------------------------------------------------------------- 1 | namespace GraphSharp.Graphs; 2 | 3 | 4 | public partial class ImmutableGraphOperation 5 | where TNode : INode 6 | where TEdge : IEdge 7 | { 8 | /// 9 | /// Apply linear-time greedy graph nodes coloring algorithm.
10 | ///
11 | public ColoringResult GreedyColorNodes() 12 | { 13 | return new GreedyColoring(Nodes, Edges).Color(); 14 | } 15 | /// 16 | /// Slightly different implementation of DSatur coloring algorithm.
17 | /// A lot better than greedy algorithm and just about a half of it's speed. 18 | ///
19 | public ColoringResult DSaturColorNodes() 20 | { 21 | return new DSaturColoring(Nodes, Edges).Color(); 22 | } 23 | /// 24 | /// Recursive largest first algorithm. The most efficient in colors used algorithm, 25 | /// but the slowest one. 26 | /// 27 | public ColoringResult RLFColorNodes() 28 | { 29 | return new RLFColoring(Nodes, Edges).Color(); 30 | } 31 | /// 32 | /// QuikGraph's coloring algorithm 33 | /// 34 | public ColoringResult QuikGraphColorNodes() 35 | { 36 | return new QuickGraphColoring(Nodes, Edges).Color(); 37 | } 38 | } -------------------------------------------------------------------------------- /GraphSharp/Propagators/Implementations/ParallelPropagator.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using GraphSharp.Common; 3 | using GraphSharp.Graphs; 4 | using GraphSharp.Visitors; 5 | namespace GraphSharp.Propagators; 6 | 7 | /// 8 | /// Concurrent implementation.
9 | /// Every that accompany this propagator must be implemented 10 | /// as thread-safe one. 11 | /// 12 | ///
13 | public class ParallelPropagator : PropagatorBase 14 | where TEdge : IEdge 15 | { 16 | /// 17 | public ParallelPropagator(IImmutableEdgeSource edges,IVisitor visitor, int maxNodeId = -1) : base(edges, visitor,maxNodeId) 18 | { 19 | } 20 | /// 21 | protected override void PropagateNodes() 22 | { 23 | Parallel.For(0, NodeStates.Length, nodeId => 24 | { 25 | var state = NodeStates.GetState(nodeId); 26 | if (ByteStatesHandler.IsInState(UsedNodeStates.ToVisit,state)) 27 | PropagateNode(nodeId,state); 28 | }); 29 | Parallel.For(0, NodeStates.Length, nodeId => 30 | { 31 | if (NodeStates.IsInState(UsedNodeStates.Visited,nodeId)) 32 | Visitor.Visit(nodeId); 33 | }); 34 | } 35 | } -------------------------------------------------------------------------------- /GraphSharp/Common/Implementations/Components.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace GraphSharp.Common; 7 | /// 8 | /// Result of components finder algorithms 9 | /// 10 | /// 11 | public class ComponentsResult : IDisposable 12 | { 13 | /// 14 | public ComponentsResult(IEnumerable[] components, UnionFind setFinder) 15 | { 16 | this.Components = components; 17 | this.SetFinder = setFinder; 18 | } 19 | /// 20 | /// An array of components, where each component contains a set of nodes it contains 21 | /// 22 | public IEnumerable[] Components { get; } 23 | /// 24 | /// Set finder that stores relationships between nodes. If two nodes in same set they in same component. 25 | /// 26 | public UnionFind SetFinder { get; } 27 | 28 | /// 29 | public void Dispose() 30 | { 31 | SetFinder.Dispose(); 32 | } 33 | /// 34 | /// Function that helps to determine whatever two nodes in same component 35 | /// 36 | public bool InSameComponent(int nodeId1,int nodeId2){ 37 | return SetFinder.FindSet(nodeId1)==SetFinder.FindSet(nodeId2); 38 | } 39 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/MakeSources.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using GraphSharp.Visitors; 4 | namespace GraphSharp.Graphs; 5 | 6 | public partial class GraphOperation 7 | where TNode : INode 8 | where TEdge : IEdge 9 | { 10 | /// 11 | /// Will create sources on nodes with id equal to nodeIndices.
12 | /// In other words after this method used any possible path in a reversed graph 13 | /// will land on one of the nodes you specified.
14 | ///
15 | /// 16 | public GraphOperation MakeSources(params int[] nodeIndices) 17 | { 18 | if (nodeIndices.Count() == 0 || Nodes.Count == 0) return this; 19 | 20 | foreach (var i in nodeIndices) 21 | if (i > Nodes.MaxNodeId) 22 | throw new ArgumentException("nodeIndex is out of range"); 23 | var sourceCreator = new SourceCreator(StructureBase); 24 | 25 | foreach (var n in nodeIndices) 26 | { 27 | foreach (var inEdge in Edges.InEdges(n).ToArray()) 28 | { 29 | Edges.Remove(inEdge.SourceId, n); 30 | } 31 | } 32 | 33 | sourceCreator.SetPosition(nodeIndices); 34 | while (!sourceCreator.Done) 35 | { 36 | sourceCreator.Propagate(); 37 | } 38 | return this; 39 | 40 | } 41 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/AlgorithmBase.cs: -------------------------------------------------------------------------------- 1 | using GraphSharp.Graphs; 2 | 3 | namespace GraphSharp.Algorithms; 4 | 5 | /// 6 | /// Base class for all algorithms on immutable graphs 7 | /// 8 | public abstract class ImmutableAlgorithmBase 9 | where TNode : INode 10 | where TEdge : IEdge 11 | { 12 | /// 13 | public ImmutableAlgorithmBase(IImmutableNodeSource nodes, IImmutableEdgeSource edges) 14 | { 15 | Nodes = nodes; 16 | Edges = edges; 17 | } 18 | /// 19 | /// Nodes used in algorithm 20 | /// 21 | public IImmutableNodeSource Nodes { get; } 22 | /// 23 | /// Edges used in algorithm 24 | /// 25 | public IImmutableEdgeSource Edges { get; } 26 | } 27 | 28 | /// 29 | /// Base class for all algorithms 30 | /// 31 | public abstract class AlgorithmBase 32 | where TNode : INode 33 | where TEdge : IEdge 34 | { 35 | /// 36 | public AlgorithmBase(INodeSource nodes, IEdgeSource edges) 37 | { 38 | Nodes = nodes; 39 | Edges = edges; 40 | } 41 | /// 42 | /// Nodes used in algorithm 43 | /// 44 | public INodeSource Nodes { get; } 45 | /// 46 | /// Edges used in algorithm 47 | /// 48 | public IEdgeSource Edges { get; } 49 | } -------------------------------------------------------------------------------- /GraphSharp/Nodes/Node.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | using MathNet.Numerics.LinearAlgebra.Single; 3 | using GraphSharp.Common; 4 | using System.Collections.Generic; 5 | using System.Collections.Concurrent; 6 | using GraphSharp.Extensions; 7 | namespace GraphSharp; 8 | 9 | /// 10 | /// Default implementation. 11 | /// 12 | public class Node : INode 13 | { 14 | /// 15 | public static Color DefaultColor = Color.Brown; 16 | /// 17 | public int Id { get; set; } 18 | 19 | /// 20 | public IDictionary Properties{get;init;} 21 | 22 | /// 23 | public object this[string propertyName] { get => Properties[propertyName]; set => Properties[propertyName]=value; } 24 | 25 | /// 26 | public Node(int id) 27 | { 28 | Id = id; 29 | Properties = new Dictionary(); 30 | } 31 | /// 32 | public override string ToString() 33 | { 34 | return $"Node {Id}"; 35 | } 36 | 37 | /// 38 | public Node Clone() 39 | { 40 | return new Node(Id) 41 | { 42 | Properties=this.Properties.Clone() 43 | }; 44 | } 45 | 46 | INode ICloneable.Clone() => Clone(); 47 | 48 | /// 49 | public bool Equals(INode? other) 50 | { 51 | return other?.Id == Id; 52 | } 53 | } -------------------------------------------------------------------------------- /GraphSharp/Visitors/BaseClasses/VisitorWithPropagator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using GraphSharp.Graphs; 3 | using GraphSharp.Propagators; 4 | namespace GraphSharp.Visitors; 5 | 6 | /// 7 | /// Base implementation of and proxy of in one instance. 8 | /// 9 | public abstract class VisitorWithPropagator : VisitorBase, IPropagator 10 | where TEdge : IEdge 11 | { 12 | /// 13 | /// implementation that used for this proxy class 14 | /// 15 | public abstract PropagatorBase Propagator { get; } 16 | /// 17 | /// Function that used to propagate any exploration algorithm 18 | /// 19 | public void Propagate() 20 | { 21 | Propagator.Propagate(); 22 | } 23 | 24 | /// 25 | /// Changes current exploration position 26 | /// 27 | public void SetPosition(params int[] nodeIndices) 28 | { 29 | Propagator.SetPosition(nodeIndices); 30 | } 31 | /// 32 | /// Resets propagator and whole exploration algorithm with new graph and visitor 33 | /// 34 | public void Reset(IImmutableEdgeSource edges, IVisitor visitor, int maxNodeId = -1) 35 | { 36 | Propagator.Reset(edges,visitor,maxNodeId); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /GraphSharp/Algorithms/IndependentSet/IndependentSetResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace GraphSharp.Graphs; 6 | 7 | /// 8 | /// Result of independent set finders algorithms 9 | /// 10 | public class IndependentSetResult : IEnumerable, IDisposable 11 | { 12 | const byte Added = 1; 13 | RentedArray NodeState { get; } 14 | /// 15 | /// Nodes in given independent set 16 | /// 17 | public IEnumerable Nodes { get; } 18 | /// 19 | /// 20 | public IndependentSetResult(RentedArray nodeState, IEnumerable nodes) 21 | { 22 | NodeState = nodeState; 23 | Nodes = nodes; 24 | } 25 | /// 26 | /// Determine whatever given node is in given independent set 27 | /// 28 | /// 29 | /// 30 | public bool IsAdded(int nodeId) => (NodeState[nodeId] & Added) == Added; 31 | /// 32 | 33 | public IEnumerator GetEnumerator() 34 | { 35 | return Nodes.GetEnumerator(); 36 | } 37 | 38 | IEnumerator IEnumerable.GetEnumerator() 39 | { 40 | return ((IEnumerable)Nodes).GetEnumerator(); 41 | } 42 | /// 43 | public void Dispose() 44 | { 45 | NodeState.Dispose(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /GraphSharp/Algorithms/Coloring/RLFColoring.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | namespace GraphSharp.Graphs; 4 | 5 | /// 6 | /// Recursive largest first algorithm. The most efficient in colors used algorithm, 7 | /// but the slowest one. 8 | /// 9 | public class RLFColoring : ColoringAlgorithmBase 10 | where TNode : INode 11 | where TEdge : IEdge 12 | { 13 | /// 14 | public RLFColoring(IImmutableNodeSource nodes, IImmutableEdgeSource edges) : base(nodes, edges) 15 | { 16 | } 17 | /// 18 | public override ColoringResult Color() 19 | { 20 | int coloredNodesCount = 0; 21 | int colorIndex = 1; 22 | var colors = ArrayPoolStorage.RentArray(Nodes.MaxNodeId+1); 23 | while (coloredNodesCount != Nodes.Count()) 24 | { 25 | var S = FindMaximalIndependentSet(colors,x => colors[x.Id]==0); 26 | var count = S.Count(); 27 | coloredNodesCount += count; 28 | foreach (var node in S) 29 | colors[node.Id] = colorIndex; 30 | colorIndex++; 31 | } 32 | return new(colors); 33 | } 34 | 35 | private IndependentSetResult FindMaximalIndependentSet(RentedArray colors, Predicate condition) 36 | { 37 | using var alg = new BallardMyerIndependentSet(Nodes,Edges,x => colors[x.Id]==0); 38 | return alg.Find(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /GraphSharp/Visitors/Implementations/AnyPathFinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using GraphSharp.Graphs; 4 | namespace GraphSharp.Visitors; 5 | 6 | /// 7 | /// Finds any first found paths between start node and all other nodes 8 | /// 9 | public class AnyPathFinder : PathFinderBase 10 | where TNode : INode 11 | where TEdge : IEdge 12 | { 13 | /// 14 | /// Creates a new instance of 15 | /// 16 | public AnyPathFinder(int startNodeId, IImmutableGraph graph, PathType pathType) : base(graph,pathType) 17 | { 18 | this.StartNodeId = startNodeId; 19 | } 20 | /// 21 | /// Clears all internal path information stored and resets path finding 22 | /// 23 | public void Clear(int startNodeId, int endNodeId) 24 | { 25 | this.StartNodeId = startNodeId; 26 | Path.Fill(-1); 27 | Done = false; 28 | } 29 | /// 30 | protected override bool SelectImpl(EdgeSelect edge) 31 | { 32 | (var sourceId,var targetId) = (edge.SourceId,edge.TargetId); 33 | if (Path[targetId] == -1) 34 | { 35 | Path[targetId] = sourceId; 36 | return true; 37 | } 38 | return false; 39 | } 40 | /// 41 | protected override void VisitImpl(int node) 42 | { 43 | DidSomething = true; 44 | } 45 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/ContractEdge.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | namespace GraphSharp.Graphs; 3 | 4 | public partial class GraphOperation 5 | where TNode : INode 6 | where TEdge : IEdge 7 | { 8 | /// 9 | /// Contract edge. Target node will be merged with source node so only source node will remain. If there is n 10 | /// 11 | /// True if successfully contracted edge, else false. 12 | public GraphOperation ContractEdge(int sourceId, int targetId) 13 | { 14 | //With edge contraction we need to remove edge we contracting first 15 | Edges.Remove(sourceId, targetId); 16 | Edges.Remove(targetId,sourceId); 17 | 18 | //we will be merging everything into source 19 | var source = Nodes[sourceId]; 20 | 21 | var targetEdges = Edges.OutEdges(targetId).ToArray(); 22 | var toMove = Edges.InEdges(targetId).ToArray(); 23 | 24 | //move target edges to became source edges 25 | foreach (var e in targetEdges) 26 | { 27 | Edges.Move(e,sourceId,e.TargetId); 28 | } 29 | 30 | //move target sources to because source sources (all edges that look like A->targetId became A->sourceId) 31 | foreach (var e in toMove) 32 | { 33 | Edges.Move(e.SourceId,targetId,e.SourceId,sourceId); 34 | } 35 | 36 | //after merging complete remove merged node 37 | Nodes.Remove(targetId); 38 | return this; 39 | } 40 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/Coloring/ColoringAlgorithmBase.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | 3 | namespace GraphSharp.Graphs; 4 | 5 | /// 6 | /// Base class for coloring algorithms 7 | /// 8 | public abstract class ColoringAlgorithmBase : Algorithms.ImmutableAlgorithmBase 9 | where TNode : INode 10 | where TEdge : IEdge 11 | { 12 | /// 13 | protected ColoringAlgorithmBase(IImmutableNodeSource nodes, IImmutableEdgeSource edges) : base(nodes, edges) 14 | { 15 | } 16 | /// 17 | /// Performs graph nodes coloring 18 | /// 19 | /// Coloring result 20 | public abstract ColoringResult Color(); 21 | /// Id of available color 22 | protected int GetAvailableColor(int nodeId, RentedArray colors) 23 | { 24 | var neighborsColors = Edges.Neighbors(nodeId).Select(x=>colors[x]).ToList(); 25 | return Enumerable.Range(1,neighborsColors.Max()+1).Except(neighborsColors).First(); 26 | } 27 | /// 28 | /// Assigns first available color 29 | /// 30 | protected void AssignColor(int nodeId, RentedArray colors) 31 | { 32 | colors[nodeId] = GetAvailableColor(nodeId, colors); 33 | } 34 | /// Amount of neighbors that have different colors 35 | protected int DegreeOfSaturation(int nodeId, RentedArray colors) 36 | { 37 | return Edges.Neighbors(nodeId).DistinctBy(x => colors[x]).Count(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/ConnectRandomly.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | namespace GraphSharp.Graphs; 3 | 4 | public partial class GraphOperation 5 | where TNode : INode 6 | where TEdge : IEdge 7 | { 8 | /// 9 | /// Randomly create some range of edges for each node, so each node have more or equal than minEdgesCount but than less maxEdgesCount edges. 10 | /// 11 | /// Min count of edges for each node 12 | /// Max count of edges for each node 13 | public GraphOperation ConnectRandomly(int minEdgesCount, int maxEdgesCount) 14 | { 15 | minEdgesCount = minEdgesCount < 0 ? 0 : minEdgesCount; 16 | maxEdgesCount = maxEdgesCount > Nodes.Count ? Nodes.Count : maxEdgesCount; 17 | 18 | //swap using xor 19 | if (minEdgesCount > maxEdgesCount) 20 | { 21 | minEdgesCount = minEdgesCount ^ maxEdgesCount; 22 | maxEdgesCount = minEdgesCount ^ maxEdgesCount; 23 | minEdgesCount = minEdgesCount ^ maxEdgesCount; 24 | } 25 | 26 | var availableNodes = Nodes.Select(x => x.Id).ToList(); 27 | 28 | foreach (var node in Nodes) 29 | { 30 | int edgesCount = Configuration.Rand.Next(minEdgesCount, maxEdgesCount); 31 | var startIndex = Configuration.Rand.Next(Nodes.Count); 32 | ConnectNodeToNodes(node, startIndex, edgesCount, availableNodes); 33 | } 34 | return this; 35 | } 36 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/TransitiveClosureOnRadius.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using GraphSharp.Common; 4 | 5 | namespace GraphSharp.Graphs; 6 | 7 | public partial class GraphOperation 8 | where TNode : INode 9 | where TEdge : IEdge 10 | { 11 | // TODO: add tests for it 12 | /// 13 | /// Produce transitive closure on some radius.
14 | /// When we have a path like a -> b -> c -> d and calling this method with radius = 1 15 | /// we will create a edges: a -> c, b -> d.
16 | /// Param radius = 2 will just repeat this process twice, so in this 17 | /// case it will create edge a -> d as addition to already created edges 18 | ///
19 | public GraphOperation TransitiveClosureOnRadius(int radius) 20 | { 21 | var edgesToAdd = new List(); 22 | for (int i = 0; i < radius; i++) 23 | { 24 | edgesToAdd.Clear(); 25 | foreach (var n in Nodes) 26 | { 27 | foreach (var edge in Edges.OutEdges(n.Id)) 28 | { 29 | foreach (var edge2 in Edges.OutEdges(edge.TargetId)){ 30 | var n2 = Nodes[edge2.TargetId]; 31 | if(!Edges.Contains(n.Id,n2.Id)) 32 | edgesToAdd.Add(Configuration.CreateEdge(n, n2)); 33 | } 34 | } 35 | } 36 | foreach (var e in edgesToAdd) Edges.Add(e); 37 | } 38 | return this; 39 | } 40 | } -------------------------------------------------------------------------------- /GraphSharp/Common/Interfaces/IStatesHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace GraphSharp.Common; 7 | /// 8 | /// States handler 9 | /// 10 | public interface IStatesHandler 11 | { 12 | /// 13 | /// Adds state to all given objects 14 | /// 15 | void AddState(TState state,TObject[] objects); 16 | /// 17 | /// Adds states to all known objects 18 | /// 19 | void AddStateToAll(TState state); 20 | /// 21 | /// Removes state from all given objects 22 | /// 23 | void RemoveState(TState state,TObject[] objects); 24 | /// 25 | /// Removes state from all known objects 26 | /// 27 | void RemoveStateFromAll(TState state); 28 | /// 29 | /// Checks if given object is in given state 30 | /// 31 | bool IsInState(TState state,TObject obj); 32 | /// 33 | /// Sets state to all given objects.
34 | /// It differs from adding states in a way, that all other states are removed. 35 | ///
36 | void SetState(TState state, TObject[] objects); 37 | /// 38 | /// Sets state to all known objects.
39 | /// It differs from adding states in a way, that all other states are removed. 40 | ///
41 | void SetStateToAll(TState state); 42 | /// A state of given object 43 | TState GetState(TObject obj); 44 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/Coloring/DSaturColoring.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | namespace GraphSharp.Graphs; 3 | 4 | /// 5 | /// Slightly different implementation of DSatur coloring algorithm.
6 | /// A lot better than greedy algorithm and just about a half of it's speed. 7 | ///
8 | public class DSaturColoring : ColoringAlgorithmBase 9 | where TNode : INode 10 | where TEdge : IEdge 11 | { 12 | /// 13 | public DSaturColoring(IImmutableNodeSource nodes, IImmutableEdgeSource edges) : base(nodes, edges) 14 | { 15 | } 16 | /// 17 | public override ColoringResult Color() 18 | { 19 | var colors = ArrayPoolStorage.RentArray(Nodes.MaxNodeId + 1); 20 | var order = Nodes.OrderBy(x => -Edges.Neighbors(x.Id).Count()); 21 | 22 | int coloredNodesCount = 0; 23 | foreach (var n in order) 24 | { 25 | if (colors[n.Id] != 0) continue; 26 | var toColor = n.Id; 27 | while (coloredNodesCount != Nodes.Count()) 28 | { 29 | AssignColor(toColor, colors); 30 | coloredNodesCount++; 31 | var neighbors = 32 | Edges.Neighbors(toColor) 33 | .Where(x => colors[x] == 0) 34 | .ToList(); 35 | if (neighbors.Count == 0) 36 | break; 37 | toColor = neighbors.MaxBy(x => DegreeOfSaturation(x, colors)); 38 | } 39 | } 40 | return new(colors); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /GraphSharp/GraphStructures/Interfaces/IGraph{}.cs: -------------------------------------------------------------------------------- 1 | namespace GraphSharp.Graphs; 2 | 3 | /// 4 | /// Graph structure interface. 5 | /// 6 | public interface IGraph : IImmutableGraph 7 | where TNode : INode 8 | where TEdge : IEdge 9 | { 10 | /// 11 | /// Graph nodes 12 | /// 13 | new INodeSource Nodes { get; } 14 | IImmutableNodeSource IImmutableGraph.Nodes => Nodes; 15 | /// 16 | /// Graph edges 17 | /// 18 | new IEdgeSource Edges { get; } 19 | IImmutableEdgeSource IImmutableGraph.Edges => Edges; 20 | /// 21 | /// Graph operations object that required to perform operations on a graph. Contains a lot of methods to do various tasks. 22 | /// 23 | new GraphOperation Do { get; } 24 | ImmutableGraphOperation IImmutableGraph.Do => Do; 25 | /// 26 | /// Graph converter. If you need to convert current graph to different representations or initialize current graph from different representations then look at this objects methods. 27 | /// 28 | new GraphConverters Converter { get; } 29 | ImmutableGraphConverters IImmutableGraph.Converter => Converter; 30 | /// 31 | /// Changes graph elements sources. 32 | /// 33 | Graph SetSources(INodeSource? nodes = null, IEdgeSource? edges = null); 34 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/PlanarRender.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using MathNet.Numerics.LinearAlgebra; 4 | using GraphSharp.Common; 5 | 6 | namespace GraphSharp.Graphs; 7 | 8 | public partial class ImmutableGraphOperation 9 | where TNode : INode 10 | where TEdge : IEdge 11 | { 12 | /// 13 | /// Moves nodes positions in such a way, that if given graph is planar it will produce such nodes 14 | /// coordinates, that when rendered would not have edge intersections 15 | /// 16 | /// 17 | /// Fixed positions. 18 | /// Algorithm will fix these nodes into right shape and render 19 | /// everything else inside of given nodes 20 | /// Dictionary, where key is node id, and value is node position 21 | public IDictionary> PlanarRender(int[] fixedNodes) 22 | { 23 | var p = new PlanarGraphRender(StructureBase,fixedNodes); 24 | while(p.ComputeStep()) ; 25 | return p.Positions; 26 | } 27 | /// 28 | /// 29 | /// Start positions count. 30 | /// Algorithm will try to find a cycle of given size and select them as fixed nodes, 31 | /// set them into right shape and render everything else inside of found nodes. 32 | /// 33 | public IDictionary> PlanarRender(int cycleSize){ 34 | var p = new PlanarGraphRender(StructureBase,cycleSize); 35 | while(p.ComputeStep()) ; 36 | return p.Positions; 37 | } 38 | } -------------------------------------------------------------------------------- /GraphSharp/GraphStructures/Implementations/BaseEdgeSource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using GraphSharp.Exceptions; 7 | 8 | namespace GraphSharp.Graphs; 9 | 10 | /// 11 | /// Base class for edge source. Implement basic functionality by abstract methods. 12 | /// 13 | public abstract class BaseEdgeSource : IImmutableEdgeSource 14 | where TEdge : IEdge 15 | { 16 | /// 17 | public bool AllowParallelEdges{get;init;} 18 | /// 19 | public int Count { get; protected set; } 20 | /// 21 | public bool IsReadOnly => true; 22 | /// 23 | public TEdge this[int sourceId, int targetId] 24 | => OutEdges(sourceId).FirstOrDefault(x => x.TargetId == targetId) ?? 25 | throw new EdgeNotFoundException($"Edge {sourceId} -> {targetId} not found."); 26 | /// 27 | public TEdge this[INode source, INode target] => this[source.Id, target.Id]; 28 | /// 29 | public abstract IEnumerable OutEdges(int sourceId); 30 | /// 31 | public abstract IEnumerable InEdges(int targetId); 32 | /// 33 | public abstract (IEnumerable OutEdges, IEnumerable InEdges) BothEdges(int nodeId); 34 | /// 35 | public abstract IEnumerable InOutEdges(int nodeId); 36 | /// 37 | public abstract IEnumerator GetEnumerator(); 38 | 39 | IEnumerator IEnumerable.GetEnumerator() 40 | { 41 | return this.GetEnumerator(); 42 | } 43 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/FindSourceRepresentativeSet.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | namespace GraphSharp.Graphs; 4 | 5 | public partial class ImmutableGraphOperation 6 | where TNode : INode 7 | where TEdge : IEdge 8 | { 9 | // TODO: ADD TEST 10 | /// 11 | /// Identifies a representative set of nodes from the source strongly connected 12 | /// components (SCCs) of the graph. 13 | /// 14 | /// 15 | /// This method first condenses the graph into its SCC condensation graph (a DAG). 16 | /// From this condensation graph, it selects all source components, i.e., components 17 | /// with no incoming edges. For each such source component, the method yields its 18 | /// original nodes. 19 | /// 20 | /// The resulting set of node groups represents a Source Representative Set, 21 | /// meaning that by choosing one node from each group, every other node in the 22 | /// original graph is guaranteed to be reachable. 23 | /// 24 | /// 25 | /// A collection of node groups, where each group corresponds to the set of original 26 | /// nodes contained in a source SCC of the condensed graph. The caller may select 27 | /// one representative node from each group to construct a minimal reachability set. 28 | /// 29 | public IEnumerable> FindSourceRepresentativeSet() 30 | { 31 | var condensed_graph = CondenseSCC(); 32 | var sources = condensed_graph.Nodes.Where(n => condensed_graph.Edges.IsSource(n.Id)).Select(c=>c.Component.Nodes); 33 | return sources; 34 | } 35 | } -------------------------------------------------------------------------------- /GraphSharp/Visitors/BaseClasses/EdgeSelect.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace GraphSharp.Common; 7 | /// 8 | /// Struct that used to message a movement from one node to another by some edge 9 | /// 10 | public struct EdgeSelect 11 | where TEdge : IEdge 12 | { 13 | /// Edge which used to move 14 | /// 15 | /// Source id - nodeId that was a predecessor for calling this edge. 16 | /// Other part of the edge is basically a node we trying to move. 17 | /// 18 | public EdgeSelect(TEdge edge, int sourceId) 19 | { 20 | Edge = edge; 21 | SourceId = sourceId; 22 | TargetId = Edge.Other(sourceId); 23 | } 24 | /// 25 | /// Underlying edge that being selected 26 | /// 27 | public TEdge Edge; 28 | /// 29 | /// Source id 30 | /// 31 | public int SourceId{get;} 32 | /// 33 | /// Target id 34 | /// 35 | public int TargetId{get;} 36 | /// 37 | /// Other part of the edge, or -1 if not found 38 | /// 39 | public int Other(int nodeId) => Edge.Other(nodeId); 40 | /// True if edges connect same nodes, without taking their directness into accountants 41 | public bool ConnectsSame(IEdge edge){ 42 | return Edge.ConnectsSame(edge); 43 | } 44 | /// 45 | /// Converts to 46 | /// 47 | public static implicit operator TEdge(EdgeSelect e) => e.Edge; 48 | } -------------------------------------------------------------------------------- /GraphSharp/GraphStructures/Implementations/GraphConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace GraphSharp.Graphs; 3 | 4 | /// 5 | /// Default implementation that uses default configuration. 6 | /// 7 | public class GraphConfiguration : IGraphConfiguration 8 | where TNode : INode 9 | where TEdge : IEdge 10 | { 11 | Func createEdge; 12 | Func createNode; 13 | /// 14 | /// Random that used by algorithms when needed 15 | /// 16 | public Random Rand { get; set; } 17 | /// 18 | /// Initialize new graph configuration 19 | /// 20 | /// Random that will be used to do graph algorithms 21 | /// How to create edge 22 | /// How to create node 23 | public GraphConfiguration(Random rand, Func createEdge, Func createNode) 24 | { 25 | this.createEdge = createEdge; 26 | this.createNode = createNode; 27 | Rand = rand; 28 | } 29 | /// 30 | public TEdge CreateEdge(TNode source, TNode target) => createEdge(source, target); 31 | /// 32 | public TNode CreateNode(int nodeId) => createNode(nodeId); 33 | /// 34 | public IEdgeSource CreateEdgeSource() 35 | { 36 | return new DefaultEdgeSource(); 37 | } 38 | /// 39 | public INodeSource CreateNodeSource() 40 | { 41 | return new DefaultNodeSource(); 42 | } 43 | 44 | 45 | } -------------------------------------------------------------------------------- /GraphSharp/GraphStructures/Extensions/ICollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace System.Linq; 7 | /// 8 | /// Contains collection extensions 9 | /// 10 | public static class ICollectionExtensions 11 | { 12 | /// 13 | /// Removes all elements that satisfies given predicate 14 | /// 15 | /// Count of elements removed 16 | public static int RemoveAll(this ICollection collection, Predicate toRemove) 17 | { 18 | if (collection is List list) 19 | { 20 | return list.RemoveAll(toRemove); 21 | } 22 | List itemsToDelete = collection 23 | .Where(x => toRemove(x)) 24 | .ToList(); 25 | 26 | foreach (var item in itemsToDelete) 27 | { 28 | collection.Remove(item); 29 | } 30 | return itemsToDelete.Count; 31 | } 32 | // TODO: add test 33 | /// 34 | /// All values that share the same minimal score 35 | /// 36 | public static IList AllMinValues(this IEnumerable e, Func getMeasure) 37 | { 38 | var result = new List(); 39 | var bestScore = float.MaxValue; 40 | foreach(var n in e) 41 | { 42 | var score = getMeasure(n); 43 | 44 | if (score < bestScore) 45 | { 46 | bestScore = score; 47 | result.Clear(); 48 | } 49 | 50 | if (score == bestScore) 51 | { 52 | result.Add(n); 53 | } 54 | }; 55 | return result; 56 | } 57 | } -------------------------------------------------------------------------------- /GraphSharp.GoogleOrTools/GraphSharp.GoogleOrTools.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.002.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GraphSharp.GoogleOrTools", "GraphSharp.GoogleOrTools.csproj", "{612FB5AD-FFEF-44AA-B01E-616628DA7BBC}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GraphSharp", "..\GraphSharp\GraphSharp.csproj", "{1D445C97-D1B3-487D-9DCF-A4216D8FC263}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {612FB5AD-FFEF-44AA-B01E-616628DA7BBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {612FB5AD-FFEF-44AA-B01E-616628DA7BBC}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {612FB5AD-FFEF-44AA-B01E-616628DA7BBC}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {612FB5AD-FFEF-44AA-B01E-616628DA7BBC}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {1D445C97-D1B3-487D-9DCF-A4216D8FC263}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {1D445C97-D1B3-487D-9DCF-A4216D8FC263}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {1D445C97-D1B3-487D-9DCF-A4216D8FC263}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {1D445C97-D1B3-487D-9DCF-A4216D8FC263}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {A764D36A-348E-4BAB-825D-9B1E35E06289} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /GraphSharp/Visitors/Implementations/ActionVisitor{TNode,TEdge}.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace GraphSharp.Visitors; 3 | 4 | /// 5 | /// implementation that uses lambda functions. 6 | /// 7 | public class ActionVisitor : VisitorBase 8 | where TNode : INode 9 | where TEdge : IEdge 10 | { 11 | 12 | /// function 13 | /// function 14 | /// function. 15 | /// function. 16 | public ActionVisitor(Action? visit = null, Predicate>? select = null, Action? end = null, Action? start = null) 17 | { 18 | this.VisitEvent += visit ?? new Action(node => { }); 19 | this.Condition = select ?? new Predicate>(edge => true); 20 | this.EndEvent += end ?? new Action(() => { }); 21 | this.StartEvent += start ?? new Action(() => { }); 22 | } 23 | /// 24 | /// End function implementation 25 | /// 26 | protected override void EndImpl() 27 | { 28 | } 29 | /// 30 | /// Select function implementation 31 | /// 32 | protected override bool SelectImpl(EdgeSelect edge) 33 | { 34 | return true; 35 | } 36 | /// 37 | /// Start function implementation 38 | /// 39 | protected override void StartImpl() 40 | { 41 | } 42 | /// 43 | /// Visit function implementation 44 | /// 45 | protected override void VisitImpl(int node) 46 | { 47 | } 48 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/Arrange.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using GraphSharp.Common; 5 | using MathNet.Numerics.LinearAlgebra.Single; 6 | 7 | namespace GraphSharp.Graphs; 8 | 9 | public partial class ImmutableGraphOperation 10 | where TNode : INode 11 | where TEdge : IEdge 12 | { 13 | /// 14 | /// Arranges graph and returns graph arrangement 15 | /// 16 | /// Compute algorithm step until summary or node positions change is bigger that theta 17 | /// Count of closest to given node elements to compute repulsion from. Let it be -1 so all nodes will be used to compute repulsion 18 | /// How to measure edge weights. By default will use distance between edge endpoints. 19 | public GraphArrange Arrange(float theta, int closestCount = -1, Func? getWeight = null) 20 | { 21 | var p = new GraphArrange(StructureBase); 22 | while(p.ComputeStep()>theta) ; 23 | return p; 24 | } 25 | /// 26 | /// Arranges graph and returns graph arrangement 27 | /// 28 | /// How many iterations to run 29 | /// Count of closest to given node elements to compute repulsion from. Let it be -1 so all nodes will be used to compute repulsion 30 | /// How to measure edge weights. By default will use distance between edge endpoints. 31 | public GraphArrange Arrange(int iterations, int closestCount = -1, Func? getWeight = null) 32 | { 33 | var p = new GraphArrange(StructureBase); 34 | while(iterations-->0) p.ComputeStep(); 35 | return p; 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /GraphSharp/Edges/Edge.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using GraphSharp.Common; 5 | namespace GraphSharp; 6 | 7 | 8 | 9 | /// 10 | /// Default implementation 11 | /// 12 | public class Edge : IEdge 13 | { 14 | /// 15 | public static Color DefaultColor = Color.DarkViolet; 16 | /// 17 | public object this[string propertyName] { get => Properties[propertyName]; set => Properties[propertyName]=value; } 18 | /// 19 | public int SourceId { get; set; } 20 | /// 21 | public int TargetId { get; set; } 22 | /// 23 | public double Weight { get=>this.MapProperties().Weight; set=>this.MapProperties().Weight=value; } 24 | /// 25 | public Color Color { get=>!this.Properties.ContainsKey("color") ? DefaultColor : this.MapProperties().Color; set=>this.MapProperties().Color=value; } 26 | /// 27 | public IDictionary Properties{get;init;} 28 | /// 29 | /// Creates a new instance of edge 30 | /// 31 | public Edge(INode source, INode target) : this(source.Id,target.Id){} 32 | /// 33 | /// Creates a new instance of edge 34 | /// 35 | public Edge(int sourceId, int targetId) 36 | { 37 | SourceId = sourceId; 38 | TargetId = targetId; 39 | Properties = new Dictionary(); 40 | } 41 | /// 42 | public override string ToString() 43 | { 44 | return $"Edge {SourceId}->{TargetId}"; 45 | } 46 | /// 47 | public virtual IEdge Clone() 48 | { 49 | return new Edge(SourceId, TargetId) 50 | { 51 | Weight = this.Weight, 52 | Color = this.Color, 53 | }; 54 | } 55 | } -------------------------------------------------------------------------------- /GraphSharp.GoogleOrTools/Extensions.cs: -------------------------------------------------------------------------------- 1 | 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Google.OrTools.LinearSolver; 5 | 6 | /// 7 | /// Google or tools lp extensions 8 | /// 9 | public static class GoogleOrToolsVariableArrayExtensions 10 | { 11 | /// 12 | /// 13 | public static LinearExpr Sum(this IEnumerable exp) 14 | { 15 | var sum = exp.First() * 1; 16 | foreach (var e in exp.Skip(1)) 17 | sum += e; 18 | return sum; 19 | } 20 | /// 21 | /// 22 | public static LinearExpr Sum(this IEnumerable exp) 23 | { 24 | var sum = exp.First() * 1; 25 | foreach (var e in exp.Skip(1)) 26 | sum += e; 27 | return sum; 28 | } 29 | /// 30 | /// 31 | public static Google.OrTools.Sat.LinearExpr Sum(this IEnumerable exp) 32 | { 33 | var sum = exp.First() * 1; 34 | foreach (var e in exp.Skip(1)) 35 | sum += e; 36 | return sum; 37 | } 38 | /// 39 | /// Computes dot product between arrays 40 | /// 41 | public static LinearExpr Dot(this Variable[] variables, double[] arr) 42 | { 43 | LinearExpr sum = variables[0] * arr[0]; 44 | for (int i = 1; i < variables.Length; i++) 45 | { 46 | sum += variables[i] * arr[i]; 47 | } 48 | return sum; 49 | } 50 | /// 51 | /// Computes dot product between arrays 52 | /// 53 | public static LinearExpr Dot(this LinearExpr[] variables, double[] arr) 54 | { 55 | LinearExpr sum = variables[0] * arr[0]; 56 | for (int i = 1; i < variables.Length; i++) 57 | { 58 | sum += variables[i] * arr[i]; 59 | } 60 | return sum; 61 | } 62 | } -------------------------------------------------------------------------------- /SimplestExample/Program.cs: -------------------------------------------------------------------------------- 1 | using GraphSharp; 2 | using GraphSharp.Graphs; 3 | using MathNet.Numerics.LinearAlgebra.Single; 4 | using SixLabors.ImageSharp; 5 | using SixLabors.ImageSharp.Formats.Tga; 6 | using SysColor = System.Drawing.Color; 7 | 8 | 9 | System.Console.WriteLine("Creaing graph..."); 10 | Graph graph = new(); 11 | 12 | graph.Do.CreateNodes(4); 13 | graph.Nodes[0].MapProperties().Position = new DenseVector(new[] { 0.0f, 0.0f }); 14 | graph.Nodes[1].MapProperties().Position = new DenseVector(new[] { 1.0f, 0.0f }); 15 | graph.Nodes[2].MapProperties().Position = new DenseVector(new[] { 0.0f, 1.0f }); 16 | graph.Nodes[3].MapProperties().Position = new DenseVector(new[] { 1.0f, 1.0f }); 17 | 18 | graph.Edges.Add(new Edge(0, 1) { Color = SysColor.Red}); 19 | graph.Edges.Add(new Edge(1, 2) { Color = SysColor.Red }); 20 | graph.Edges.Add(new Edge(2, 0) { Color = SysColor.Red }); 21 | 22 | graph.Edges.Add(new Edge(3, 1) { Color = SysColor.Yellow }); 23 | graph.Edges.Add(new Edge(2, 3) { Color = SysColor.Yellow }); 24 | 25 | graph.Do.MakeBidirected(); 26 | 27 | foreach(var c in graph.Do.FindCyclesBasis()) 28 | { 29 | System.Console.WriteLine(string.Join(' ', c.Select(c => c.Id.ToString()))); 30 | } 31 | 32 | System.Console.WriteLine("Creating Image..."); 33 | using var image = ImageSharpShapeDrawer.CreateImage(graph, drawer => 34 | { 35 | drawer.Clear(SysColor.Black); 36 | drawer.DrawEdgesParallel(graph.Edges, 0.01, color: SysColor.Azure); 37 | drawer.DrawDirectionsParallel(graph.Edges, 0.01,0.5, color: SysColor.Blue); 38 | drawer.DrawNodesParallel(graph.Nodes, 0.012, color: SysColor.Red); 39 | drawer.DrawNodeIds(graph.Nodes, SysColor.White, 0.01); 40 | }, 41 | x => (Vector)(x.MapProperties().Position*0.9f+0.05f), 42 | outputResolution: 2000 43 | ); 44 | 45 | System.Console.WriteLine("Saving Image..."); 46 | image.SaveAsJpeg("example.jpg"); 47 | -------------------------------------------------------------------------------- /GraphSharp/Common/Implementations/KruskalForest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace GraphSharp.Common; 7 | /// 8 | /// Result of kruskal algorithm 9 | /// 10 | public class KruskalForest : IDisposable, IForest 11 | where TEdge : IEdge 12 | { 13 | /// 14 | public KruskalForest(UnionFind treeFinder,RentedArray degree, IList forest) 15 | { 16 | this.TreeFinder = treeFinder; 17 | this.Degree = degree; 18 | this.Forest = forest; 19 | } 20 | /// 21 | public KruskalForest(IList forest,int nodesCount){ 22 | this.Forest = forest; 23 | this.Degree = ArrayPoolStorage.RentArray(nodesCount); 24 | this.TreeFinder = new UnionFind(nodesCount); 25 | foreach(var e in forest){ 26 | Degree[e.SourceId]+=1; 27 | Degree[e.TargetId]+=1; 28 | TreeFinder.UnionSet(e.SourceId,e.TargetId); 29 | } 30 | } 31 | /// 32 | /// Set finder that helps to determine if two nodes in same tree 33 | /// 34 | /// 35 | public UnionFind TreeFinder { get; } 36 | /// 37 | /// Allows to get degree of node in computed forest 38 | /// 39 | public RentedArray Degree { get; } 40 | /// 41 | /// A list of edges that forms a forest 42 | /// 43 | public IList Forest { get; } 44 | IEnumerable IForest.Forest => Forest; 45 | /// 46 | /// Helps to determine if two given nodes in the same tree 47 | /// 48 | public bool InSameComponent(int nodeId1, int nodeId2){ 49 | return TreeFinder.FindSet(nodeId1) == TreeFinder.FindSet(nodeId2); 50 | } 51 | /// 52 | public void Dispose() 53 | { 54 | TreeFinder.Dispose(); 55 | Degree.Dispose(); 56 | } 57 | 58 | int IForest.Degree(int nodeId) 59 | { 60 | return Degree[nodeId]; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /GraphSharp/Algorithms/KruskalAlgorithm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | namespace GraphSharp.Graphs; 4 | 5 | /// 6 | /// Basic kruskal algorithm implementation 7 | /// 8 | /// 9 | /// 10 | public class KruskalAlgorithm 11 | where TNode : INode 12 | where TEdge : IEdge 13 | { 14 | /// Graph nodes 15 | /// Graph edges 16 | public KruskalAlgorithm(IImmutableNodeSource nodes, IEnumerable edges) 17 | { 18 | Nodes = nodes; 19 | Edges = edges; 20 | } 21 | /// 22 | /// Graph nodes 23 | /// 24 | public IImmutableNodeSource Nodes { get; } 25 | /// 26 | /// Graph edges 27 | /// 28 | public IEnumerable Edges { get; } 29 | /// 30 | /// Apply Kruskal algorithm on set edges. 31 | /// 32 | /// Max node degree limiter 33 | /// Kruskal forest 34 | public KruskalForest Find(Func maxDegree) 35 | { 36 | using UnionFind unionFind = new(Nodes.MaxNodeId + 1); 37 | using var degree = ArrayPoolStorage.RentArray(Nodes.MaxNodeId + 1); 38 | var outputEdges = new List(); 39 | foreach (var n in Nodes) 40 | unionFind.MakeSet(n.Id); 41 | int sourceId = 0, targetId = 0; 42 | foreach (var edge in Edges) 43 | { 44 | sourceId = edge.SourceId; 45 | targetId = edge.TargetId; 46 | if (unionFind.FindSet(sourceId) == unionFind.FindSet(targetId)) 47 | continue; 48 | if (degree[sourceId] + 1 > maxDegree(Nodes[sourceId]) || degree[targetId] + 1 > maxDegree(Nodes[targetId])) 49 | continue; 50 | outputEdges.Add(edge); 51 | degree[sourceId]++; 52 | degree[targetId]++; 53 | unionFind.UnionSet(sourceId, targetId); 54 | } 55 | return new(unionFind, degree, outputEdges); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /GraphSharp/GraphStructures/Implementations/GraphStructure.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace GraphSharp.Graphs; 5 | 6 | /// 7 | /// Graph structure class. 8 | /// 9 | public class Graph : IGraph 10 | where TNode : INode 11 | where TEdge : IEdge 12 | { 13 | /// 14 | public IGraphConfiguration Configuration { get; protected set; } 15 | /// 16 | public INodeSource Nodes { get; protected set; } 17 | /// 18 | public IEdgeSource Edges { get; protected set; } 19 | 20 | /// 21 | /// Create new graph with specified nodes and edges creation functions 22 | /// 23 | public Graph(Func createNode, Func createEdge) 24 | : this(new GraphConfiguration(new Random(), createEdge, createNode)) 25 | { 26 | } 27 | 28 | /// 29 | /// Just init new graph with empty Nodes and Edges using given configuration. 30 | /// 31 | /// 32 | public Graph(IGraphConfiguration configuration) 33 | { 34 | Configuration = configuration; 35 | Nodes = configuration.CreateNodeSource(); 36 | Edges = configuration.CreateEdgeSource(); 37 | Do = new GraphOperation(this); 38 | } 39 | 40 | /// 41 | /// Copy constructor. Will make shallow copy of Graph 42 | /// 43 | public Graph(IGraph Graph) 44 | { 45 | Nodes = Graph.Nodes; 46 | Edges = Graph.Edges; 47 | Configuration = Graph.Configuration; 48 | Do = new GraphOperation(this); 49 | } 50 | 51 | /// 52 | public Graph SetSources(INodeSource? nodes = null, IEdgeSource? edges = null) 53 | { 54 | Nodes = nodes ?? Nodes; 55 | Edges = edges ?? Edges; 56 | return this; 57 | } 58 | 59 | /// 60 | public GraphOperation Do{get;} 61 | /// 62 | public GraphConverters Converter => new(this); 63 | 64 | } -------------------------------------------------------------------------------- /GraphSharp/Common/Implementations/UnionFind.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace GraphSharp.Common; 5 | 6 | /// 7 | /// Disjoint-set data structure 8 | /// https://en.wikipedia.org/wiki/Disjoint-set_data_structure 9 | /// 10 | public class UnionFind : IDisposable 11 | { 12 | RentedArray parent; 13 | RentedArray rank; 14 | /// 15 | /// Total count of sets in the union find 16 | /// 17 | /// 18 | public int SetsCount => parent.Distinct().Count(); 19 | /// Max element index in union set 20 | public UnionFind(int maxSetSize) 21 | { 22 | parent = ArrayPoolStorage.RentArray(maxSetSize); 23 | rank = ArrayPoolStorage.RentArray(maxSetSize); 24 | } 25 | /// 26 | public void Dispose(){ 27 | parent.Dispose(); 28 | rank.Dispose(); 29 | } 30 | /// 31 | /// Assigns new set for given element 32 | /// 33 | public void MakeSet(int v) 34 | { 35 | parent[v] = v; 36 | rank[v] = 0; 37 | } 38 | /// 39 | /// Finds a set of given element. If two elements returns same id it means they in same set 40 | /// 41 | /// Set id 42 | public int FindSet(int v) 43 | { 44 | if (v == parent[v]) 45 | return v; 46 | return parent[v] = FindSet(parent[v]); 47 | } 48 | /// 49 | /// Helps to determine if two objects in same set 50 | /// 51 | /// True if two objects in same set 52 | public bool SameSet(int a, int b) => 53 | FindSet(a)==FindSet(b); 54 | /// 55 | /// Unions two object to be in same set 56 | /// 57 | public void UnionSet(int a, int b) 58 | { 59 | a = FindSet(a); 60 | b = FindSet(b); 61 | if (a != b) 62 | { 63 | if (rank[a] < rank[b]) 64 | { 65 | a ^= b; 66 | b = a ^ b; 67 | a ^= b; 68 | } 69 | parent[b] = a; 70 | if (rank[a] == rank[b]) 71 | ++rank[a]; 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/MathUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using MIConvexHull; 5 | 6 | namespace GraphSharp.Algorithms; 7 | 8 | /// 9 | /// Math utils class 10 | /// 11 | public class MathUtils 12 | { 13 | /// 14 | /// N-dimensional delaunay triangulation 15 | /// 16 | public static IEnumerable<(double[] v, double[] u)> DelaunayND(IEnumerable points, double planeDistanceTolerance = 0.001) 17 | { 18 | var verts = points.Select(v => new DefaultVertex() { Position = v }).ToList(); 19 | var indices = new Dictionary(); 20 | foreach (var (p, index) in points.Select((point, index) => (point, index))) 21 | { 22 | indices[p] = index; 23 | } 24 | //key is source, value is target. Source key is always < target key 25 | var edges = new Dictionary>(); 26 | 27 | foreach (var v in verts) 28 | edges[v.Position] = new(); 29 | var dims = points.First().Length; 30 | 31 | //this delaunay triangulation algorithm only provides results as a set of simplexes. 32 | //so we need to convert them to edges manually 33 | var delaunay = DelaunayTriangulation>.Create(verts, planeDistanceTolerance); 34 | foreach (var cell in delaunay.Cells) 35 | { 36 | for (int i = 0; i <= dims; i++) 37 | for (int j = i + 1; j <= dims; j++) 38 | { 39 | var v1 = cell.Vertices[i]; 40 | var v2 = cell.Vertices[j]; 41 | 42 | var index1 = indices[v1.Position]; 43 | var index2 = indices[v2.Position]; 44 | 45 | var source = (index1 < index2 ? v1 : v2).Position; 46 | var target = (index1 >= index2 ? v1 : v2).Position; 47 | if (edges[source].Contains(target)) continue; 48 | edges[source].Add(target); 49 | } 50 | } 51 | foreach (var pair in edges) 52 | foreach (var val in pair.Value) 53 | yield return (pair.Key, val); 54 | 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/DelaunayTriangulation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using MathNet.Numerics.LinearAlgebra.Single; 6 | using MIConvexHull; 7 | 8 | namespace GraphSharp.Graphs; 9 | class DelaunayVertex : DefaultVertex{ 10 | public DelaunayVertex(object node) 11 | { 12 | Node = node; 13 | } 14 | public object Node{get;set;} 15 | } 16 | public partial class GraphOperation 17 | where TNode : INode 18 | where TEdge : IEdge 19 | { 20 | /// 21 | public GraphOperation DelaunayTriangulation(Func getPos, double planeDistanceTolerance = 1e-15){ 22 | return DelaunayTriangulation(n=>getPos(n).Select(c=>(double)c).ToArray(),planeDistanceTolerance); 23 | } 24 | /// 25 | /// Removes all edges from graph then 26 | /// preforms delaunay triangulation. See https://en.wikipedia.org/wiki/Delaunay_triangulation
27 | /// Works on any number of dimensions 28 | ///
29 | public GraphOperation DelaunayTriangulation(Func getPos, double planeDistanceTolerance = 1e-15) 30 | { 31 | var verts = Nodes.Select(v => new DelaunayVertex(v) { Position = getPos(v) }).ToList(); 32 | var dims = verts.First().Position.Length; 33 | 34 | //this delaunay triangulation algorithm only provides results as a set of simplexes. 35 | //so we need to convert them to edges manually 36 | var delaunay = DelaunayTriangulation>.Create(verts, planeDistanceTolerance); 37 | 38 | foreach (var cell in delaunay.Cells) 39 | { 40 | for (int i = 0; i <= dims; i++) 41 | for (int j = i + 1; j <= dims; j++) 42 | { 43 | var v1 = (TNode)cell.Vertices[i].Node; 44 | var v2 = (TNode)cell.Vertices[j].Node; 45 | if(Edges.BetweenOrDefault(v1.Id,v2.Id) != null) continue; 46 | 47 | var edge =Configuration.CreateEdge(v1,v2); 48 | Edges.Add(edge); 49 | } 50 | } 51 | return this; 52 | } 53 | } -------------------------------------------------------------------------------- /GraphSharp.GoogleOrTools/MinCostFlowResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace GraphSharp.Graphs; 6 | /// 7 | /// Min cost max flow algorithm result 8 | /// 9 | public class MinCostFlowResult where TEdge : IEdge 10 | { 11 | /// 12 | /// Total flow that goes through graph 13 | /// 14 | public double MaxFlow{get;} 15 | /// 16 | /// Optimal cost computed 17 | /// 18 | public double OptimalCost { get; } 19 | /// 20 | /// Capacities used in current max flow result 21 | /// 22 | public IDictionary Capacities { get; } 23 | /// 24 | /// ResidualCapacities in current max flow result 25 | /// 26 | /// 27 | public double ResidualCapacities(TEdge e)=>Capacities[e]-Flow[e]; 28 | /// 29 | /// Flow that goes trough edge 30 | /// 31 | public IDictionary Flow { get; } 32 | /// 33 | /// Cost of one flow unit going trough edge 34 | /// 35 | public Dictionary UnitCost { get; } 36 | /// 37 | /// Cost of flow going trough edge = UnitCost * Flow of edge 38 | /// 39 | public double Cost(TEdge edge)=>UnitCost[edge]*Flow[edge]; 40 | /// 41 | /// Supply of flow at node 42 | /// 43 | public Dictionary Supply { get; } 44 | /// 45 | /// Creates new min cost flow result 46 | /// 47 | public MinCostFlowResult( 48 | IEnumerable nodes, 49 | IEnumerable edges, 50 | double maxFlow, 51 | double optimalCost, 52 | Func capacities, 53 | Func unitCost, 54 | Func flow, 55 | Func nodeFlowSupply) 56 | { 57 | MaxFlow = maxFlow; 58 | OptimalCost = optimalCost; 59 | Capacities = edges.ToDictionary(x=>x,x=>capacities(x)); 60 | Flow = edges.ToDictionary(x=>x,x=>flow(x)); 61 | UnitCost = edges.ToDictionary(x=>x,x=>unitCost(x)); 62 | Supply = nodes.ToDictionary(x=>x,x=>nodeFlowSupply(x)); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /GraphSharp/Algorithms/IndependentSet/MaximalIndependentSetAlgorithmBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace GraphSharp.Graphs; 4 | 5 | /// 6 | /// Base class for maximal independent set algorithms 7 | /// 8 | public abstract class IndependentSetAlgorithmBase : Algorithms.ImmutableAlgorithmBase, IDisposable 9 | where TNode : INode 10 | where TEdge : IEdge 11 | { 12 | #pragma warning disable 13 | protected const byte Added = 1; 14 | protected const byte AroundAdded = 2; 15 | protected const byte Forbidden = 4; 16 | protected RentedArray nodeState; 17 | protected RentedArray freeNeighbors; 18 | protected RentedArray countOfForbiddenNeighbors; 19 | protected RentedArray countOfColoredNeighbors; 20 | #pragma warning enable 21 | /// 22 | protected IndependentSetAlgorithmBase(IImmutableNodeSource nodes, IImmutableEdgeSource edges) : base(nodes, edges) 23 | { 24 | nodeState = ArrayPoolStorage.RentArray(Nodes.MaxNodeId + 1); 25 | freeNeighbors = ArrayPoolStorage.RentArray(Nodes.MaxNodeId + 1); 26 | countOfForbiddenNeighbors = ArrayPoolStorage.RentArray(Nodes.MaxNodeId + 1); 27 | countOfColoredNeighbors = ArrayPoolStorage.RentArray(Nodes.MaxNodeId + 1); 28 | } 29 | #pragma warning disable 30 | protected bool IsAdded(int nodeId) => (nodeState[nodeId] & Added) == Added; 31 | protected bool IsForbidden(int nodeId) => (nodeState[nodeId] & Forbidden) == Forbidden; 32 | protected bool IsAroundAdded(int nodeId) => (nodeState[nodeId] & AroundAdded) == AroundAdded; 33 | protected int CountOfUncoloredNeighbors(int nodeId) 34 | { 35 | int result = 0; 36 | foreach (var n in Edges.Neighbors(nodeId)) 37 | if (nodeState[n] == 0) result++; 38 | return result; 39 | } 40 | #pragma warning enable 41 | 42 | /// 43 | /// Finds maximal independent set 44 | /// 45 | public abstract IndependentSetResult Find(); 46 | 47 | /// 48 | void IDisposable.Dispose() 49 | { 50 | nodeState.Dispose(); 51 | freeNeighbors.Dispose(); 52 | countOfForbiddenNeighbors.Dispose(); 53 | countOfColoredNeighbors.Dispose(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /GraphSharp/GraphStructures/Extensions/EdgeNodeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace GraphSharp; 4 | 5 | /// 6 | /// Node extensions 7 | /// 8 | public static class EdgeNodeExtensions{ 9 | /// 10 | /// Other part of edge, or if not found 11 | /// 12 | public static int Other(this IEdge edge,int nodeId) 13 | { 14 | if (edge.SourceId == nodeId) 15 | return edge.TargetId; 16 | if (edge.TargetId == nodeId) 17 | return edge.SourceId; 18 | return -1; 19 | } 20 | /// True if edges connect same nodes, without taking their directness into accountants 21 | public static bool ConnectsSame(this IEdge current,IEdge edge){ 22 | return current.TargetId==edge.TargetId && current.SourceId==edge.SourceId || current.SourceId==edge.TargetId && current.TargetId==edge.SourceId; 23 | } 24 | /// 25 | /// Get edge property 26 | /// 27 | public static T Get(this IEdge edge,string name){ 28 | return (T)edge.Properties[name]; 29 | } 30 | /// 31 | /// Get node property 32 | /// 33 | public static T Get(this INode node,string name){ 34 | return (T)node.Properties[name]; 35 | } 36 | /// Properties mapper that can be used to map object properties to specific types 37 | public static NodePropertiesMap MapProperties(this INode n){ 38 | return new(n); 39 | } 40 | /// Properties mapper that can be used to map object properties to specific types 41 | public static EdgePropertiesMap MapProperties(this IEdge e){ 42 | return new(e); 43 | } 44 | /// 45 | /// Convenient method to set properties of edge 46 | /// 47 | public static TEdge With(this TEdge e,Action mapSetter) 48 | where TEdge : IEdge 49 | { 50 | mapSetter(e.MapProperties()); 51 | return e; 52 | } 53 | /// 54 | /// Convenient method to set properties of node 55 | /// 56 | public static TNode With(this TNode e,Action mapSetter) 57 | where TNode : INode 58 | { 59 | mapSetter(e.MapProperties()); 60 | return e; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/FindEccentricity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using GraphSharp.Propagators; 4 | using GraphSharp.Visitors; 5 | namespace GraphSharp.Graphs; 6 | 7 | public partial class ImmutableGraphOperation 8 | where TNode : INode 9 | where TEdge : IEdge 10 | { 11 | 12 | /// Node from which we need to find eccentricity 13 | /// Determine how to find a eccentricity of a node. By default it uses edges weights, but you can change it. 14 | /// Length of a longest shortest path for a given node and endpoint of that path. 15 | public (double length, TNode farthestNode) FindEccentricity(int nodeId, Func? getWeight = null) 16 | { 17 | return FindEccentricityBase( 18 | nodeId, 19 | v=>GetPropagator(v), 20 | getWeight 21 | ); 22 | } 23 | /// Node from which we need to find eccentricity 24 | /// Determine how to find a eccentricity of a node. By default it uses edges weights, but you can change it. 25 | /// Length of a longest shortest path for a given node and endpoint of that path. 26 | public (double length, TNode farthestNode) FindEccentricityParallel(int nodeId, Func? getWeight = null) 27 | { 28 | return FindEccentricityBase( 29 | nodeId, 30 | v=>GetParallelPropagator(v), 31 | getWeight 32 | ); 33 | } 34 | 35 | (double length, TNode farthestNode) FindEccentricityBase(int nodeId,Func,IPropagator> createPropagator, Func? getWeight = null){ 36 | getWeight ??= x=>x.MapProperties().Weight; 37 | var pathFinder = new ShortestPathsLengthFinderAlgorithms(nodeId, StructureBase){GetWeight = getWeight}; 38 | var propagator = createPropagator(pathFinder); 39 | propagator.SetPosition(nodeId); 40 | while (!pathFinder.Done) 41 | { 42 | propagator.Propagate(); 43 | } 44 | var p = pathFinder.PathLength.Select((length, index) => (length, index)).MaxBy(x => x.length); 45 | ReturnPropagator(propagator); 46 | return (p.length, Nodes[p.index]); 47 | } 48 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/FindCyclesBasis.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | namespace GraphSharp.Graphs; 8 | 9 | public partial class ImmutableGraphOperation 10 | where TNode : INode 11 | where TEdge : IEdge 12 | { 13 | /// 14 | /// Finds fundamental cycles basis. 15 | /// See https://en.wikipedia.org/wiki/Cycle_basis#Fundamental_cycles 16 | /// 17 | /// A list of paths that forms a fundamental cycles basis 18 | public IEnumerable> FindCyclesBasis() 19 | { 20 | var treeGraph = new Graph(Configuration); 21 | treeGraph.SetSources(Nodes, Configuration.CreateEdgeSource()); 22 | { 23 | using var tree = FindSpanningForestKruskal(); 24 | foreach (var e in tree.Forest) 25 | { 26 | treeGraph.Edges.Add(e); 27 | if (Edges.TryGetEdge(e.TargetId, e.SourceId, out var bidirected)) 28 | { 29 | if (bidirected is not null) 30 | treeGraph.Edges.Add(bidirected); 31 | } 32 | } 33 | } 34 | 35 | // debug stuff 36 | // foreach (var e in treeGraph.Edges) 37 | // System.Console.WriteLine($"{e.SourceId} {e.TargetId}"); 38 | // System.Console.WriteLine("-------------"); 39 | // foreach(var e in Edges) 40 | // System.Console.WriteLine($"{e.SourceId} {e.TargetId}"); 41 | // System.Console.WriteLine("-------------"); 42 | 43 | var outsideEdges = Edges.Except(treeGraph.Edges); 44 | outsideEdges=outsideEdges.DistinctBy(v => (Math.Min(v.SourceId, v.TargetId), Math.Max(v.SourceId, v.TargetId))); 45 | var result = new ConcurrentBag>(); 46 | Parallel.ForEach(outsideEdges, e => 47 | { 48 | var path = treeGraph.Do.FindAnyPath(e.TargetId, e.SourceId).Path; 49 | if (path.Count() != 0) 50 | { 51 | var p = path.Prepend(StructureBase.GetSource(e)).ToList(); 52 | result.Add(new PathResult(x=>StructureBase.ComputePathCost(x),p,PathType.OutEdges)); 53 | } 54 | }); 55 | return result; 56 | } 57 | } -------------------------------------------------------------------------------- /GraphSharp/Visitors/Implementations/SourceCreator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using GraphSharp.Common; 5 | using GraphSharp.Graphs; 6 | using GraphSharp.Propagators; 7 | namespace GraphSharp.Visitors; 8 | 9 | /// 10 | /// This visitor will create sources on assigned points so any path in graph will ends on some of this points 11 | /// 12 | public class SourceCreator : VisitorWithPropagator 13 | where TNode : INode 14 | where TEdge : IEdge 15 | { 16 | 17 | const byte Proceed = 16; 18 | const byte ToRemove = 32; 19 | ByteStatesHandler NodeStates => Propagator.NodeStates; 20 | /// 21 | public override PropagatorBase Propagator { get; } 22 | /// 23 | /// Graph that used by this class 24 | /// 25 | public IGraph Graph { get; } 26 | /// 27 | /// Creates a new instance of source creator 28 | /// 29 | public SourceCreator(IGraph graph) 30 | { 31 | Propagator = new ParallelPropagator(graph.Edges,this,graph.Nodes.MaxNodeId); 32 | this.Graph = graph; 33 | } 34 | 35 | /// 36 | protected override void StartImpl() 37 | { 38 | DidSomething = false; 39 | } 40 | /// 41 | protected override bool SelectImpl(EdgeSelect edge) 42 | { 43 | return !NodeStates.IsInState(Proceed | ToRemove,edge.TargetId); 44 | } 45 | 46 | /// 47 | protected override void VisitImpl(int nodeId) 48 | { 49 | NodeStates.AddState(Proceed,nodeId); 50 | 51 | var edges = Graph.Edges.OutEdges(nodeId); 52 | var toRemove = new List(edges.Count()); 53 | foreach (var edge in edges) 54 | { 55 | if (NodeStates.IsInState(ToRemove,edge.TargetId)) 56 | toRemove.Add(edge); 57 | } 58 | lock(Graph) 59 | foreach (var edge in toRemove) 60 | Graph.Edges.Remove(edge); 61 | 62 | DidSomething = true; 63 | } 64 | /// 65 | protected override void EndImpl() 66 | { 67 | for (int i = 0; i < Graph.Nodes.MaxNodeId + 1; i++) 68 | if (NodeStates.IsInState(Proceed,i)) 69 | NodeStates.AddState(ToRemove,i); 70 | Steps++; 71 | if(!DidSomething) Done=true; 72 | } 73 | } -------------------------------------------------------------------------------- /GraphSharp/Common/Implementations/ByteStatesHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace GraphSharp.Common; 7 | /// 8 | /// Id to byte state handler 9 | /// 10 | public class ByteStatesHandler : IStatesHandler, IDisposable 11 | { 12 | RentedArray states; 13 | /// 14 | /// Default state assigned to all objects 15 | /// 16 | public byte DefaultState = 0; 17 | /// 18 | /// Count of objects 19 | /// 20 | public int Length{get;} 21 | /// How many objects need 22 | public ByteStatesHandler(int length) 23 | { 24 | states = ArrayPoolStorage.RentArray(length); 25 | Length = length; 26 | } 27 | /// 28 | public void AddState(byte state, params int[] id) 29 | { 30 | for (int i = 0; i < id.Length; i++) 31 | states[id[i]] |= state; 32 | } 33 | /// 34 | public void AddStateToAll(byte state) 35 | { 36 | for (int i = 0; i < states.Length; i++) 37 | states[i] |= state; 38 | } 39 | 40 | /// 41 | public void SetState(byte state,params int[] id) 42 | { 43 | for (int i = 0; i < id.Length; i++) 44 | states[id[i]] = state; 45 | } 46 | /// 47 | public void SetStateToAll(byte state) 48 | { 49 | states.Fill(state); 50 | } 51 | 52 | /// 53 | public void Dispose() 54 | { 55 | states.Dispose(); 56 | } 57 | 58 | /// 59 | public byte GetState(int id) 60 | { 61 | return states[id]; 62 | } 63 | 64 | /// 65 | public bool IsInState(byte state, int id) 66 | { 67 | return (states[id] & state) == state; 68 | } 69 | 70 | /// 71 | public void RemoveState(byte state, params int[] id) 72 | { 73 | for (int i = 0; i < id.Length; i++) 74 | states[id[i]] &= (byte)~state; 75 | } 76 | /// 77 | public void RemoveStateFromAll(byte state) 78 | { 79 | for (int i = 0; i < states.Length; i++) 80 | states[i] &= (byte)~state; 81 | } 82 | /// 83 | public static bool IsInState(byte state, byte State){ 84 | return (State & state) == state; 85 | } 86 | } -------------------------------------------------------------------------------- /GraphSharp/GraphStructures/Extensions/DictionaryExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | 5 | namespace GraphSharp.Extensions; 6 | /// 7 | /// Dictionary extensions 8 | /// 9 | public static class DictionaryExtensions 10 | { 11 | /// Found value in dictionary, or default value if key not found 12 | public static TValue? GetOrDefault(this IDictionary dict, TKey key){ 13 | if(dict.TryGetValue(key,out var result)){ 14 | return result; 15 | } 16 | return default; 17 | } 18 | /// Found value in dictionary, or default value if key not found 19 | public static TValue GetOrDefault(this IDictionary dict, TKey key, TValue defaultV){ 20 | if(dict.TryGetValue(key,out var result)){ 21 | return result; 22 | } 23 | return defaultV; 24 | } 25 | /// 26 | /// Tries to make deep copy of dictionary 27 | /// 28 | public static ConcurrentDictionary CloneConcurrent(this IDictionary dict) 29 | where TKey : notnull 30 | { 31 | var result = new ConcurrentDictionary(); 32 | foreach(var pair in dict){ 33 | var key = pair.Key is ICloneable cKey ? cKey.Clone() : pair.Key; 34 | var value = pair.Value is ICloneable cValue ? cValue.Clone() : pair.Key; 35 | if(key is TKey k && value is TValue v){ 36 | result[k] = v; 37 | } 38 | else{ 39 | result[pair.Key] = pair.Value; 40 | } 41 | } 42 | return result; 43 | } 44 | /// 45 | /// Tries to make deep copy of dictionary 46 | /// 47 | public static Dictionary Clone(this IDictionary dict) 48 | where TKey : notnull 49 | { 50 | var result = new Dictionary(); 51 | foreach(var pair in dict){ 52 | var key = pair.Key is ICloneable cKey ? cKey.Clone() : pair.Key; 53 | var value = pair.Value is ICloneable cValue ? cValue.Clone() : pair.Key; 54 | if(key is TKey k && value is TValue v){ 55 | result[k] = v; 56 | } 57 | else{ 58 | result[pair.Key] = pair.Value; 59 | } 60 | } 61 | return result; 62 | } 63 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/FindLocalClusteringCoefficients.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace GraphSharp.Graphs; 7 | 8 | public partial class ImmutableGraphOperation 9 | where TNode : INode 10 | where TEdge : IEdge 11 | { 12 | /// 13 | /// Averaged local clustering coefficients. 14 | /// 15 | /// Precomputed clustering coefficients 16 | /// Array, where index is node Id and value is coefficient. -1 means this node was not present in the graph. 17 | public double[] FindAveragedLocalClusteringCoefficients(Func clusteringCoefficients) 18 | { 19 | var avgClustering = new double[Nodes.MaxNodeId+1]; 20 | 21 | Array.Fill(avgClustering,-1); 22 | foreach(var n in Nodes){ 23 | avgClustering[n.Id]=clusteringCoefficients(n.Id); 24 | } 25 | 26 | Parallel.ForEach(Nodes, n => 27 | { 28 | var neigh = Edges.Neighbors(n.Id).ToList(); 29 | var oldAvg = clusteringCoefficients(n.Id); 30 | var avg = oldAvg; 31 | foreach (var nei in neigh) 32 | { 33 | avg += clusteringCoefficients(nei); 34 | } 35 | avg /= neigh.Count + 1; 36 | avgClustering[n.Id] = avg; 37 | }); 38 | return avgClustering; 39 | } 40 | /// 41 | /// Finds local clustering coefficients 42 | /// 43 | /// Array, where index is node Id and value is coefficient. -1 means this node was not present in the graph. 44 | public RentedArray FindLocalClusteringCoefficients() 45 | { 46 | //for each node n take it's neighbors 47 | //induce graph on {n+neighborhoods} in sum N nodes 48 | //in induced graph find count of edges = K 49 | //set coeff for n equal K/(N(N-1)) 50 | 51 | var coeff = ArrayPoolStorage.RentArray(Nodes.MaxNodeId + 1); 52 | coeff.Fill(-1f); 53 | Parallel.ForEach(Nodes, n => 54 | { 55 | var toInduce = Edges.Neighbors(n.Id).Append(n.Id).ToArray(); 56 | if (toInduce.Length == 1) 57 | { 58 | coeff[n.Id] = 0; 59 | return; 60 | } 61 | var induced = Induce(toInduce); 62 | double N = toInduce.Length; 63 | double K = induced.Edges.Count; 64 | coeff[n.Id] = K / (N * (N - 1)); 65 | }); 66 | return coeff; 67 | } 68 | } -------------------------------------------------------------------------------- /GraphSharp/GraphStructures/Extensions/GraphOperationUtills.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | namespace GraphSharp.Graphs; 5 | 6 | public partial class ImmutableGraphOperation 7 | where TNode : INode 8 | where TEdge : IEdge 9 | { 10 | /// 11 | /// Creates random automorphism of graph, producing two isomorphic graphs 12 | /// 13 | /// new graph that is isomorphic to input graph and mapping of original nodes to new graph 14 | public (Graph isomorphic, Dictionary mapping) CreateRandomAutomorphism() 15 | { 16 | var sourceNodes = Nodes.Select(n=>n.Id).ToArray(); 17 | var mapped = sourceNodes.OrderBy(i=>Random.Shared.Next()).ToArray(); 18 | var mapping = sourceNodes.Zip(mapped).ToDictionary(k=>k.First,k=>k.Second); 19 | return (CreateAutomorphism(mapping),mapping); 20 | } 21 | /// 22 | /// Creates automorphism of graph, producing two isomorphic graphs 23 | /// 24 | /// 25 | /// new graph that is isomorphic to input graph and mapping of original nodes to new graph 26 | public Graph CreateAutomorphism(IDictionary mapping) 27 | { 28 | var sourceNodes = Nodes.Select(n=>n.Id).ToArray(); 29 | 30 | var isomorphic = new Graph(); 31 | foreach(var n in Nodes){ 32 | isomorphic.Nodes.Add(new Node(mapping[n.Id])); 33 | } 34 | foreach(var e in Edges){ 35 | var source = mapping[e.SourceId]; 36 | var target = mapping[e.TargetId]; 37 | isomorphic.Edges.Add(new Edge(source,target)); 38 | } 39 | return isomorphic; 40 | } 41 | /// 42 | /// Count node degrees from given edges 43 | /// 44 | public RentedArray CountDegrees(IEnumerable edges) 45 | { 46 | var result = ArrayPoolStorage.RentArray(Nodes.MaxNodeId + 1); 47 | foreach (var e in edges) 48 | { 49 | result[e.SourceId]++; 50 | result[e.TargetId]++; 51 | } 52 | return result; 53 | } 54 | /// 55 | /// Apply Kruskal algorithm on set edges. 56 | /// 57 | /// Spanning tree edges 58 | /// Maximal degree that limits tree building 59 | public KruskalForest KruskalAlgorithm(IEnumerable edges, Func maxDegree) 60 | { 61 | return new KruskalAlgorithm(Nodes,edges).Find(maxDegree); 62 | } 63 | } -------------------------------------------------------------------------------- /GraphSharp.Tests/ManualTestData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | public static class ManualTestData 4 | { 5 | public static int[][] NodesConnections = new[]{ 6 | new[]{0,4},new[]{0,1}, 7 | new[]{1,2},new[]{1,3}, 8 | new[]{1,8},new[]{2,7}, 9 | new[]{2,9},new[]{3,7}, 10 | new[]{4,5},new[]{5,0}, 11 | new[]{5,1},new[]{5,3}, 12 | new[]{5,6},new[]{6,3}, 13 | new[]{7,1},new[]{7,9}, 14 | new[]{8,9},new[]{9,3} 15 | }; 16 | public static int[][][] ExpectedOrder = 17 | new[]{ 18 | new[]{ 19 | new[]{0, 7}, 20 | new[]{1, 4, 9}, 21 | new[]{2, 3, 5, 8}, 22 | new[]{0, 1, 3, 6, 7, 9}, 23 | new[]{1, 2, 3, 4, 7, 8, 9}, 24 | new[]{1, 2, 3, 5, 7, 8, 9} 25 | }, 26 | new[]{ 27 | new[]{1,5}, 28 | new[]{0,1,2,3,6,8}, 29 | new[]{1,2,3,4,7,8,9}, 30 | new[]{1,2,3,5,7,8,9}, 31 | new[]{0,1,2,3,6,7,8,9}, 32 | new[]{1,2,3,4,7,8,9} 33 | } 34 | }; 35 | public static IDictionary> TestConnectionsList = 36 | new Dictionary>{ 37 | {1,new[]{2,4,6,13}}, 38 | {2,new[]{3,6,7,12}}, 39 | {3,new[]{4,19}}, 40 | {5,new[]{9,15,20}}, 41 | {6,new[]{12,13,18}}, 42 | {7,new[]{9,12,15}}, 43 | {8,new[]{9,12,14,16}}, 44 | {9,new[]{10}}, 45 | {10,new[]{14,17,20}}, 46 | {11,new[]{12,16,18}}, 47 | {14,new[]{16}}, 48 | {15,new[]{19}}, 49 | {16,new[]{17}} 50 | }; 51 | /// 52 | /// Values, that expected after calling method MakeSources(1,14) on graph builded on top of TestConnectionsList and made bidirected 53 | /// 54 | public static IEnumerable<(int sourceId, int[] targetren)> AfterMakeSourcesExpected = 55 | new[]{ 56 | (1, new[]{2,4,6,13}), 57 | (2, new[]{3,6,7,12}), 58 | (3, new[]{19}), 59 | (4, new[]{3}), 60 | (5, new[]{15}), 61 | (6, new[]{2,12,13,18}), 62 | (7, new[]{9,12,15}), 63 | (8, new[]{9,12,16}), 64 | (9, new[]{5,7}), 65 | (10,new[]{9,17,20}), 66 | (11,new[]{12,18}), 67 | (12,new[]{7,11}), 68 | (13,new[]{6}), 69 | (14,new[]{8,10,16}), 70 | (15,new[]{5,19}), 71 | (16,new[]{8,11,17}), 72 | (18,new[]{11}), 73 | (19,new[]{15}), 74 | (20,new[]{5}), 75 | }; 76 | } -------------------------------------------------------------------------------- /GraphSharp.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31903.59 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GraphSharp", "GraphSharp\GraphSharp.csproj", "{907C4E43-AAAE-40DB-AE4C-E2682C3A7E29}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GraphSharp.GoogleOrTools", "GraphSharp.GoogleOrTools\GraphSharp.GoogleOrTools.csproj", "{197C080F-00D8-423E-9359-666575C81D7A}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimplestExample", "SimplestExample\SimplestExample.csproj", "{789CACF2-B294-4AFC-B6AC-FE3E83A6B61D}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GraphSharp.Tests", "GraphSharp.Tests\GraphSharp.Tests.csproj", "{E3EA7321-F7E1-4174-A9A3-CA3752FB5EB5}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {907C4E43-AAAE-40DB-AE4C-E2682C3A7E29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {907C4E43-AAAE-40DB-AE4C-E2682C3A7E29}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {907C4E43-AAAE-40DB-AE4C-E2682C3A7E29}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {907C4E43-AAAE-40DB-AE4C-E2682C3A7E29}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {197C080F-00D8-423E-9359-666575C81D7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {197C080F-00D8-423E-9359-666575C81D7A}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {197C080F-00D8-423E-9359-666575C81D7A}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {197C080F-00D8-423E-9359-666575C81D7A}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {789CACF2-B294-4AFC-B6AC-FE3E83A6B61D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {789CACF2-B294-4AFC-B6AC-FE3E83A6B61D}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {789CACF2-B294-4AFC-B6AC-FE3E83A6B61D}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {789CACF2-B294-4AFC-B6AC-FE3E83A6B61D}.Release|Any CPU.Build.0 = Release|Any CPU 35 | {E3EA7321-F7E1-4174-A9A3-CA3752FB5EB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 36 | {E3EA7321-F7E1-4174-A9A3-CA3752FB5EB5}.Debug|Any CPU.Build.0 = Debug|Any CPU 37 | {E3EA7321-F7E1-4174-A9A3-CA3752FB5EB5}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {E3EA7321-F7E1-4174-A9A3-CA3752FB5EB5}.Release|Any CPU.Build.0 = Release|Any CPU 39 | EndGlobalSection 40 | EndGlobal 41 | -------------------------------------------------------------------------------- /GraphSharp.GoogleOrTools/Coloring.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Google.OrTools.Sat; 5 | 6 | namespace GraphSharp.Graphs; 7 | /// 8 | /// SAT coloring extensions 9 | /// 10 | public static class ImmutableGraphOperationColoring 11 | { 12 | //TODO: add test 13 | /// 14 | /// Computes nodes coloring up to optimal coloring using corresponding SAT problem
15 | /// If it returns with given amount of colors it means this is impossible to color graph with this amount of colors 16 | ///
17 | /// 18 | /// Max amount of colors to use 19 | /// Solve status 20 | /// Coloring 21 | public static ColoringResult SATColoring(this ImmutableGraphOperation g,int maxColors,out CpSolverStatus res) 22 | where TNode : INode 23 | where TEdge : IEdge 24 | { 25 | var model = new CpModel(); 26 | var nodeColor = new Dictionary(); 27 | foreach (var n in g.Nodes) 28 | { 29 | var variable = model.NewIntVar(0, maxColors-1, n.ToString()); 30 | nodeColor[n.Id] = variable; 31 | } 32 | var uniqueEdges = 33 | g.Edges 34 | .Select(e => (Math.Min(e.SourceId, e.TargetId), Math.Max(e.SourceId, e.TargetId))) 35 | .Distinct() 36 | .ToList(); 37 | 38 | foreach (var (source, target) in uniqueEdges) 39 | { 40 | var sourceColor = nodeColor[source]; 41 | var targetColor = nodeColor[target]; 42 | // abs(sourceColor-targetColor)>=1 43 | model.Add(sourceColor != targetColor); 44 | } 45 | 46 | //-----minimize total sum of node colors 47 | var values = nodeColor.Values.ToList(); 48 | var colorSum = values.Sum(); 49 | 50 | 51 | var solver = new CpSolver(); 52 | // solver.StringParameters = $"max_time_in_seconds:{millisecondsToRun/1000}"; 53 | solver.StringParameters = $"stop_after_first_solution: true"; 54 | 55 | res = solver.Solve(model); 56 | var arr = RentedArraySharp.ArrayPoolStorage.RentArray(g.Nodes.MaxNodeId+1); 57 | if(res == CpSolverStatus.ModelInvalid || res == CpSolverStatus.Infeasible || res == CpSolverStatus.Unknown) return new ColoringResult(arr); 58 | 59 | foreach(var n in g.Nodes){ 60 | arr[n.Id]=(int)solver.Value(nodeColor[n.Id]); 61 | } 62 | return new ColoringResult(arr); 63 | } 64 | } -------------------------------------------------------------------------------- /GraphSharp/GraphStructures/Implementations/ImmutableGraphOperation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using GraphSharp.Common; 3 | using GraphSharp.Propagators; 4 | using GraphSharp.Visitors; 5 | 6 | namespace GraphSharp.Graphs; 7 | 8 | /// 9 | /// Contains graph algorithms. 10 | /// 11 | public partial class ImmutableGraphOperation 12 | where TNode : INode 13 | where TEdge : IEdge 14 | { 15 | /// 16 | /// Source graph 17 | /// 18 | public IImmutableGraph StructureBase{get;} 19 | /// 20 | /// Graph nodes 21 | /// 22 | public IImmutableNodeSource Nodes => StructureBase.Nodes; 23 | /// 24 | /// Graph edges 25 | /// 26 | public IImmutableEdgeSource Edges => StructureBase.Edges; 27 | /// 28 | /// Graph configuration 29 | /// 30 | public IGraphConfiguration Configuration => StructureBase.Configuration; 31 | ObjectPool> PropagatorPool; 32 | ObjectPool> ParallelPropagatorPool; 33 | /// 34 | public ImmutableGraphOperation(IImmutableGraph structureBase) 35 | { 36 | StructureBase = structureBase; 37 | var tmpVisitor = new ActionVisitor(); 38 | PropagatorPool = new(() => new Propagator(StructureBase.Edges,tmpVisitor,StructureBase.Nodes.MaxNodeId)); 39 | ParallelPropagatorPool = new(() => new ParallelPropagator(StructureBase.Edges,tmpVisitor,StructureBase.Nodes.MaxNodeId)); 40 | } 41 | /// 42 | /// Get propagator from pool 43 | /// 44 | public Propagator GetPropagator(IVisitor visitor) 45 | { 46 | var p = PropagatorPool.Get(); 47 | p.Reset(StructureBase.Edges, visitor,StructureBase.Nodes.MaxNodeId); 48 | return p; 49 | } 50 | /// 51 | /// Get parallel propagator from pool 52 | /// 53 | public ParallelPropagator GetParallelPropagator(IVisitor visitor) 54 | { 55 | var p = ParallelPropagatorPool.Get(); 56 | p.Reset(StructureBase.Edges, visitor,StructureBase.Nodes.MaxNodeId); 57 | return p; 58 | } 59 | /// 60 | /// Returns propagator to pool 61 | /// 62 | public void ReturnPropagator(IPropagator propagator){ 63 | if(propagator is Propagator p1) 64 | PropagatorPool.Return(p1); 65 | if(propagator is ParallelPropagator p2) 66 | ParallelPropagatorPool.Return(p2); 67 | } 68 | 69 | } -------------------------------------------------------------------------------- /GraphSharp/Visitors/Implementations/DijkstrasAlgorithm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using GraphSharp.Graphs; 4 | namespace GraphSharp.Visitors; 5 | 6 | /// 7 | /// Visitor that finds all shortest paths between given node to all other nodes in a graph. 8 | /// 9 | public class DijkstrasAlgorithm : PathFinderBase 10 | where TNode : INode 11 | where TEdge : IEdge 12 | { 13 | /// 14 | /// what is the length of path from startNode to some other node so far. 15 | /// 16 | public RentedArray PathLength; 17 | /// 18 | /// Creates a new instance of 19 | /// 20 | /// Node from which we need to find a shortest path 21 | /// Algorithm will be executed on this graph 22 | /// The type of path 23 | public DijkstrasAlgorithm(int startNodeId, IImmutableGraph graph,PathType pathType) : base(graph,pathType) 24 | { 25 | GetWeight = e => e.MapProperties().Weight; 26 | this.StartNodeId = startNodeId; 27 | PathLength = ArrayPoolStorage.RentArray(graph.Nodes.MaxNodeId + 1); 28 | PathLength.Fill(-1); 29 | PathLength[startNodeId] = 0; 30 | } 31 | /// 32 | /// Clears state of an algorithm and reset it's startNodeId 33 | /// 34 | public void Clear(int startNodeId) 35 | { 36 | this.StartNodeId = startNodeId; 37 | PathLength.Fill(-1); 38 | PathLength[startNodeId] = 0; 39 | ClearPaths(); 40 | } 41 | /// 42 | protected override bool SelectImpl(EdgeSelect edge) 43 | { 44 | (var sourceId,var targetId) = (edge.SourceId,edge.TargetId); 45 | var pathLength = PathLength[sourceId] + GetWeight(edge); 46 | 47 | var pathSoFar = PathLength[targetId]; 48 | 49 | if (pathSoFar != -1) 50 | { 51 | if (pathSoFar <= pathLength) 52 | { 53 | return false; 54 | } 55 | } 56 | PathLength[targetId] = pathLength; 57 | Path[targetId] = sourceId; 58 | return true; 59 | } 60 | /// 61 | protected override void VisitImpl(int node) 62 | { 63 | DidSomething = true; 64 | } 65 | 66 | /// 67 | /// Get path from to 68 | /// 69 | /// Empty list if path not found 70 | public IPath GetPath(int endNodeId) => GetPath(StartNodeId, endNodeId); 71 | 72 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/MinCut.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.ComponentModel.DataAnnotations; 5 | using System.Linq; 6 | using GraphSharp.Adapters; 7 | using GraphSharp.Exceptions; 8 | using QuikGraph.Algorithms.MaximumFlow; 9 | using QuikGraph.Algorithms.Ranking; 10 | 11 | namespace GraphSharp.Graphs; 12 | 13 | /// 14 | /// Result of max flow algorithms 15 | /// 16 | public class MinCutResult 17 | { 18 | /// 19 | /// Nodes on the left side of the cut 20 | /// 21 | public HashSet LeftCutNodes{get;} 22 | /// 23 | /// Nodes on the right side of the cut 24 | /// 25 | public HashSet RightCutNodes{get;} 26 | /// 27 | /// Creates a new min cut result 28 | /// 29 | public MinCutResult(IEnumerable leftCutNodes, IEnumerable rightCutNodes) 30 | { 31 | LeftCutNodes = leftCutNodes .ToHashSet(); 32 | RightCutNodes = rightCutNodes.ToHashSet(); 33 | } 34 | /// True if two nodes are in same cut 35 | public bool InSameCut(int node1,int node2){ 36 | return LeftCutNodes.Contains(node1) && LeftCutNodes.Contains(node2) || RightCutNodes.Contains(node1) && RightCutNodes.Contains(node2); 37 | } 38 | } 39 | 40 | public partial class GraphOperation 41 | where TNode : INode 42 | where TEdge : IEdge 43 | { 44 | // TODO: Add test 45 | /// 46 | /// Computes min cut from solved max flow 47 | /// 48 | public MinCutResult MinCut(MaxFlowResult maxFlow){ 49 | var residual = maxFlow.ResidualCapacities; 50 | var left = new List(); 51 | var right = new List(); 52 | 53 | var sourcePaths = 54 | FindShortestPathsDijkstra( 55 | maxFlow. 56 | SourceId, 57 | e=>1, 58 | condition:e=>residual(e.Edge)!=0 59 | ); 60 | var sinkPaths = 61 | FindShortestPathsDijkstra( 62 | maxFlow.SinkId, 63 | e=>1, 64 | condition:e=>residual(e.Edge)!=0, 65 | pathType:PathType.InEdges 66 | ); 67 | 68 | foreach(var n in Nodes){ 69 | var p1 = sourcePaths.GetPath(n.Id); 70 | var p2 = sinkPaths.GetPath(n.Id); 71 | if(p1.Count!=0) 72 | right.Add(n.Id); 73 | // else 74 | if(p2.Count!=0) 75 | left.Add(n.Id); 76 | } 77 | right.Add(maxFlow.SinkId); 78 | left.Add(maxFlow.SourceId); 79 | return new MinCutResult(left,right); 80 | } 81 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/Reindex.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | namespace GraphSharp.Graphs; 3 | 4 | public partial class GraphOperation 5 | where TNode : INode 6 | where TEdge : IEdge 7 | { 8 | 9 | /// 10 | /// Reindexes all nodes and edges to minimal possible values. 11 | /// Use this method if your graph node indices are out of hand. 12 | /// 13 | public GraphOperation Reindex() 14 | { 15 | var reindexed = ReindexNodes(); 16 | var edgesToMove = new List<(TEdge edge, int newSourceId, int newTargetId)>(); 17 | foreach (var edge in Edges) 18 | { 19 | var targetReindexed = reindexed.TryGetValue(edge.TargetId, out var newTargetId); 20 | var sourceReindexed = reindexed.TryGetValue(edge.SourceId, out var newSourceId); 21 | if (targetReindexed || sourceReindexed) 22 | edgesToMove.Add(( 23 | edge, 24 | sourceReindexed ? newSourceId : edge.SourceId, 25 | targetReindexed ? newTargetId : edge.TargetId 26 | )); 27 | } 28 | 29 | foreach (var toMove in edgesToMove) 30 | { 31 | Edges.Move(toMove.edge,toMove.newSourceId,toMove.newTargetId); 32 | } 33 | 34 | return this; 35 | } 36 | /// 37 | /// Reindex nodes only and return dict where Key is old node id and Value is new node id 38 | /// 39 | /// 40 | protected IDictionary ReindexNodes() 41 | { 42 | var idMap = new Dictionary(); 43 | using var nodeIdsMap = ArrayPoolStorage.RentArray(Nodes.MaxNodeId + 1); 44 | foreach (var n in Nodes) 45 | { 46 | nodeIdsMap[n.Id] = 1; 47 | } 48 | 49 | //search for free index, after it search for occupied index and move 50 | //occupied index node id to a free index 51 | //after it change free index to occupied and occupied to a free index 52 | //so after this we will 'move' all nodes in a left path of a array 53 | //and reindex nodes in result 54 | for (int i = 0; i < nodeIdsMap.Length; i++) 55 | { 56 | if (nodeIdsMap[i] == 0) 57 | for (int b = nodeIdsMap.Length - 1; b > i; b--) 58 | { 59 | if (nodeIdsMap[b] == 1) 60 | { 61 | Nodes.Move(b,i); 62 | nodeIdsMap[b] = 0; 63 | nodeIdsMap[i] = 1; 64 | idMap[b] = i; 65 | break; 66 | } 67 | } 68 | } 69 | return idMap; 70 | } 71 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/Coloring/ColoringResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | 6 | namespace GraphSharp.Graphs; 7 | 8 | /// 9 | /// Result of coloring algorithm 10 | /// 11 | public class ColoringResult : IDisposable{ 12 | /// Array, which index is nodeId and value is colorId. If node is not colored it's value is 0 13 | public RentedArray Colors { get; } 14 | /// 15 | /// Return node color 16 | /// 17 | public int this[int nodeId]=>Colors[nodeId]; 18 | /// 19 | /// Return node color 20 | /// 21 | public int this[INode node]=>Colors[node.Id]; 22 | /// 23 | /// Colors to use.
24 | ///
25 | /// Where is node id, is id of color assigned to that node 26 | /// 27 | public ColoringResult(RentedArray colors){ 28 | this.Colors = colors; 29 | } 30 | /// 31 | /// A dictionary where key is colorId and value is count of nodes that have colorId as assigned to them color 32 | /// 33 | public IDictionary CountUsedColors(){ 34 | var dict = new Dictionary(); 35 | for(int i = 0;i 46 | /// Method to apply some coloring to a graph 47 | /// 48 | /// Nodes to assign colors 49 | /// A list of colors to actually use. Changes nodes colors to colors from this list. Will be extended automatically if given colors is not enough. 50 | public void ApplyColors(IImmutableNodeSource nodes,IEnumerable? colorsToApply = null) 51 | where TNode : INode 52 | { 53 | var colorsList = new List(colorsToApply ?? Enumerable.Empty()); 54 | foreach(var n in nodes){ 55 | var colorId = Colors[n.Id]; 56 | while(colorsList.Count<=colorId){ 57 | colorsList.Add(RandomColor(Random.Shared)); 58 | } 59 | var c = colorsList[colorId]; 60 | n.MapProperties().Color = c; 61 | } 62 | } 63 | /// 64 | /// Disposes object 65 | /// 66 | public void Dispose() 67 | { 68 | ((IDisposable)Colors).Dispose(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /GraphSharp/Algorithms/LowLinkValuesFinder copy: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace GraphSharp.Graphs; 6 | 7 | /// 8 | /// Low link values finder. 9 | /// 10 | public class LowLinkValuesFinder 11 | where TEdge : IEdge 12 | { 13 | IImmutableEdgeSource Edges { get; } 14 | HashSet Nodes { get; } 15 | 16 | /// 17 | /// Creates new low link values finder 18 | /// 19 | public LowLinkValuesFinder(IEnumerable nodes,IImmutableEdgeSource edges) 20 | { 21 | this.Edges = edges; 22 | this.Nodes = nodes.Select(i=>i.Id).ToHashSet(); 23 | } 24 | /// 25 | /// Finds low link values for nodes. Can be used to get strongly connected components 26 | /// 27 | /// Array where index is node id and value is low link value. When value is -1 it means that there is not node with given index. 28 | public RentedArray FindLowLinkValues() 29 | { 30 | var maxNodeId=Nodes.MaxBy(v=>v); 31 | using var ids = ArrayPoolStorage.RentArray(maxNodeId + 1); 32 | using var onStack = ArrayPoolStorage.RentArray(maxNodeId + 1); 33 | //thanks to https://www.youtube.com/watch?v=wUgWX0nc4NY 34 | var UNVISITED = -1; 35 | 36 | //we assign new local id to each node so we can find low link values 37 | //here we store low link values 38 | var low = ArrayPoolStorage.RentArray(maxNodeId + 1); 39 | //if value > 0 then on a stack 40 | 41 | for (int i = 0; i < ids.Length; i++){ 42 | ids[i] = UNVISITED; 43 | } 44 | 45 | var stack = new Stack(); 46 | //id counter 47 | var id = 0; 48 | void dfs(int at) 49 | { 50 | stack.Push(at); 51 | onStack[at] = 1; 52 | id++; 53 | ids[at] = id; 54 | low[at] = id; 55 | foreach (var e in Edges.OutEdges(at)) 56 | { 57 | var to = e.TargetId; 58 | if (ids[to] == UNVISITED) dfs(to); 59 | if (onStack[to] > 0) low[at] = Math.Min(low[at], low[to]); 60 | } 61 | if (ids[at] == low[at]) 62 | { 63 | while(true) 64 | { 65 | var nodeId = stack.Pop(); 66 | onStack[nodeId] = 0; 67 | low[nodeId] = ids[at]; 68 | if (nodeId == at) break; 69 | } 70 | } 71 | } 72 | 73 | for (int i = 0; i < ids.Length; i++) 74 | { 75 | if(Nodes.Contains(i)) 76 | if (ids[i] == UNVISITED) 77 | dfs(i); 78 | } 79 | 80 | return low; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /GraphSharp/Algorithms/LowLinkValuesFinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace GraphSharp.Graphs; 6 | 7 | /// 8 | /// Low link values finder. 9 | /// 10 | public class LowLinkValuesFinder 11 | where TEdge : IEdge 12 | { 13 | IImmutableEdgeSource Edges { get; } 14 | HashSet Nodes { get; } 15 | 16 | /// 17 | /// Creates new low link values finder 18 | /// 19 | public LowLinkValuesFinder(IEnumerable nodes,IImmutableEdgeSource edges) 20 | { 21 | this.Edges = edges; 22 | this.Nodes = nodes.Select(i=>i.Id).ToHashSet(); 23 | } 24 | /// 25 | /// Finds low link values for nodes. Can be used to get strongly connected components 26 | /// 27 | /// Array where index is node id and value is low link value. When value is -1 it means that there is not node with given index. 28 | public RentedArray FindLowLinkValues() 29 | { 30 | var maxNodeId=Nodes.MaxBy(v=>v); 31 | using var ids = ArrayPoolStorage.RentArray(maxNodeId + 1); 32 | using var onStack = ArrayPoolStorage.RentArray(maxNodeId + 1); 33 | //thanks to https://www.youtube.com/watch?v=wUgWX0nc4NY 34 | var UNVISITED = -1; 35 | 36 | //we assign new local id to each node so we can find low link values 37 | //here we store low link values 38 | var low = ArrayPoolStorage.RentArray(maxNodeId + 1); 39 | //if value > 0 then on a stack 40 | 41 | for (int i = 0; i < ids.Length; i++){ 42 | ids[i] = UNVISITED; 43 | } 44 | 45 | var stack = new Stack(); 46 | //id counter 47 | var id = 0; 48 | void dfs(int at) 49 | { 50 | stack.Push(at); 51 | onStack[at] = 1; 52 | id++; 53 | ids[at] = id; 54 | low[at] = id; 55 | foreach (var e in Edges.OutEdges(at)) 56 | { 57 | var to = e.TargetId; 58 | if (ids[to] == UNVISITED) dfs(to); 59 | if (onStack[to] > 0) low[at] = Math.Min(low[at], low[to]); 60 | } 61 | if (ids[at] == low[at]) 62 | { 63 | while(true) 64 | { 65 | var nodeId = stack.Pop(); 66 | onStack[nodeId] = 0; 67 | low[nodeId] = ids[at]; 68 | if (nodeId == at) break; 69 | } 70 | } 71 | } 72 | 73 | for (int i = 0; i < ids.Length; i++) 74 | { 75 | if(Nodes.Contains(i)) 76 | if (ids[i] == UNVISITED) 77 | dfs(i); 78 | } 79 | 80 | return low; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/FindStronglyConnectedComponents.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | namespace GraphSharp.Graphs; 5 | /// 6 | /// Result of finding strongly connected components algorithm 7 | /// 8 | public class StronglyConnectedComponents : IDisposable 9 | where TNode : INode 10 | { 11 | /// List of tuples, where first value is a list of nodes in a certain component and second value is this component id. 12 | public IEnumerable<(IEnumerable nodes, int componentId)> Components { get; } 13 | private RentedArray low; 14 | /// 15 | /// Mapping of node id to component id where this node resides 16 | /// 17 | public IDictionary NodeIdToComponentId(){ 18 | var nodeIdToComponentId = new Dictionary(); 19 | foreach(var c in Components){ 20 | foreach(var n in c.nodes){ 21 | nodeIdToComponentId[n.Id]=c.componentId; 22 | } 23 | } 24 | return nodeIdToComponentId; 25 | } 26 | 27 | /// 28 | /// 29 | public StronglyConnectedComponents(RentedArray lowLinkValues, IImmutableNodeSource Nodes) 30 | { 31 | Components = lowLinkValues 32 | .Select((componentId, index) => (componentId, index)) 33 | .Where(x => Nodes.TryGetNode(x.index, out var _)) 34 | .GroupBy(x => x.componentId) 35 | .Select(x => (x.Select(x => Nodes[x.index]), x.Key)).ToList(); 36 | low = lowLinkValues; 37 | } 38 | /// if nodes in the same strongly connected component, else 39 | public bool InSameComponent(int nodeId1, int nodeId2) 40 | { 41 | return low[nodeId1] == low[nodeId2]; 42 | } 43 | /// 44 | /// 45 | public void Dispose() 46 | { 47 | low.Dispose(); 48 | } 49 | } 50 | public partial class ImmutableGraphOperation 51 | where TNode : INode 52 | where TEdge : IEdge 53 | { 54 | /// 55 | /// Finds all strongly connected components. 56 | /// It means that if there is a path between two nodes like A->...->B and B->...->A (in both directions) 57 | /// then these nodes are strongly connected and in the same strongly connected component.
58 | /// WARNING: Works only on ONE connected component! If your graph have multiple components, 59 | /// consider to use to 60 | /// divide your big graph into connected subgraphs and run this algorithm on them separately. 61 | ///
62 | public StronglyConnectedComponents FindStronglyConnectedComponentsTarjan() 63 | { 64 | var low = FindLowLinkValues(); 65 | return new StronglyConnectedComponents(low, Nodes); 66 | } 67 | } -------------------------------------------------------------------------------- /GraphSharp.Tests/Operations/ColoringTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using GraphSharp.Graphs; 7 | using GraphSharp.Tests.Models; 8 | using Xunit; 9 | 10 | namespace GraphSharp.Tests.Operations 11 | { 12 | public class ColoringTests : BaseTest 13 | { 14 | [Fact] 15 | public void QuickGraphColorNodes_Works() 16 | { 17 | var coloring = _Graph.Do.ConnectRandomly(1, 5).QuikGraphColorNodes(); 18 | var usedColors = coloring.CountUsedColors(); 19 | coloring.ApplyColors(_Graph.Nodes); 20 | _Graph.EnsureRightColoring(); 21 | Assert.Equal(usedColors.Sum(x => x.Value), _Graph.Nodes.Count); 22 | } 23 | [Fact] 24 | public void GreedyColorNodes_Works() 25 | { 26 | var coloring = _Graph.Do.ConnectRandomly(1, 5).GreedyColorNodes(); 27 | var usedColors = coloring.CountUsedColors(); 28 | coloring.ApplyColors(_Graph.Nodes); 29 | _Graph.EnsureRightColoring(); 30 | Assert.Equal(usedColors.Sum(x => x.Value), _Graph.Nodes.Count); 31 | } 32 | [Fact] 33 | public void DSaturColoring_Works() 34 | { 35 | var coloring = _Graph.Do.ConnectRandomly(1, 5).DSaturColorNodes(); 36 | var usedColors = coloring.CountUsedColors(); 37 | coloring.ApplyColors(_Graph.Nodes); 38 | _Graph.EnsureRightColoring(); 39 | Assert.Equal(usedColors.Sum(x => x.Value), _Graph.Nodes.Count); 40 | } 41 | [Fact] 42 | public void RLFColoring_Works() 43 | { 44 | _Graph.Do.ConnectRandomly(1, 5); 45 | var coloring1 = _Graph.Do.GreedyColorNodes(); 46 | var usedColors1 = coloring1.CountUsedColors(); 47 | ClearColors(_Graph); 48 | coloring1.ApplyColors(_Graph.Nodes); 49 | _Graph.EnsureRightColoring(); 50 | 51 | var coloring2 = _Graph.Do.DSaturColorNodes(); 52 | var usedColors2 = coloring2.CountUsedColors(); 53 | ClearColors(_Graph); 54 | coloring2.ApplyColors(_Graph.Nodes); 55 | _Graph.EnsureRightColoring(); 56 | 57 | var coloring3 = _Graph.Do.RLFColorNodes(); 58 | var usedColors3 = coloring3.CountUsedColors(); 59 | ClearColors(_Graph); 60 | coloring3.ApplyColors(_Graph.Nodes); 61 | _Graph.EnsureRightColoring(); 62 | 63 | var count1 = usedColors1.Where(x => x.Value != 0).Count(); 64 | var count2 = usedColors2.Where(x => x.Value != 0).Count(); 65 | var count3 = usedColors3.Where(x => x.Value != 0).Count(); 66 | 67 | Assert.True(count3 <= count2 && count2 <= count1); 68 | } 69 | 70 | void ClearColors( IGraph g){ 71 | foreach(var n in g.Nodes) 72 | n.MapProperties().Color=Color.Empty; 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /GraphSharp/Common/Implementations/Path.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace GraphSharp.Common; 8 | 9 | /// 10 | /// Determines how we find paths 11 | /// 12 | public enum PathType 13 | { 14 | /// 15 | /// Find path on undirected graph 16 | /// 17 | Undirected, 18 | /// 19 | /// Find path on in edges of directed graph 20 | /// 21 | InEdges, 22 | /// 23 | /// Find path on out edges of directed graph 24 | /// 25 | OutEdges 26 | } 27 | 28 | /// 29 | public record PathResult : IPath 30 | { 31 | Lazy CostLazy { get; init; } 32 | /// 33 | public PathType PathType { get; } 34 | 35 | /// 36 | 37 | public PathResult(Func, double> computePathCost, IList path, PathType pathType) 38 | { 39 | Path = path; 40 | this.CostLazy = new Lazy(() => computePathCost(Path)); 41 | this.PathType = pathType; 42 | } 43 | /// 44 | public double Cost => CostLazy.Value; 45 | /// 46 | public IList Path { get; init; } 47 | /// 48 | 49 | public int Count => Path.Count; 50 | /// 51 | 52 | public bool IsReadOnly => Path.IsReadOnly; 53 | /// 54 | /// Gets or set some node in path under given index 55 | /// 56 | public TNode this[int index] { get => Path[index]; set => Path[index] = value; } 57 | /// 58 | 59 | public int IndexOf(TNode item) 60 | { 61 | return Path.IndexOf(item); 62 | } 63 | /// 64 | 65 | public void Insert(int index, TNode item) 66 | { 67 | Path.Insert(index, item); 68 | } 69 | /// 70 | 71 | public void RemoveAt(int index) 72 | { 73 | Path.RemoveAt(index); 74 | } 75 | /// 76 | 77 | public void Add(TNode item) 78 | { 79 | Path.Add(item); 80 | } 81 | /// 82 | 83 | public void Clear() 84 | { 85 | Path.Clear(); 86 | } 87 | /// 88 | 89 | public bool Contains(TNode item) 90 | { 91 | return Path.Contains(item); 92 | } 93 | /// 94 | 95 | public void CopyTo(TNode[] array, int arrayIndex) 96 | { 97 | Path.CopyTo(array, arrayIndex); 98 | } 99 | 100 | /// 101 | public bool Remove(TNode item) 102 | { 103 | return Path.Remove(item); 104 | } 105 | 106 | /// 107 | public IEnumerator GetEnumerator() 108 | { 109 | return Path.GetEnumerator(); 110 | } 111 | 112 | IEnumerator IEnumerable.GetEnumerator() 113 | { 114 | return ((IEnumerable)Path).GetEnumerator(); 115 | } 116 | } -------------------------------------------------------------------------------- /GraphSharp/Visitors/Implementations/ShortestPathLengthFinderAlgorithms.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using GraphSharp.Common; 3 | using GraphSharp.Graphs; 4 | namespace GraphSharp.Visitors; 5 | 6 | /// 7 | /// Algorithm to find length of shortest path between given node to all nodes in a graph. 8 | /// Basically the same as but only keeps track of lengths of paths so uses(x2) less memory than DijkstrasAlgorithm and a bit faster. 9 | /// 10 | public class ShortestPathsLengthFinderAlgorithms : VisitorBase 11 | where TNode : INode 12 | where TEdge : IEdge 13 | { 14 | /// 15 | public Func GetWeight{get;set;} 16 | /// 17 | /// What is the length of path from startNode to some other node so far. 18 | /// 19 | public RentedArray PathLength{get;protected set;} 20 | IImmutableGraph Graph{get;} 21 | /// 22 | /// Root of all found path lengths 23 | /// 24 | /// 25 | public int StartNodeId{get;protected set;} 26 | 27 | /// Node from which we need to find a shortest path 28 | /// Algorithm will be executed on this graph 29 | public ShortestPathsLengthFinderAlgorithms(int startNodeId, IImmutableGraph graph) 30 | { 31 | GetWeight = e => e.MapProperties().Weight; 32 | this.Graph = graph; 33 | this.StartNodeId = startNodeId; 34 | PathLength = ArrayPoolStorage.RentArray(graph.Nodes.MaxNodeId + 1); 35 | PathLength.Fill(-1); 36 | PathLength[startNodeId] = 0; 37 | } 38 | /// 39 | /// Disposes when collected by GC 40 | /// 41 | ~ShortestPathsLengthFinderAlgorithms(){ 42 | PathLength.Dispose(); 43 | } 44 | /// 45 | /// Clears state of an algorithm and reset it's startNodeId 46 | /// 47 | public void Clear(int startNodeId) 48 | { 49 | this.StartNodeId = startNodeId; 50 | PathLength.Fill(-1); 51 | PathLength[startNodeId] = 0; 52 | Steps = 0; 53 | DidSomething = true; 54 | Done = false; 55 | } 56 | /// 57 | protected override bool SelectImpl(EdgeSelect connection) 58 | { 59 | var sourceId = connection.SourceId; 60 | var targetId = connection.TargetId; 61 | var pathLength = PathLength[sourceId] + GetWeight(connection); 62 | 63 | var pathSoFar = PathLength[targetId]; 64 | 65 | if (pathSoFar != -1) 66 | { 67 | if (pathSoFar <= pathLength) 68 | { 69 | return false; 70 | } 71 | } 72 | PathLength[targetId] = pathLength; 73 | return true; 74 | } 75 | 76 | /// 77 | protected override void VisitImpl(int node) 78 | { 79 | DidSomething = true; 80 | } 81 | /// 82 | protected override void EndImpl() 83 | { 84 | if(!DidSomething) Done = true; 85 | } 86 | 87 | /// 88 | protected override void StartImpl() 89 | { 90 | 91 | } 92 | } -------------------------------------------------------------------------------- /GraphSharp/Visitors/BaseClasses/VisitorBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace GraphSharp.Visitors; 3 | 4 | /// 5 | /// Base class for visitors that contains default extension capabilities and tracking 6 | /// of algorithm execution steps count and algorithm completion. 7 | /// 8 | public abstract class VisitorBase : IVisitorWithSteps 9 | where TEdge : IEdge 10 | { 11 | /// 12 | /// This predicate can be used to avoid some edges/nodes in path finding. 13 | /// By default pass all edges.
14 | /// Begin called at the beginning of method.
15 | /// If is this 16 | /// is allowed to pass function, 17 | /// else not allowed. 18 | ///
19 | public Predicate> Condition { get; set;} 20 | /// 21 | /// Called at the beginning of method 22 | /// 23 | public event Action VisitEvent; 24 | /// 25 | /// Called at the beginning of method 26 | /// 27 | public event Action> SelectEvent; 28 | /// 29 | /// Called at the beginning of method 30 | /// 31 | public event Action StartEvent; 32 | /// 33 | /// Called at the beginning of method 34 | /// 35 | public event Action EndEvent; 36 | /// 37 | public bool Done{get;set;} = false; 38 | /// 39 | public int Steps{get;set;} = 0; 40 | /// 41 | public bool DidSomething{get;set;} 42 | /// 43 | public VisitorBase() 44 | { 45 | Condition = edge=>true; 46 | VisitEvent = node=>{}; 47 | SelectEvent = edge=>{}; 48 | StartEvent = ()=>{}; 49 | EndEvent = ()=>{}; 50 | } 51 | /// 52 | public void Start(){ 53 | DidSomething = false; 54 | StartEvent(); 55 | StartImpl(); 56 | } 57 | /// 58 | public bool Select(EdgeSelect edge){ 59 | if(!Condition(edge) || Done) return false; 60 | SelectEvent(edge); 61 | return SelectImpl(edge); 62 | } 63 | /// 64 | public void Visit(int node){ 65 | if(Done) return; 66 | VisitEvent(node); 67 | VisitImpl(node); 68 | } 69 | /// 70 | public void End(){ 71 | EndEvent(); 72 | EndImpl(); 73 | Steps++; 74 | } 75 | /// 76 | /// function implementation 77 | /// 78 | protected abstract void StartImpl(); 79 | /// 80 | /// function implementation 81 | /// 82 | protected abstract bool SelectImpl(EdgeSelect edge); 83 | /// 84 | /// function implementation 85 | /// 86 | protected abstract void VisitImpl(int node); 87 | /// 88 | /// function implementation 89 | /// 90 | protected abstract void EndImpl(); 91 | } -------------------------------------------------------------------------------- /GraphSharp.GoogleOrTools/TSP.cs: -------------------------------------------------------------------------------- 1 | using Google.OrTools.Graph; 2 | using Google.OrTools.ConstraintSolver; 3 | using Unchase.Satsuma.TSP.Contracts; 4 | using System.Linq; 5 | using Google.OrTools.ModelBuilder; 6 | using MathNet.Numerics; 7 | using Unchase.Satsuma.Adapters; 8 | using System.Collections.Generic; 9 | using System; 10 | namespace GraphSharp.Graphs; 11 | 12 | class TspResult : ITsp 13 | { 14 | public IEnumerable Tour{get;} 15 | public double TourCost{get;} 16 | public TspResult(IEnumerable tour, double cost) 17 | { 18 | 19 | Tour = tour; 20 | TourCost = cost; 21 | } 22 | } 23 | 24 | /// 25 | /// Tsp google or tools extensions 26 | /// 27 | public static class ImmutableGraphOperationTSP 28 | { 29 | /// 30 | /// Tsp of graph by google or tools. 31 | /// 32 | /// 33 | /// Distance between two nodes by their ids. It is long, so you better to scale your distances. 34 | /// Tsp 35 | public static ITsp TspGoogleOrTools(this ImmutableGraphOperation g, Func distances) 36 | where TNode : INode 37 | where TEdge : IEdge 38 | { 39 | 40 | var Nodes = g.Nodes; 41 | // Create Routing Index Manager 42 | RoutingIndexManager manager = 43 | new RoutingIndexManager(Nodes.MaxNodeId+1, 1, Nodes.First().Id); 44 | 45 | // Create Routing Model. 46 | RoutingModel routing = new RoutingModel(manager); 47 | 48 | int transitCallbackIndex = 49 | routing.RegisterTransitCallback((long fromIndex, long toIndex) => 50 | { 51 | // Convert from routing variable Index to 52 | // distance matrix NodeIndex. 53 | var fromNode = manager.IndexToNode(fromIndex); 54 | var toNode = manager.IndexToNode(toIndex); 55 | return distances(fromNode, toNode); 56 | }); 57 | 58 | // Define cost of each arc. 59 | routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); 60 | 61 | // Setting first solution heuristic. 62 | RoutingSearchParameters searchParameters = 63 | operations_research_constraint_solver.DefaultRoutingSearchParameters(); 64 | searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.Automatic; 65 | 66 | // Solve the problem. 67 | Assignment solution = routing.SolveWithParameters(searchParameters); 68 | 69 | var pathLength = solution.ObjectiveValue(); 70 | 71 | long routeDistance = 0; 72 | var index = routing.Start(0); 73 | var path = new List(); 74 | var node = 0; 75 | while (routing.IsEnd(index) == false) 76 | { 77 | node = manager.IndexToNode((int)index); 78 | path.Add(Nodes[node]); 79 | var previousIndex = index; 80 | index = solution.Value(routing.NextVar(index)); 81 | routeDistance += routing.GetArcCostForVehicle(previousIndex, index, 0); 82 | } 83 | 84 | node= manager.IndexToNode((int)index); 85 | path.Add(Nodes[node]); 86 | 87 | return new TspResult(path,pathLength); 88 | } 89 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/FindShortestPaths.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using GraphSharp.Common; 3 | using GraphSharp.Propagators; 4 | using GraphSharp.Visitors; 5 | namespace GraphSharp.Graphs; 6 | 7 | public partial class ImmutableGraphOperation 8 | where TNode : INode 9 | where TEdge : IEdge 10 | { 11 | /// 12 | public DijkstrasAlgorithm FindShortestPathsDijkstra(int nodeId, Func? getWeight = null,Predicate>? condition = null, PathType pathType = PathType.OutEdges) 13 | { 14 | return FindShortestPathsDijkstraBase( 15 | nodeId, 16 | pathFinder=>GetPropagator(pathFinder), 17 | condition, 18 | getWeight, 19 | pathType); 20 | } 21 | /// 22 | /// Concurrently Finds all shortest paths. 23 | /// 24 | /// 25 | public DijkstrasAlgorithm FindShortestPathsParallelDijkstra(int nodeId, Func? getWeight = null,Predicate>? condition = null, PathType pathType = PathType.OutEdges) 26 | { 27 | return FindShortestPathsDijkstraBase( 28 | nodeId, 29 | pathFinder=>GetParallelPropagator(pathFinder), 30 | condition, 31 | getWeight, 32 | pathType); 33 | } 34 | /// 35 | /// Finds all shortest paths by Dijkstra's algorithm. 36 | /// 37 | /// Start of paths 38 | /// 39 | /// What edges need to be skipped. Use this to avoid some forbidden paths 40 | /// Weight of the edge that determines how shortest path is built 41 | /// Type of path you want to build 42 | /// DijkstrasAlgorithm instance that can be used to get path to any other node and length of this path 43 | DijkstrasAlgorithm FindShortestPathsDijkstraBase( 44 | int nodeId, 45 | Func, 46 | PropagatorBase> createPropagator, 47 | Predicate>? condition = null, 48 | Func? getWeight = null, 49 | PathType pathType = PathType.OutEdges) 50 | { 51 | getWeight ??= x=>x.MapProperties().Weight; 52 | condition ??= e=>true; 53 | 54 | var pathFinder = new DijkstrasAlgorithm(nodeId, StructureBase, pathType); 55 | 56 | pathFinder.Condition = condition; 57 | pathFinder.GetWeight = getWeight; 58 | 59 | var propagator = createPropagator(pathFinder); 60 | propagator.SetPosition(nodeId); 61 | 62 | if(pathType == PathType.Undirected) 63 | propagator.SetToIterateByBothEdges(); 64 | if(pathType == PathType.InEdges) 65 | propagator.SetToIterateByInEdges(); 66 | if(pathType == PathType.OutEdges) 67 | propagator.SetToIterateByOutEdges(); 68 | 69 | while (!pathFinder.Done) 70 | { 71 | propagator.Propagate(); 72 | } 73 | ReturnPropagator(propagator); 74 | return pathFinder; 75 | } 76 | } -------------------------------------------------------------------------------- /GraphSharp/Edges/EdgePropertiesMap.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Drawing; 3 | using GraphSharp.Extensions; 4 | using MathNet.Numerics.LinearAlgebra.Double; 5 | namespace GraphSharp; 6 | 7 | /// 8 | /// Represents default readings to properties of node 9 | /// 10 | public partial class EdgePropertiesMap : IEdge 11 | { 12 | /// 13 | public object this[string propertyName] { get => Properties[propertyName]; set => Properties[propertyName]=value; } 14 | /// 15 | /// Base edge 16 | /// 17 | public IEdge BaseEdge { get; } 18 | /// 19 | /// Edge cost 20 | /// 21 | public double Cost 22 | { 23 | get 24 | { 25 | lock (Properties) 26 | { 27 | var c = Properties.GetOrDefault("cost"); 28 | if (c is double cap) 29 | return cap; 30 | return 0; 31 | } 32 | } 33 | set 34 | { 35 | Properties["cost"] = value; 36 | } 37 | } 38 | /// 39 | /// Edge capacity 40 | /// 41 | public double Capacity 42 | { 43 | get 44 | { 45 | lock (Properties) 46 | { 47 | var c = Properties.GetOrDefault("capacity"); 48 | if (c is double cap) 49 | return cap; 50 | return 0; 51 | } 52 | } 53 | set 54 | { 55 | lock (Properties) 56 | Properties["capacity"] = value; 57 | } 58 | } 59 | /// 60 | /// Node color 61 | /// 62 | public Color Color 63 | { 64 | get 65 | { 66 | lock (Properties) 67 | { 68 | var c = Properties.GetOrDefault("color"); 69 | if (c is Color color) 70 | return color; 71 | return Color.Empty; 72 | } 73 | } 74 | set 75 | { 76 | Properties["color"] = value; 77 | } 78 | } 79 | /// 80 | /// Node weight 81 | /// 82 | public double Weight 83 | { 84 | get 85 | { 86 | lock (Properties) 87 | { 88 | var c = Properties.GetOrDefault("weight"); 89 | if (c is double w) 90 | return w; 91 | return 0; 92 | } 93 | } 94 | set 95 | { 96 | lock (Properties) 97 | Properties["weight"] = value; 98 | } 99 | } 100 | 101 | /// 102 | public IDictionary Properties => BaseEdge.Properties; 103 | 104 | /// 105 | public int SourceId { get => BaseEdge.SourceId; set => BaseEdge.SourceId = value; } 106 | /// 107 | public int TargetId { get => BaseEdge.TargetId; set => BaseEdge.TargetId = value; } 108 | 109 | 110 | /// 111 | public EdgePropertiesMap(IEdge edge) 112 | { 113 | BaseEdge = edge; 114 | } 115 | 116 | /// 117 | public IEdge Clone() 118 | { 119 | return BaseEdge.Clone(); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /GraphSharp/Nodes/NodePropertiesMap.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Drawing; 3 | using GraphSharp.Extensions; 4 | using MathNet.Numerics.LinearAlgebra.Single; 5 | namespace GraphSharp; 6 | 7 | /// 8 | /// Represents default readings to properties of node 9 | /// 10 | public partial class NodePropertiesMap : INode 11 | { 12 | /// 13 | public object this[string propertyName] { get => Properties[propertyName]; set => Properties[propertyName]=value; } 14 | /// 15 | /// Base node 16 | /// 17 | public INode BaseNode { get; } 18 | /// 19 | /// Node color 20 | /// 21 | public Color Color 22 | { 23 | get 24 | { 25 | lock (Properties) 26 | { 27 | var c = Properties.GetOrDefault("color"); 28 | if (c is Color color) 29 | return color; 30 | return Color.Empty; 31 | } 32 | } 33 | set 34 | { 35 | lock (Properties) 36 | Properties["color"] = value; 37 | } 38 | } 39 | /// 40 | /// Node supply 41 | /// 42 | public double Supply 43 | { 44 | get 45 | { 46 | lock (Properties) 47 | { 48 | var c = Properties.GetOrDefault("supply"); 49 | if (c is double w) 50 | return w; 51 | return 0; 52 | } 53 | } 54 | set 55 | { 56 | lock (Properties) 57 | Properties["supply"] = value; 58 | } 59 | } 60 | /// 61 | /// Node weight 62 | /// 63 | public double Weight 64 | { 65 | get 66 | { 67 | lock (Properties) 68 | { 69 | var c = Properties.GetOrDefault("weight"); 70 | if (c is double w) 71 | return w; 72 | return 0; 73 | } 74 | } 75 | set 76 | { 77 | lock (Properties) 78 | Properties["weight"] = value; 79 | } 80 | } 81 | /// 82 | public Vector Position{ 83 | get 84 | { 85 | lock (Properties) 86 | { 87 | var c = Properties.GetOrDefault("position"); 88 | if (c is Vector w) 89 | return w; 90 | var newW = DenseVector.Create(2, 0); 91 | Properties["position"] = newW; 92 | return newW; 93 | } 94 | } 95 | set 96 | { 97 | lock (Properties) 98 | Properties["position"] = value; 99 | } 100 | } 101 | /// 102 | public IDictionary Properties => BaseNode.Properties; 103 | 104 | /// 105 | public int Id { get => BaseNode.Id; set => BaseNode.Id = value; } 106 | 107 | /// 108 | public NodePropertiesMap(INode node) 109 | { 110 | BaseNode = node; 111 | } 112 | 113 | /// 114 | public INode Clone() 115 | { 116 | return BaseNode.Clone(); 117 | } 118 | 119 | /// 120 | public bool Equals(INode? other) 121 | { 122 | return BaseNode.Equals(other); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /GraphSharp/Algorithms/GraphOperations/FindArticulationPoints.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using GraphSharp.Common; 5 | 6 | namespace GraphSharp.Graphs; 7 | 8 | public partial class ImmutableGraphOperation 9 | where TNode : INode 10 | where TEdge : IEdge 11 | { 12 | /// 13 | /// Algorithm to find articulation points. Works on any type of graph. 14 | /// Thanks to https://www.geeksforgeeks.org/articulation-points-or-cut-vertices-in-a-graph/ 15 | /// 16 | /// Articulation points of a graph 17 | public IEnumerable FindArticulationPointsTarjan() 18 | { 19 | if (Nodes.Count() == 0 || Edges.Count() == 0) 20 | return Enumerable.Empty(); 21 | 22 | using var disc = ArrayPoolStorage.RentArray(Nodes.MaxNodeId + 1); 23 | using var low = ArrayPoolStorage.RentArray(Nodes.MaxNodeId + 1); 24 | using var flags = ArrayPoolStorage.RentArray(Nodes.MaxNodeId + 1); 25 | 26 | int time = 0, parent = -1; 27 | const byte visitedFlag = 1; 28 | const byte isApFlag = 2; 29 | // Adding this loop so that the 30 | // code works even if we are given 31 | // disconnected graph 32 | foreach (var u in Nodes) 33 | if ((flags[u.Id] & visitedFlag) != visitedFlag) 34 | ArticulationPointsFinder( 35 | Edges, 36 | u.Id, flags, 37 | disc, low, ref 38 | time, parent); 39 | 40 | var result = new List(); 41 | for (int i = 0; i < flags.Length; i++) 42 | { 43 | if ((flags[i] & isApFlag) == isApFlag) 44 | { 45 | result.Add(Nodes[i]); 46 | } 47 | } 48 | return result; 49 | } 50 | void ArticulationPointsFinder(IImmutableEdgeSource adj, int u, RentedArray flags, RentedArray disc, RentedArray low, ref int time, int parent) 51 | { 52 | const byte visitedFlag = 1; 53 | const byte isApFlag = 2; 54 | // Count of children in DFS Tree 55 | int children = 0; 56 | 57 | // Mark the current node as visited 58 | flags[u] |= visitedFlag; 59 | 60 | // Initialize discovery time and low value 61 | disc[u] = low[u] = ++time; 62 | 63 | // Go through all vertices adjacent to this 64 | foreach (var v in adj.OutEdges(u).Select(x => x.TargetId)) 65 | { 66 | // If v is not visited yet, then make it a child of u 67 | // in DFS tree and recur for it 68 | if ((flags[v] & visitedFlag) != visitedFlag) 69 | { 70 | children++; 71 | ArticulationPointsFinder(adj, v, flags, disc, low, ref time, u); 72 | 73 | // Check if the subtree rooted with v has 74 | // a connection to one of the ancestors of u 75 | low[u] = Math.Min(low[u], low[v]); 76 | 77 | // If u is not root and low value of one of 78 | // its child is more than discovery value of u. 79 | if (parent != -1 && low[v] >= disc[u]) 80 | flags[u] |= isApFlag; 81 | } 82 | 83 | // Update low value of u for parent function calls. 84 | else if (v != parent) 85 | low[u] = Math.Min(low[u], disc[v]); 86 | } 87 | 88 | // If u is root of DFS tree and has two or more children. 89 | if (parent == -1 && children > 1) 90 | flags[u] |= isApFlag; 91 | } 92 | } -------------------------------------------------------------------------------- /GraphSharp.Tests/Operations/PathFindersTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using GraphSharp.Common; 7 | using GraphSharp.Graphs; 8 | using GraphSharp.Tests.Models; 9 | using Xunit; 10 | 11 | namespace GraphSharp.Tests.Operations 12 | { 13 | public class PathFindersTests : BaseTest 14 | { 15 | public void FindPath(Func, int, int, IPath> getPath) 16 | { 17 | _Graph.Do.CreateNodes(1000); 18 | for (int i = 0; i < 10; i++) 19 | { 20 | _Graph.Do.ConnectRandomly(0, 7); 21 | _Graph.Do.MakeBidirected(); 22 | var components = _Graph.Do.FindComponents(); 23 | if (components.Components.Count() >= 2) 24 | { 25 | var c1 = components.Components.First(); 26 | var c2 = components.Components.ElementAt(1); 27 | var n1 = c1.First(); 28 | var n2 = c2.First(); 29 | var path1 = getPath(_Graph, n1.Id, n2.Id); 30 | Assert.Empty(path1.Path); 31 | } 32 | var first = components.Components.First(); 33 | if (first.Count() < 2) continue; 34 | var d1 = components.Components.First().First(); 35 | var d2 = components.Components.First().Last(); 36 | var path2 = getPath(_Graph, d1.Id, d2.Id); 37 | Assert.NotEmpty(path2.Path); 38 | _Graph.ValidatePath(path2); 39 | } 40 | } 41 | [Fact] 42 | public void FindAnyPath_Works() 43 | { 44 | FindPath((graph, n1, n2) => graph.Do.FindAnyPath(n1, n2)); 45 | FindPath((graph, n1, n2) => graph.Do.FindAnyPathParallel(n1, n2)); 46 | } 47 | [Fact] 48 | public void FindAnyPathWithCondition_Works() 49 | { 50 | FindPath((graph, n1, n2) => graph.Do.FindAnyPath(n1, n2, x => true)); 51 | FindPath((graph, n1, n2) => graph.Do.FindAnyPathParallel(n1, n2, x => true)); 52 | _Graph.Do.DelaunayTriangulation(x=>x.MapProperties().Position); 53 | for (int i = 0; i < 10; i++) 54 | { 55 | var p = Random.Shared.Next(999) + 1; 56 | var path1 = _Graph.Do.FindAnyPath(0, p, x => x.TargetId % 5 != 0).Path; 57 | var path2 = _Graph.Do.FindAnyPathParallel(0, p, x => x.TargetId % 5 != 0).Path; 58 | if (path1.Count() == 0) 59 | { 60 | Assert.Empty(path2); 61 | continue; 62 | } 63 | Assert.NotEmpty(path1); 64 | Assert.NotEmpty(path2); 65 | Assert.True(path1.All(x => x.Id % 5 != 0 || x.Id == 0)); 66 | Assert.True(path2.All(x => x.Id % 5 != 0 || x.Id == 0)); 67 | } 68 | } 69 | [Fact] 70 | public void FindShortestPaths_Works() 71 | { 72 | FindPath((graph, n1, n2) => graph.Do.FindShortestPathsDijkstra(n1).GetPath(n2)); 73 | FindPath((graph, n1, n2) => graph.Do.FindShortestPathsParallelDijkstra(n1).GetPath(n2)); 74 | } 75 | [Fact] 76 | public void FindPathByMeetInTheMiddle_Works(){ 77 | FindPath((graph, n1, n2) => graph.Do.FindPathByMeetInTheMiddleDijkstra(n1,n2)); 78 | FindPath((graph, n1, n2) => graph.Do.FindPathByMeetInTheMiddle(n1,n2)); 79 | FindPath((graph, n1, n2) => graph.Do.FindPathByMeetInTheMiddleParallel(n1,n2)); 80 | FindPath((graph, n1, n2) => graph.Do.FindPathByMeetInTheMiddleDijkstraParallel(n1,n2)); 81 | } 82 | 83 | } 84 | } -------------------------------------------------------------------------------- /GraphSharp/Algorithms/IndependentSet/BallardMyerIndependentSet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace GraphSharp.Graphs; 6 | 7 | ///
8 | /// Finds maximal independent set.
9 | /// Altered implementation of this algorithm:
10 | ///
11 | public class BallardMyerIndependentSet : IndependentSetAlgorithmBase 12 | where TNode : INode 13 | where TEdge : IEdge 14 | { 15 | #pragma warning disable 16 | /// 17 | /// You may need to find independent set from some subset of nodes. Use this to control it. 18 | /// Only nodes that pass a condition can be added to independent set 19 | /// 20 | /// 21 | public BallardMyerIndependentSet(IImmutableNodeSource nodes, IImmutableEdgeSource edges, Predicate condition) : base(nodes, edges) 22 | { 23 | this.Condition = condition; 24 | } 25 | #pragma warning enable 26 | 27 | public Predicate Condition { get; } 28 | 29 | /// 30 | public override IndependentSetResult Find() 31 | { 32 | foreach (var n in Nodes) 33 | { 34 | if (!Condition(n)) 35 | { 36 | nodeState[n.Id] |= Forbidden; 37 | foreach (var n2 in Edges.Neighbors(n.Id)) 38 | { 39 | countOfForbiddenNeighbors[n2]++; 40 | } 41 | } 42 | } 43 | 44 | int toAdd = 45 | Nodes.Where(x => !IsForbidden(x.Id)).MaxBy(x => Edges.Neighbors(x.Id).Count())?.Id 46 | ?? 47 | throw new ArgumentException("Graph must contain at least one node to compute independent set"); 48 | int bestScore; 49 | IEnumerable neighbors; 50 | IList candidates = new List() { toAdd }; 51 | while (true) 52 | unchecked 53 | { 54 | if (IsAdded(toAdd)) break; 55 | nodeState[toAdd] |= Added; 56 | candidates.Clear(); 57 | 58 | neighbors = Edges.Neighbors(toAdd); 59 | foreach (var n in neighbors) 60 | { 61 | countOfColoredNeighbors[n]--; 62 | if (nodeState[n] != 0) continue; 63 | nodeState[n] |= AroundAdded; 64 | foreach (var l in Edges.Neighbors(n)) 65 | freeNeighbors[l]--; 66 | }; 67 | 68 | bestScore = int.MaxValue; 69 | for (int index = 0; index < freeNeighbors.Length; index++) 70 | { 71 | if (nodeState[index] != 0) continue; 72 | var score = freeNeighbors[index]; 73 | if (score < bestScore) 74 | { 75 | bestScore = score; 76 | candidates.Clear(); 77 | } 78 | 79 | if (score == bestScore) 80 | { 81 | candidates.Add(index); 82 | } 83 | }; 84 | if (bestScore == int.MaxValue) 85 | break; 86 | var candidates2 = candidates.AllMinValues(x => countOfForbiddenNeighbors[x]); 87 | toAdd = candidates2.MinBy(x => countOfColoredNeighbors[x]); 88 | } 89 | var result = new List(Nodes.Count() / 3); 90 | foreach (var n in Nodes) 91 | { 92 | if (IsAdded(n.Id)) 93 | result.Add(n); 94 | } 95 | return new(nodeState, result); 96 | } 97 | } 98 | --------------------------------------------------------------------------------