Gapotchenko.FX.Math.Topology
2022.2.7
Prefix Reserved
Gapotchenko.FX.Math.Topology package was formalized, improved, and refactored into Gapotchenko.FX.Math.Graphs package. Consider using the new Gapotchenko.FX.Math.Graphs package for all new developments. The upgrade is easy thanks to the fact that both packages provide nearly identical APIs.
dotnet add package Gapotchenko.FX.Math.Topology --version 2022.2.7
NuGet\Install-Package Gapotchenko.FX.Math.Topology -Version 2022.2.7
<PackageReference Include="Gapotchenko.FX.Math.Topology" Version="2022.2.7" />
paket add Gapotchenko.FX.Math.Topology --version 2022.2.7
#r "nuget: Gapotchenko.FX.Math.Topology, 2022.2.7"
// Install Gapotchenko.FX.Math.Topology as a Cake Addin #addin nuget:?package=Gapotchenko.FX.Math.Topology&version=2022.2.7 // Install Gapotchenko.FX.Math.Topology as a Cake Tool #tool nuget:?package=Gapotchenko.FX.Math.Topology&version=2022.2.7
Overview
The module provides data structures and primitives for working with abstract topologies.
In its classical meaning, the science of topology is concerned with the properties of geometric objects. However, such a definition may be considered as too narrow by some. Math has no boundaries, so topology should not be viewed as limited to just one unique flavor of objects (geometric, physical); it can really be anything.
Graph
Basics
Graph<T>
provided by Gapotchenko.FX.Math.Topology
represents a strongly-typed directional graph of objects.
The objects correspond to mathematical abstractions called graph vertices and each of the related pairs of vertices is called an edge.
A graph can be viewed as a structure that contains two sets: set of vertices and set of edges.
Vertices define "what" graph contains and edges define "how" those vertices are connected.
Let's take a look at the simplest graph that contains just two vertices:
using Gapotchenko.FX.Math.Topology;
var g = new Graph<int>
{
Vertices = { 1, 2 }
};
If we could visualize that graph then it would look like this:
Now let's add one more vertex 3
plus an edge that goes from vertex 1
to vertex 2
:
var g = new Graph<int>
{
Vertices = { 1, 2, 3 },
Edges = { (1, 2) } // <-- an edge has (from, to) notation
};
Our new graph looks like this:
The vertices already defined in edges can be omitted for brevity:
var h = new Graph<int>
{
Vertices = { 3 },
Edges = { (1, 2) }
};
Console.WriteLine(g.GraphEquals(h)); // will print "True"
It is worth mentioning that the graph provides its vertices as an ISet<T>
, so the usual operations on a set apply to the vertices as well:
var g = new Graph<int>
{
Vertices = { 3 },
Edges = { (1, 2) }
};
g.Vertices.UnionWith(new[] { 3, 4, 5 });
The example above produces the following graph:
The same ISet<T>
model applies to the graph edges: they are treated as a set too.
Operations
Now once we have the basics in place, let's take a look at graph operations. Consider the graph:
var g = new Graph<int>
{
Edges =
{
(7, 5), (7, 6),
(6, 3), (6, 4),
(5, 2), (5, 4),
(3, 1),
(2, 1),
(1, 0)
}
};
which looks like this:
Let's transpose the graph (i.e. reverse the direction of its edges):
var h = g.GetTransposition();
Transposed graph h
renders as:
Note that graph h
is a new instance of Graph<T>
.
But what if we want to transpose the graph g
in place?
Every graph operation has a corresponding in-place variant, so for transposition it will be:
g.Transpose();
In this way, a developer can freely choose between immutable, mutable, or combined data models when working on a particular task at hand.
Graph transposition is just one example but there are plenty of other operations available. They all work in the same manner and follow the same model:
Operation | Description | Immutable Function | In-Place Method |
---|---|---|---|
Transposition | Reverses the direction of all edges in the graph. | GetTransposition |
Transpose |
Transitive reduction | Prunes the transitive relations that have shorter paths. | GetTransitiveReduction |
ReduceTransitions |
Reflexive reduction | Removes the loops (also called self-loops or buckles). | GetReflexiveReduction |
ReduceReflexes |
Subgraph | Produces a vertex-induced or edge-induced subgraph. | GetSubgraph |
Subgraph |
Intersection | Produces a graph containing vertices and edges that are present in both the current and a specified graphs. | Intersect |
IntersectWith |
Union | Produces a graph containing all vertices and edges that are present in the current graph, in the specified graph, or in both. | Union |
UnionWith |
Exception | Produces a graph containing vertices and edges that are present in the current graph but not in the specified graph. | Except |
ExceptWith |
Topological Sorting
Topological sort of a graph is a linear ordering of its vertices such that
for every directed edge u
→ v
,
u
comes before v
in the ordering.
A classic example of topological sorting is to schedule a sequence of jobs (or tasks) according to their dependencies.
The jobs are represented by vertices, and there is an edge from x
to y
if job x
must be completed before job y
can be started.
Performing topological sort on such a graph would give us an order in which to perform the jobs.
Let's take a look at example graph:
Let's assume that vertices represent the jobs, and edges define the dependencies between them.
In this way, job 0
depends on job 1
and thus cannot be started unless job 1
is finished.
In turn, job 1
cannot be started unless jobs 2
and 3
are finished. And so on.
In what order should the jobs be executed?
To answer that question, let's use OrderTopologically
method:
using Gapotchenko.FX.Math.Topology;
// Define a graph according to the diagram.
var g = new Graph<int>
{
Edges =
{
(7, 5), (7, 6),
(6, 3), (6, 4),
(5, 2), (5, 4),
(3, 1),
(2, 1),
(1, 0)
}
};
// Sort the graph topologically.
var ordering = g.OrderTopologically();
// Print the results.
Console.WriteLine(string.Join(", ", ordering));
The resulting sequence of jobs is:
7, 5, 6, 2, 3, 4, 1, 0
OrderTopologically
method can only work on directed acyclic graphs.
If the graph contains a cycle then CircularDependencyException
is raised.
Stable Topological Sort of a Graph
Graph is a data structure similar to a set: it does not guarantee to preserve the order in which the elements were added. As a result, topological sorting may return different orderings for otherwise equal graphs.
To overcome that limitation, it may be beneficial to use the topological sorting with a subsequent ordering by some other criteria.
Such approach makes the topological sorting stable.
It can be achieved by leveraging the standard IOrderedEnumerable<T>
LINQ semantics of the operation, like so:
g.OrderTopologically().ThenBy(…)
Stable Topological Sort of a Sequence
Sorting a sequence of elements in topological order is another play on topological sorting idea.
Say we have a sequence of elements {A, B, C, D, E, F}
. Some elements depend on others:
- A depends on B
- B depends on D
Objective: sort the sequence so that its elements are ordered according to their dependencies. The resulting sequence should have a minimal edit distance to the original one. In other words, sequence should be topologically sorted while preserving the original order of elements whenever it is possible.
Gapotchenko.FX.Math.Topology
provides an extension method for IEnumerable<T>
that allows to achieve that:
using Gapotchenko.FX.Math.Topology;
string seq = "ABCDEF";
// Dependency function.
static bool df(char a, char b) =>
(a + " depends on " + b) switch
{
"A depends on B" or
"B depends on D" => true,
_ => false
};
var ordering = seq.OrderTopologicallyBy(x => x, df);
Console.WriteLine(string.Join(", ", ordering)); // <- prints "D, B, A, C, E, F"
Unlike its graph sibling, OrderTopologicallyBy
method tolerates circular dependencies by ignoring them.
They are resolved according to the original order of elements in the sequence.
OrderTopologicallyBy
method allows a subseqent sorting by following the standard IOrderedEnumerable<T>
LINQ convention:
seq.OrderTopologicallyBy(…).ThenBy(…)
Commonly Used Types
- Gapotchenko.FX.Math.Topology.Graph
Other Modules
Let's continue with a look at some other modules provided by Gapotchenko.FX:
- Gapotchenko.FX
- Gapotchenko.FX.AppModel.Information
- Gapotchenko.FX.Collections
- Gapotchenko.FX.Console
- Gapotchenko.FX.Data
- Gapotchenko.FX.Diagnostics
- Gapotchenko.FX.IO
- Gapotchenko.FX.Linq
- Gapotchenko.FX.Math
- Gapotchenko.FX.Memory
- Gapotchenko.FX.Security.Cryptography
- Gapotchenko.FX.Text
- Gapotchenko.FX.Threading
Or look at the full list of modules.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 is compatible. net5.0-windows was computed. net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp2.0 is compatible. netcoreapp2.1 is compatible. netcoreapp2.2 was computed. netcoreapp3.0 is compatible. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 is compatible. |
.NET Framework | net46 is compatible. net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 is compatible. net472 is compatible. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETCoreApp 2.0
- Gapotchenko.FX.Collections (>= 2022.2.7)
- Gapotchenko.FX.Linq (>= 2022.2.7)
- Gapotchenko.FX.Math (>= 2022.2.7)
-
.NETCoreApp 2.1
- Gapotchenko.FX.Collections (>= 2022.2.7)
- Gapotchenko.FX.Linq (>= 2022.2.7)
- Gapotchenko.FX.Math (>= 2022.2.7)
-
.NETCoreApp 3.0
- Gapotchenko.FX.Collections (>= 2022.2.7)
- Gapotchenko.FX.Linq (>= 2022.2.7)
- Gapotchenko.FX.Math (>= 2022.2.7)
-
.NETFramework 4.6
- Gapotchenko.FX.Collections (>= 2022.2.7)
- Gapotchenko.FX.Linq (>= 2022.2.7)
- Gapotchenko.FX.Math (>= 2022.2.7)
-
.NETFramework 4.7.1
- Gapotchenko.FX.Collections (>= 2022.2.7)
- Gapotchenko.FX.Linq (>= 2022.2.7)
- Gapotchenko.FX.Math (>= 2022.2.7)
-
.NETFramework 4.7.2
- Gapotchenko.FX.Collections (>= 2022.2.7)
- Gapotchenko.FX.Linq (>= 2022.2.7)
- Gapotchenko.FX.Math (>= 2022.2.7)
-
.NETStandard 2.0
- Gapotchenko.FX.Collections (>= 2022.2.7)
- Gapotchenko.FX.Linq (>= 2022.2.7)
- Gapotchenko.FX.Math (>= 2022.2.7)
-
.NETStandard 2.1
- Gapotchenko.FX.Collections (>= 2022.2.7)
- Gapotchenko.FX.Linq (>= 2022.2.7)
- Gapotchenko.FX.Math (>= 2022.2.7)
-
net5.0
- Gapotchenko.FX.Collections (>= 2022.2.7)
- Gapotchenko.FX.Linq (>= 2022.2.7)
- Gapotchenko.FX.Math (>= 2022.2.7)
-
net6.0
- Gapotchenko.FX.Collections (>= 2022.2.7)
- Gapotchenko.FX.Linq (>= 2022.2.7)
- Gapotchenko.FX.Math (>= 2022.2.7)
-
net7.0
- Gapotchenko.FX.Collections (>= 2022.2.7)
- Gapotchenko.FX.Linq (>= 2022.2.7)
- Gapotchenko.FX.Math (>= 2022.2.7)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.