Reduce complex graphs to the best paths

Interested? Contact muehlhaus@bio.uni-kl.de or weil@bio.uni-kl.de

Table of contents

Introduction

Imagine you're elected Minister of Infrastructure and tasked to build a road grid. This grid is supposed to connect all cities. Your budget is pretty tight though so the combined length of the roads should be as low as possible

Problem's like this are predetermined to be regarded as a graph problem. Graphs are structures that consist of two different kinds of components: - Vertices are the entities of the graph - Edges connect these vertices

Although simple in principal, graphs can become complex as they grow. Graph algorithms have emerged for all kinds of problems. One of them is finding minimum spanning trees which have been used to solve the problem described in the beginning.

Minimum spanning trees

A minimum spanning tree (MST) or minimum weight spanning tree is a subset of the edges of a connected, edge-weighted undirected graph that connects all the vertices together, without any cycles and with the minimum possible total edge weight.

As indicated above, MSTs have applications everytime someone needs to design an efficient grid. Examples for this might be electric grids, road grids or water pipe grids. Besides that, MSTs can be used for studying epidemeology by finding the shortest paths trough which pathogens might traverse a population network.

In biological research, MSTs might be used for visualization or clustering purposes. An example for this can be seen in the picture below, where phylogenetic groups were found and visualized using an MST.

drawing drawing

Several algorithms exist for finding MST, where often there is a tradeoff made between performance and quality of the result. Your task will be to implement one of them.

Prim's algorithm

Prim's algorithm is a simple, greedy approach for finding the MST of a network. Greedy approaches always find the best solution in exchange for lower performance.

In Prim's algorithm, you start a new graph by selecting a single vertex in the original graph. This new graph is repetitively grown by finding the closest connections to vertices not yet included in the MST.

Aim for this project


  • Get a basic understanding of network science
  • Implement Prim's algorithm for finding minimum spanning trees
  • Write a blogpost entry

References

Coding clues

General steps:

  1. Initialize a tree with a single vertex, chosen arbitrarily from the graph.
  2. Grow the tree by one edge: of the edges that connect the tree to vertices not yet in the tree, find the minimum-weight edge, and transfer it to the tree.
  3. Repeat step 2 (until all vertices are in the tree).

Using the graph library:

#r "nuget: FSharp.FGL, 0.0.2"

open FSharp.FGL
open FSharp.FGL.Undirected

// Create a new graph
let g : Graph<int,string,float> = Graph.empty


// Add vertices 
let v1 = (1,"VertexNumeroUno")
let v2 = (2,"VertexNumeroDos")

let gWithVertices = 
    Vertices.add v1 g
    |> Vertices.add v2

// Add edges
let e = (1,2,1.)

let gWithEdge = 
    Edges.add e gWithVertices

// Show all edges to find the best
Edges.toEdgeList

// Remove vertex (Including its edges)
Vertices.remove (fst v1) gWithEdge

Visualize the graph:

#r "nuget: Cyjs.NET, 0.0.4"

open Cyjs.NET

let inline toCyJS (g : Graph<'Vertex,'Label,float>) =
    let vertices = 
        g
        |> Vertices.toVertexList
        |> List.map (fun (id,name) -> Elements.node (string id) [CyParam.label (string name)])

    let edges =
        g
        |> Edges.toEdgeList
        |> List.map (fun (v1,v2,weight) -> Elements.edge (string v1 + string v2) (string v1) (string v2) [CyParam.weight (weight)])

    CyGraph.initEmpty ()
    |> CyGraph.withElements vertices
    |> CyGraph.withElements edges
    |> CyGraph.withStyle "node" [CyParam.content =. CyParam.label]
    |> CyGraph.withStyle "edge" [CyParam.content =. CyParam.weight]
gWithEdge
|> toCyJS
|> CyGraph.show

The visualized graph should look as follows:

No value returned by any evaluator

Additional information

Testing

For testing whether your implementation does return the correct results, you can use this website:

https://algorithms.discrete.ma.tum.de/graph-algorithms/mst-prim/index_en.html

Blog post

  • What is a minimum spanning tree
  • Classical application examples
  • Advantages and drawbacks of Prim's algorithm
  • Short description of the algorithm
Multiple items
namespace FSharp

--------------------
namespace Microsoft.FSharp
namespace FSharp.FGL
namespace FSharp.FGL.Undirected
val g : Graph<int,string,float>
Multiple items
module Graph from FSharp.FGL.Undirected

--------------------
module Graph from FSharp.FGL
<summary> General functions for both directed and undirected graphs </summary>

--------------------
type Graph<'Vertex,'Label,'Edge (requires comparison)> = Map<'Vertex,MContext<'Vertex,'Label,'Edge>>
<summary> Map of Vertices as keys and MContexts as values </summary>
Multiple items
val int : value:'T -> int (requires member op_Explicit)
<summary>Converts the argument to signed 32-bit integer. This is a direct conversion for all primitive numeric types. For strings, the input is converted using <c>Int32.Parse()</c> with InvariantCulture settings. Otherwise the operation requires an appropriate static conversion method on the input type.</summary>
<param name="value">The input value.</param>
<returns>The converted int</returns>


--------------------
[<Struct>] type int = int32
<summary>An abbreviation for the CLI type <see cref="T:System.Int32" />.</summary>
<category>Basic Types</category>


--------------------
type int<'Measure> = int
<summary>The type of 32-bit signed integer numbers, annotated with a unit of measure. The unit of measure is erased in compiled code and when values of this type are analyzed using reflection. The type is representationally equivalent to <see cref="T:System.Int32" />.</summary>
<category>Basic Types with Units of Measure</category>
Multiple items
val string : value:'T -> string
<summary>Converts the argument to a string using <c>ToString</c>.</summary>
<remarks>For standard integer and floating point values the and any type that implements <c>IFormattable</c><c>ToString</c> conversion uses <c>CultureInfo.InvariantCulture</c>. </remarks>
<param name="value">The input value.</param>
<returns>The converted string.</returns>


--------------------
type string = System.String
<summary>An abbreviation for the CLI type <see cref="T:System.String" />.</summary>
<category>Basic Types</category>
Multiple items
val float : value:'T -> float (requires member op_Explicit)
<summary>Converts the argument to 64-bit float. This is a direct conversion for all primitive numeric types. For strings, the input is converted using <c>Double.Parse()</c> with InvariantCulture settings. Otherwise the operation requires an appropriate static conversion method on the input type.</summary>
<param name="value">The input value.</param>
<returns>The converted float</returns>


--------------------
[<Struct>] type float = System.Double
<summary>An abbreviation for the CLI type <see cref="T:System.Double" />.</summary>
<category>Basic Types</category>


--------------------
type float<'Measure> = float
<summary>The type of double-precision floating point numbers, annotated with a unit of measure. The unit of measure is erased in compiled code and when values of this type are analyzed using reflection. The type is representationally equivalent to <see cref="T:System.Double" />.</summary>
<category index="6">Basic Types with Units of Measure</category>
val empty : Graph<'Vertex,'Label,'Edge> (requires comparison)
<summary> Creates a new, empty graph. </summary>
val v1 : int * string
val v2 : int * string
val gWithVertices : Graph<int,string,float>
Multiple items
module Vertices from FSharp.FGL.Undirected
<summary> Functions for vertices of undirected Graphs </summary>

--------------------
module Vertices from FSharp.FGL
<summary> Functions for vertices of both directed and undirected graphs </summary>
val add : 'Vertex * 'Label -> g:Graph<'Vertex,'Label,'Edge> -> Graph<'Vertex,'Label,'Edge> (requires comparison)
<summary> Adds a labeled vertex to the graph. </summary>
val e : int * int * float
val gWithEdge : Graph<int,string,float>
module Edges from FSharp.FGL.Undirected
<summary> Functions for edges of undirected Graphs </summary>
val add : 'Vertex * 'Vertex * 'Edge -> g:Graph<'Vertex,'Label,'Edge> -> Graph<'Vertex,'Label,'Edge> (requires comparison)
<summary> Adds a labeled, undirected edge to the graph. </summary>
val toEdgeList : g:Graph<'Vertex,'Label,'Edge> -> LEdge<'Vertex,'Edge> list (requires comparison and equality)
<summary> Creates a list of all edges and their labels. </summary>
val remove : v:'Vertex -> g:Graph<'Vertex,'Label,'Edge> -> Graph<'Vertex,'Label,'Edge> (requires comparison)
<summary> Removes a vertex from the graph. </summary>
val fst : tuple:('T1 * 'T2) -> 'T1
<summary>Return the first element of a tuple, <c>fst (a,b) = a</c>.</summary>
<param name="tuple">The input tuple.</param>
<returns>The first value.</returns>
namespace Cyjs
namespace Cyjs.NET
val toCyJS : g:Graph<'Vertex,'Label,float> -> CyGraph.CyGraph (requires comparison)
val g : Graph<'Vertex,'Label,float> (requires comparison)
val vertices : Elements.Node list
val toVertexList : g:Graph<'Vertex,'Label,'Edge> -> LVertex<'Vertex,'Label> list (requires comparison)
<summary> Creates a list of all vertices and their labels. </summary>
Multiple items
module List from Microsoft.FSharp.Collections
<summary>Contains operations for working with values of type <see cref="T:Microsoft.FSharp.Collections.list`1" />.</summary>
<namespacedoc><summary>Operations for collections such as lists, arrays, sets, maps and sequences. See also <a href="https://docs.microsoft.com/dotnet/fsharp/language-reference/fsharp-collection-types">F# Collection Types</a> in the F# Language Guide. </summary></namespacedoc>


--------------------
type List<'T> = | ( [] ) | ( :: ) of Head: 'T * Tail: 'T list interface IReadOnlyList<'T> interface IReadOnlyCollection<'T> interface IEnumerable interface IEnumerable<'T> member GetReverseIndex : rank:int * offset:int -> int member GetSlice : startIndex:int option * endIndex:int option -> 'T list static member Cons : head:'T * tail:'T list -> 'T list member Head : 'T member IsEmpty : bool member Item : index:int -> 'T with get ...
<summary>The type of immutable singly-linked lists.</summary>
<remarks>Use the constructors <c>[]</c> and <c>::</c> (infix) to create values of this type, or the notation <c>[1;2;3]</c>. Use the values in the <c>List</c> module to manipulate values of this type, or pattern match against the values directly. </remarks>
<exclude />
val map : mapping:('T -> 'U) -> list:'T list -> 'U list
<summary>Builds a new collection whose elements are the results of applying the given function to each of the elements of the collection.</summary>
<param name="mapping">The function to transform elements from the input list.</param>
<param name="list">The input list.</param>
<returns>The list of transformed elements.</returns>
val id : 'Vertex (requires comparison)
val name : 'Label
module Elements from Cyjs.NET
val node : id:string -> dataAttributes:CyParam.CyStyleParam list -> Elements.Node
module CyParam from Cyjs.NET
val label : v:'a -> CyParam.CyStyleParam
val edges : Elements.Edge list
val v1 : 'Vertex (requires comparison)
val v2 : 'Vertex (requires comparison)
val weight : float
val edge : id:string -> sourceId:string -> targetId:string -> dataAttributes:CyParam.CyStyleParam list -> Elements.Edge
val weight : v:'a -> CyParam.CyStyleParam
module CyGraph from Cyjs.NET
val initEmpty : unit -> CytoscapeModel.Cytoscape
val withElements : elems:seq<CytoscapeModel.Element> -> cy:CyGraph.CyGraph -> CyGraph.CyGraph
val withStyle : selector:string -> cyStyles:seq<CyParam.CyStyleParam> -> cy:CyGraph.CyGraph -> CyGraph.CyGraph
val content : v:'a -> CyParam.CyStyleParam
val show : cy:CyGraph.CyGraph -> unit
val withSize : width:int * height:int -> cy:CyGraph.CyGraph -> CyGraph.CyGraph
module HTML from Cyjs.NET
<summary> HTML template for Cytoscape </summary>
val toEmbeddedHTML : cy:CytoscapeModel.Cytoscape -> string
<summary> Converts a CyGraph to it HTML representation and embeds it into a html page. </summary>