What is AdjGraph

The AdjGraph is an undirected adaptation of the FGraph. It combines the predecessors and successors into just one element called neighbours.

Quickstart

Creating an empty graph and filling it with single elements

Begin by creating an empty graph,meaning a data structure with no nodes or edges. Then populate the graph with single elements, individual nodes are added one by one, and edges can be introduced to establish connections between them.

open Graphoscope
open AdjGraph

let graphToFillAdjGraph =

    AdjGraph.empty
    |> AdjGraph.addNode 1 "1"
    |> AdjGraph.addNode 2 "2"
    |> AdjGraph.addEdge 1 2 1.
"You have created a graph with 2 nodes and 1 edges"

Working with Graphs

Creating a Graph using AdjGraph

Creating an empty graph and add collections of elements

Another way of creating a graph is by filling it with collections of nodes and edges as seen below:

let graphToFillAdjGraph' =
    
    let nodes = List.init 100 (fun x -> x,$"{x}")

    let edges = List.init 45 (fun x -> x,x*2,1.)

    AdjGraph.empty
    |> AdjGraph.addNodes nodes
    |> AdjGraph.addEdges edges
"You have created a graph with 100 nodes and 45 edges"

Removing Nodes and Edges

To remove Nodes or Edges you can just use the remove functions provided:

let graphWithRemovedElementsAdjGraph =
    graphToFillAdjGraph'
    |> AdjGraph.removeEdge 1 2
    |> AdjGraph.removeNode 0
"You have reduced the graph to 99 nodes and 43 edges"

From Data

Import a graph

This is the well-known and much-used Zachary karate club network. The data was collected from the members of a university karate club by Wayne Zachary in 1977. Each node represents a member of the club, and each edge represents a tie between two members of the club. The network is undirected.

open FSharp.Data

let getElementOfFile (fullpath: string) (delimiter: string) (headerRows: int) (weightsIncluded: bool)  = 
        let rows  = CsvFile.Load(fullpath, delimiter, skipRows = headerRows, hasHeaders = false).Rows
        rows
        |> Seq.map (fun row -> int row[0],int row[0], int row[1],int row[1], if weightsIncluded then float row[2] else 1.0)


let karateFileAdj= __SOURCE_DIRECTORY__ + "/../tests/Graphoscope.Tests/ReferenceGraphs/zachary.txt"

let karateGraphAdj = 
  let g = AdjGraph.empty<int,int,float>
  getElementOfFile karateFileAdj " " 2 false
  |> Seq.iter(fun (s1,s2,t1,t2,w: float) -> AdjGraph.addElement s1 s2 t1 t2 w g|>ignore)
  g

let's use Cytoscape.NET for visualization:

open Cytoscape.NET
let vizGraph =
    CyGraph.initEmpty ()
    |> CyGraph.withElements [
            for (sk,s,tk,t,el) in (AdjGraph.toSeq karateGraphAdj) do
                let sk, tk = (string sk), (string tk)
                yield Elements.node sk [ CyParam.label s ]
                yield Elements.node tk [ CyParam.label t ]
                yield Elements.edge  (sprintf "%s_%s" sk tk) sk tk [ CyParam.label el ]
        ]
    |> CyGraph.withStyle "node"     
        [
            CyParam.content =. CyParam.label
            CyParam.color "#A00975"
        ]
    |> CyGraph.withLayout(Cytoscape.NET.Layout.initBreadthfirst(id))
namespace Graphoscope
Multiple items
module AdjGraph from Graphoscope

--------------------
type AdjGraph = new: unit -> AdjGraph static member addEdge: sourceKey: 'NodeKey -> targetKey: 'NodeKey -> data: 'EdgeData -> graph: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> AdjGraph<'NodeKey,'NodeData,'EdgeData> (requires comparison) static member addEdges: edgeSeq: seq<'NodeKey * 'NodeKey * 'EdgeData> -> g: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> AdjGraph<'NodeKey,'NodeData,'EdgeData> (requires comparison) static member addElement: nk1: 'NodeKey -> nd1: 'NodeData -> nk2: 'NodeKey -> nd2: 'NodeData -> ed: 'EdgeData -> g: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> AdjGraph<'NodeKey,'NodeData,'EdgeData> (requires comparison) static member addNode: nk: 'NodeKey -> nd: 'NodeData -> g: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> AdjGraph<'NodeKey,'NodeData,'EdgeData> (requires comparison) static member addNodes: nodeSeq: seq<'NodeKey * 'NodeData> -> g: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> AdjGraph<'NodeKey,'NodeData,'EdgeData> (requires comparison) static member containsEdge: v1: 'NodeKey -> v2: 'NodeKey -> g: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> bool (requires comparison) static member containsNode: vk: 'NodeKey -> g: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> bool (requires comparison) static member countEdges: graph: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> int (requires comparison) static member countNodes: g: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> int (requires comparison) ...
<summary> Functions to operate on the AdjGraph representation </summary>

--------------------
type AdjGraph<'NodeKey,'NodeData,'EdgeData (requires comparison)> = System.Collections.Generic.Dictionary<'NodeKey,('NodeData * System.Collections.Generic.Dictionary<'NodeKey,'EdgeData>)>
<summary> Basic Adjacency Graph representation </summary>

--------------------
new: unit -> AdjGraph
val graphToFillAdjGraph: AdjGraph<int,string,float>
val empty<'NodeKey,'NodeData,'EdgeData (requires comparison)> : AdjGraph<'NodeKey,'NodeData,'EdgeData> (requires comparison)
<summary> Creates an empty Adjacency Graph </summary>
<returns>Empty AdjGraph</returns>
static member AdjGraph.addNode: nk: 'NodeKey -> nd: 'NodeData -> g: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> AdjGraph<'NodeKey,'NodeData,'EdgeData> (requires comparison)
static member AdjGraph.addEdge: sourceKey: 'NodeKey -> targetKey: 'NodeKey -> data: 'EdgeData -> graph: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> AdjGraph<'NodeKey,'NodeData,'EdgeData> (requires comparison)
val graphToFillOutput: string
val sprintf: format: Printf.StringFormat<'T> -> 'T
<summary>Print to a string using the given format.</summary>
<param name="format">The formatter.</param>
<returns>The formatted result.</returns>
<example>See <c>Printf.sprintf</c> (link: <see cref="M:Microsoft.FSharp.Core.PrintfModule.PrintFormatToStringThen``1" />) for examples.</example>
static member AdjGraph.countNodes: g: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> int (requires comparison)
static member AdjGraph.countEdges: graph: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> int (requires comparison)
val graphToFillAdjGraph': AdjGraph<int,string,float>
val nodes: (int * string) list
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> = | op_Nil | op_ColonColon 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 init: length: int -> initializer: (int -> 'T) -> 'T list
<summary>Creates a list by calling the given generator on each index.</summary>
<param name="length">The length of the list to generate.</param>
<param name="initializer">The function to generate an element from an index.</param>
<exception cref="T:System.ArgumentException">Thrown when the input length is negative.</exception>
<returns>The list of generated elements.</returns>
<example id="init-1"><code lang="fsharp"> List.init 4 (fun v -&gt; v + 5) </code> Evaluates to <c>[5; 6; 7; 8]</c></example>
<example id="init-2"><code lang="fsharp"> List.init -5 (fun v -&gt; v + 5) </code> Throws <c>ArgumentException</c></example>
val x: int
val edges: (int * int * float) list
static member AdjGraph.addNodes: nodeSeq: seq<'NodeKey * 'NodeData> -> g: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> AdjGraph<'NodeKey,'NodeData,'EdgeData> (requires comparison)
static member AdjGraph.addEdges: edgeSeq: seq<'NodeKey * 'NodeKey * 'EdgeData> -> g: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> AdjGraph<'NodeKey,'NodeData,'EdgeData> (requires comparison)
val graphToFill2Output: string
val graphWithRemovedElementsAdjGraph: AdjGraph<int,string,float>
static member AdjGraph.removeEdge: nkSource: 'NodeKey -> nkTarget: 'NodeKey -> g: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> AdjGraph<'NodeKey,'NodeData,'EdgeData> (requires comparison)
static member AdjGraph.removeNode: nk: 'NodeKey -> g: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> AdjGraph<'NodeKey,'NodeData,'EdgeData> (requires comparison)
val removing: string
Multiple items
namespace FSharp

--------------------
namespace Microsoft.FSharp
Multiple items
namespace FSharp.Data

--------------------
namespace Microsoft.FSharp.Data
val getElementOfFile: fullpath: string -> delimiter: string -> headerRows: int -> weightsIncluded: bool -> seq<int * int * int * int * float>
val fullpath: string
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>
<example id="string-example"><code lang="fsharp"></code></example>


--------------------
type string = System.String
<summary>An abbreviation for the CLI type <see cref="T:System.String" />.</summary>
<category>Basic Types</category>
val delimiter: string
val headerRows: int
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>
<example id="int-example"><code lang="fsharp"></code></example>


--------------------
[<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>
val weightsIncluded: bool
[<Struct>] type bool = System.Boolean
<summary>An abbreviation for the CLI type <see cref="T:System.Boolean" />.</summary>
<category>Basic Types</category>
val rows: seq<CsvRow>
type CsvFile = inherit CsvFile<CsvRow> member GetColumnIndex: columnName: string -> int member TryGetColumnIndex: columnName: string -> int option static member AsyncLoad: uri: string * ?separators: string * ?quote: char * ?hasHeaders: bool * ?ignoreErrors: bool * ?skipRows: int * ?encoding: Encoding -> Async<CsvFile> static member Load: stream: Stream * ?separators: string * ?quote: char * ?hasHeaders: bool * ?ignoreErrors: bool * ?skipRows: int -> CsvFile + 2 overloads static member Parse: text: string * ?separators: string * ?quote: char * ?hasHeaders: bool * ?ignoreErrors: bool * ?skipRows: int -> CsvFile
<summary> Represents a CSV file. The lines are read on demand from <c>reader</c>. Columns are delimited by one of the chars passed by <c>separators</c> (defaults to just <c>,</c>), and to escape the separator chars, the <c>quote</c> character will be used (defaults to <c>"</c>). If <c>hasHeaders</c> is true (the default), the first line read by <c>reader</c> will not be considered part of data. If <c>ignoreErrors</c> is true (the default is false), rows with a different number of columns from the header row (or the first row if headers are not present) will be ignored. The first <c>skipRows</c> lines will be skipped. </summary>
static member CsvFile.Load: reader: System.IO.TextReader * ?separators: string * ?quote: char * ?hasHeaders: bool * ?ignoreErrors: bool * ?skipRows: int -> CsvFile
static member CsvFile.Load: stream: System.IO.Stream * ?separators: string * ?quote: char * ?hasHeaders: bool * ?ignoreErrors: bool * ?skipRows: int -> CsvFile
static member CsvFile.Load: uri: string * ?separators: string * ?quote: char * ?hasHeaders: bool * ?ignoreErrors: bool * ?skipRows: int * ?encoding: System.Text.Encoding -> CsvFile
module Seq from Microsoft.FSharp.Collections
<summary>Contains operations for working with values of type <see cref="T:Microsoft.FSharp.Collections.seq`1" />.</summary>
val map: mapping: ('T -> 'U) -> source: seq<'T> -> seq<'U>
<summary>Builds a new collection whose elements are the results of applying the given function to each of the elements of the collection. The given function will be applied as elements are demanded using the <c>MoveNext</c> method on enumerators retrieved from the object.</summary>
<remarks>The returned sequence may be passed between threads safely. However, individual IEnumerator values generated from the returned sequence should not be accessed concurrently.</remarks>
<param name="mapping">A function to transform items from the input sequence.</param>
<param name="source">The input sequence.</param>
<returns>The result sequence.</returns>
<exception cref="T:System.ArgumentNullException">Thrown when the input sequence is null.</exception>
<example id="item-1"><code lang="fsharp"> let inputs = ["a"; "bbb"; "cc"] inputs |&gt; Seq.map (fun x -&gt; x.Length) </code> Evaluates to a sequence yielding the same results as <c>seq { 1; 3; 2 }</c></example>
val row: CsvRow
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>
<example id="float-example"><code lang="fsharp"></code></example>


--------------------
[<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 karateFileAdj: string
val karateGraphAdj: AdjGraph<int,int,float>
val g: AdjGraph<int,int,float>
val iter: action: ('T -> unit) -> source: seq<'T> -> unit
<summary>Applies the given function to each element of the collection.</summary>
<param name="action">A function to apply to each element of the sequence.</param>
<param name="source">The input sequence.</param>
<exception cref="T:System.ArgumentNullException">Thrown when the input sequence is null.</exception>
<example id="iter-1"><code lang="fsharp"> ["a"; "b"; "c"] |&gt; Seq.iter (printfn "%s") </code> Evaluates to <c>unit</c> and prints <code> a b c </code> in the console. </example>
val s1: int
val s2: int
val t1: int
val t2: int
val w: float
static member AdjGraph.addElement: nk1: 'NodeKey -> nd1: 'NodeData -> nk2: 'NodeKey -> nd2: 'NodeData -> ed: 'EdgeData -> g: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> AdjGraph<'NodeKey,'NodeData,'EdgeData> (requires comparison)
val ignore: value: 'T -> unit
<summary>Ignore the passed value. This is often used to throw away results of a computation.</summary>
<param name="value">The value to ignore.</param>
<example id="min-example"><code lang="fsharp"> ignore 55555 // Evaluates to () </code></example>
namespace Cytoscape
namespace Cytoscape.NET
val vizGraph: CyGraph.CyGraph
module CyGraph from Cytoscape.NET
val initEmpty: unit -> CyGraph.CyGraph
val withElements: elems: seq<CytoscapeModel.Element> -> cy: CyGraph.CyGraph -> CyGraph.CyGraph
val sk: int
val s: int
val tk: int
val t: int
val el: float
static member AdjGraph.toSeq: graph: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> seq<'NodeKey * 'NodeData * 'NodeKey * 'NodeData * 'EdgeData> (requires comparison and comparison and comparison)
val sk: string
val tk: string
module Elements from Cytoscape.NET
val node: id: string -> dataAttributes: CyParam.CyStyleParam list -> Elements.Node
module CyParam from Cytoscape.NET
val label: v: 'a -> CyParam.CyStyleParam
val edge: id: string -> sourceId: string -> targetId: string -> dataAttributes: CyParam.CyStyleParam list -> Elements.Edge
val withStyle: selector: string -> cyStyles: seq<CyParam.CyStyleParam> -> cy: CyGraph.CyGraph -> CyGraph.CyGraph
val content: v: 'a -> CyParam.CyStyleParam
val color: v: 'a -> CyParam.CyStyleParam
val withLayout: ly: Layout -> cy: CyGraph.CyGraph -> CyGraph.CyGraph
Multiple items
module Layout from Cytoscape.NET

--------------------
type Layout = inherit DynamicObj new: name: string -> Layout member name: string
<summary> Layout type inherits from dynamic object </summary>

--------------------
new: name: string -> Layout
val initBreadthfirst: applyOption: (Layout -> Layout) -> Layout
<summary> initializes a layout of type "breadthfirst" applying the givin layout option function. The "breadthfirst" layout puts nodes in a hierarchy, based on a breadthfirst traversal of the graph. It is best suited to trees and forests in its default top-down mode, and it is best suited to DAGs in its circle mode. </summary>
val id: x: 'T -> 'T
<summary>The identity function</summary>
<param name="x">The input value.</param>
<returns>The same value.</returns>
<example id="id-example"><code lang="fsharp"> id 12 // Evaulates to 12 id "abc" // Evaulates to "abc" </code></example>
val withZoom: zoom: CytoscapeModel.Zoom -> cy: CyGraph.CyGraph -> CyGraph.CyGraph
namespace Cytoscape.NET.CytoscapeModel
Multiple items
type Zoom = inherit DynamicObj new: unit -> Zoom static member Init: ?Level: float * ?Position: (int * int) * ?RenderedPosition: (int * int) * ?ZoomingEnabled: bool -> Zoom static member Update: ?Level: float * ?Position: (int * int) * ?RenderedPosition: (int * int) * ?ZoomingEnabled: bool -> (Zoom -> Zoom)

--------------------
new: unit -> CytoscapeModel.Zoom
static member CytoscapeModel.Zoom.Init: ?Level: float * ?Position: (int * int) * ?RenderedPosition: (int * int) * ?ZoomingEnabled: bool -> CytoscapeModel.Zoom
val withSize: width: int * height: int -> cy: CyGraph.CyGraph -> CyGraph.CyGraph
type HTML = static member CreateGraphHTML: graphData: string * zooming: string * divId: string * cytoscapeReference: CytoscapeJSReference * ?Width: int * ?Height: int -> XmlNode list static member CreateGraphScript: graphData: string * zooming: string * cytoscapeReference: CytoscapeJSReference -> XmlNode static member Doc: graphHTML: XmlNode list * cytoscapeReference: CytoscapeJSReference * ?AdditionalHeadTags: XmlNode list -> XmlNode static member show: cy: Cytoscape * ?DisplayOpts: DisplayOptions -> unit static member toEmbeddedHTML: ?DisplayOpts: DisplayOptions -> (Cytoscape -> string) static member toGraphHTML: ?DisplayOpts: DisplayOptions -> (Cytoscape -> string) static member toGraphHTMLNodes: ?DisplayOpts: DisplayOptions -> (Cytoscape -> XmlNode list)
<summary> HTML template for Cytoscape </summary>
static member HTML.toGraphHTML: ?DisplayOpts: DisplayOptions -> (CytoscapeModel.Cytoscape -> string)