MBrace.Core and MBrace.Azure


Example: Running an iterative algorithm at scale with incremental notifications

This example is from the MBrace Starter Kit.

This example shows how to implement the iterative algorithm k-Means, which finds centroids of clusters for points.

It shows some important techniques

  • How to partition data and keep affinity of workers to data
  • How to emit partial results to an intermediate queue
  • How to observe that queue using incremental charting

First define some parameters for the input set we want to classify:

1: 
2: 
3: 
4: 
5: 
let dim = 2 // point dimensions: we use 2 dimensions so we can chart the results
let numCentroids = 5 // The number of centroids to find
let partitions = 12 // The number of point partitions
let pointsPerPartition = 50000 // The number of points per partition
let epsilon = 0.1

Generate some random input data, a deterministic set of points based on the parameters above.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
/// Represents a multi-dimensional point.
type Point = float[]

/// Generates a set of points via a random walk from the origin, using provided seed.
let generatePoints dim numPoints seed : Point[] =
    let rand = Random(seed * 2003 + 22)
    let prev = Array.zeroCreate dim

    let nextPoint () =
        let arr = Array.zeroCreate dim
        for i = 0 to dim - 1 do 
            arr.[i] <- prev.[i] + rand.NextDouble() * 40.0 - 20.0
            prev.[i] <- arr.[i]
        arr

    [| for i in 1 .. numPoints -> nextPoint() |]

let randPoints = Array.init partitions (generatePoints dim pointsPerPartition)

Next you display a chart showing the first 500 points from each partition:

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
let point2d (p:Point) = p.[0], p.[1]

let selectionOfPoints = 
    [ for points in randPoints do 
         for i in 0 .. 100 .. points.Length-1 do
             yield point2d points.[i] ]

Chart.Point selectionOfPoints 

Giving Input to KMeans

Now you define a set of helper functions and types related to points and finding centroids:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
38: 
39: 
40: 
41: 
42: 
43: 
44: 
[<AutoOpen>]
module KMeansHelpers =

    /// Calculates the distance between two points.
    let dist (p1 : Point) (p2 : Point) = 
        Array.fold2 (fun acc e1 e2 -> acc + pown (e1 - e2) 2) 0.0 p1 p2

    /// Assigns a point to the correct centroid, and returns the index of that centroid.
    let findCentroid (p: Point) (centroids: Point[]) : int =
        let mutable mini = 0
        let mutable min = Double.PositiveInfinity
        for i = 0 to centroids.Length - 1 do
            let dist = dist p centroids.[i]
            if dist < min then
                min <- dist
                mini <- i

        mini

    /// Given a set of points, calculates the number of points assigned to each centroid.
    let kmeansLocal (points : Point[]) (centroids : Point[]) : (int * (int * Point))[] =
        let lens = Array.zeroCreate centroids.Length
        let sums = 
            Array.init centroids.Length (fun _ -> Array.zeroCreate centroids.[0].Length)

        for point in points do
            let cent = findCentroid point centroids
            lens.[cent] <- lens.[cent] + 1
            for i = 0 to point.Length - 1 do
                sums.[cent].[i] <- sums.[cent].[i] + point.[i]

        Array.init centroids.Length (fun i -> (i, (lens.[i], sums.[i])))

    /// Sums a collection of points
    let sumPoints (pointArr : Point []) dim : Point =
        let sum = Array.zeroCreate dim
        for p in pointArr do
            for i = 0 to dim - 1 do
                sum.[i] <- sum.[i] + p.[i]
        sum

    /// Scalar division of a point
    let divPoint (point : Point) (x : float) : Point =
        Array.map (fun p -> p / x) point

This is the iterative computation. Computes the new centroids based on classifying each point to an existing centroid. Then computes new centroids based on that classification. emit is used to emit observations of intermediate states to a queue or some other sink.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
let rec KMeansCloudIterate (partitionedPoints, epsilon, centroids, iteration, emit) = cloud {

     // Stage 1: map computations to each worker per point partition
    let! clusterParts =
        partitionedPoints
        |> Array.map (fun (p:CloudArray<_>, w) -> cloud { return kmeansLocal p.Value centroids }, w)
        |> Cloud.ParallelOnSpecificWorkers

    // Stage 2: reduce computations to obtain the new centroids
    let dim = centroids.[0].Length
    let newCentroids =
        clusterParts
        |> Array.concat
        |> ParStream.ofArray
        |> ParStream.groupBy fst
        |> ParStream.sortBy fst
        |> ParStream.map snd
        |> ParStream.map (fun clp -> clp |> Seq.map snd |> Seq.toArray |> Array.unzip)
        |> ParStream.map (fun (ns,points) -> Array.sum ns, sumPoints points dim)
        |> ParStream.map (fun (n, sum) -> divPoint sum (float n))
        |> ParStream.toArray

    // Stage 3: check convergence and decide whether to continue iteration
    let diff = Array.map2 dist newCentroids centroids |> Array.max

    do! Cloud.Logf "KMeans: iteration [#%d], diff %A with centroids /n%A" iteration diff centroids

    // emit an observation
    emit(DateTimeOffset.UtcNow,iteration,diff,centroids)

    if diff < epsilon then
        return newCentroids
    else
        return! KMeansCloudIterate (partitionedPoints, epsilon, newCentroids, iteration+1, emit)
}

            

The main cloud routine. Partitions the points according to the available workers, then iterates.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
let KMeansCloud(points, numCentroids, epsilon, emit) = cloud {  

    let initCentroids = points |> Seq.concat |> Seq.take numCentroids |> Seq.toArray

    let! workers = Cloud.GetAvailableWorkers()
    do! Cloud.Logf "KMeans: persisting partitioned point data to store."
        
    // Divide the points
    let! partitionedPoints = 
        points 
        |> Seq.mapi (fun i p -> 
            local { 
                // always schedule the same subset of points to the same worker
                // for caching performance gains
                let! ca = CloudValue.NewArray(p, StorageLevel.MemoryAndDisk) 
                return ca, workers.[i % workers.Length] }) 
        |> Local.Parallel

    do! Cloud.Logf "KMeans: persist completed, starting iteration."

    return! KMeansCloudIterate(partitionedPoints, epsilon, initCentroids, 1, emit) 
}

Running a test flight of the algorithm

You can now run a test flight of the algorithm with a drastically increased epsilon value to allow for more rapid convergence:

1: 
2: 
3: 
let kmeansTask = 
    KMeansCloud(randPoints, numCentroids, epsilon*10000.0, ignore) 
    |> cluster.CreateProcess

Take a look at progress

1: 
2: 
3: 
4: 
cluster.ShowWorkers()
cluster.ShowProcesses()
kmeansTask.ShowLogs()
kmeansTask.ShowInfo()

Get the result:

1: 
let centroids = kmeansTask.Result

Now chart a selection of the original points and the overall result

1: 
2: 
3: 
Chart.Combine   
    [ Chart.Point(selectionOfPoints)
      Chart.Point(centroids |> Array.map point2d, Color=Drawing.Color.Red) ]

Giving First results from KMeans

Observing intermediate states of the algorithm

Frequently when running iterative algorithms or long running processes you will need to emit information for visualization and inspection of the progress of the algorithm.

To do this, you create a queue to observe the partial output results from the iterations.

1: 
2: 
3: 
type Observation = DateTimeOffset*int*float*Point[]

let watchQueue =  CloudQueue.New<Observation>()  |> cluster.RunLocally

Next, you start the task, emitting observations to the queue:

1: 
2: 
3: 
let kmeansTask2 = 
    KMeansCloud(randPoints, numCentroids, epsilon, watchQueue.Enqueue) 
    |> cluster.CreateProcess

Take a look at progress

1: 
2: 
3: 
4: 
kmeansTask2.ShowLogs()
cluster.ShowWorkers()
cluster.ShowProcesses()
kmeansTask2.ShowInfo()

Next, you chart the intermediate results as they arrive as an incrementally updating chart:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
open FSharp.Control

asyncSeq { 
    let centroidsSoFar = ResizeArray()
    while true do
        match watchQueue.TryDequeue() with
        | Some (time, iteration, diff, centroids) -> 
                centroidsSoFar.Add centroids
                let d = [ for centroids in centroidsSoFar do for p in centroids -> point2d p ]
                yield d
                do! Async.Sleep 1000
        | None -> do! Async.Sleep 1000
} |> AsyncSeq.toObservable |> LiveChart.Point 

This produces the following incrementally:

Incremental results from KMeans

Now wait for the overall result:

1: 
let centroids2 = kmeansTask2.Result

Now chart the original points, the centroids we computed on the first flight, and the final centroids.

1: 
2: 
3: 
4: 
Chart.Combine   
    [ Chart.Point(selectionOfPoints)
      Chart.Point(centroids |> Array.map point2d, Color=Drawing.Color.Orange,MarkerSize=10) 
      Chart.Point(centroids2 |> Array.map point2d, Color=Drawing.Color.Red,MarkerSize=5) ]

Giving

The centroids found by the clustering

In this example, you've learned how to run an iterative algorithm on an MBrace cluster, including how to emit and observe intermediate states from the iterations. Continue with further samples to learn more about the MBrace programming model.

Note, you can use the above techniques from both scripts and compiled projects. To see the components referenced by this script, see ThespianCluster.fsx or AzureCluster.fsx.

namespace System
namespace Nessos
namespace Nessos.Streams
namespace MBrace
namespace MBrace.Core
Multiple items
namespace FSharp

--------------------
namespace Microsoft.FSharp
namespace FSharp.Charting
val cluster : MBrace.Thespian.ThespianCluster

Full name: 200-kmeans-clustering-example.cluster
module Config
val GetCluster : unit -> MBrace.Thespian.ThespianCluster

Full name: Config.GetCluster


 Gets or creates a new Thespian cluster session.
val dim : int

Full name: 200-kmeans-clustering-example.dim
val numCentroids : int

Full name: 200-kmeans-clustering-example.numCentroids
val partitions : int

Full name: 200-kmeans-clustering-example.partitions
val pointsPerPartition : int

Full name: 200-kmeans-clustering-example.pointsPerPartition
val epsilon : float

Full name: 200-kmeans-clustering-example.epsilon
type Point = float []

Full name: 200-kmeans-clustering-example.Point


 Represents a multi-dimensional point.
Multiple items
val float : value:'T -> float (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.float

--------------------
type float = Double

Full name: Microsoft.FSharp.Core.float

--------------------
type float<'Measure> = float

Full name: Microsoft.FSharp.Core.float<_>
val generatePoints : dim:int -> numPoints:int -> seed:int -> Point []

Full name: 200-kmeans-clustering-example.generatePoints


 Generates a set of points via a random walk from the origin, using provided seed.
val dim : int
val numPoints : int
val seed : int
val rand : Random
Multiple items
type Random =
  new : unit -> Random + 1 overload
  member Next : unit -> int + 2 overloads
  member NextBytes : buffer:byte[] -> unit
  member NextDouble : unit -> float

Full name: System.Random

--------------------
Random() : unit
Random(Seed: int) : unit
val prev : float []
type Array =
  member Clone : unit -> obj
  member CopyTo : array:Array * index:int -> unit + 1 overload
  member GetEnumerator : unit -> IEnumerator
  member GetLength : dimension:int -> int
  member GetLongLength : dimension:int -> int64
  member GetLowerBound : dimension:int -> int
  member GetUpperBound : dimension:int -> int
  member GetValue : [<ParamArray>] indices:int[] -> obj + 7 overloads
  member Initialize : unit -> unit
  member IsFixedSize : bool
  ...

Full name: System.Array
val zeroCreate : count:int -> 'T []

Full name: Microsoft.FSharp.Collections.Array.zeroCreate
val nextPoint : (unit -> float [])
val arr : float []
val i : int
Random.NextDouble() : float
val randPoints : Point [] []

Full name: 200-kmeans-clustering-example.randPoints
val init : count:int -> initializer:(int -> 'T) -> 'T []

Full name: Microsoft.FSharp.Collections.Array.init
val point2d : p:Point -> float * float

Full name: 200-kmeans-clustering-example.point2d
val p : Point
val selectionOfPoints : (float * float) list

Full name: 200-kmeans-clustering-example.selectionOfPoints
val points : Point []
property Array.Length: int
type Chart =
  static member Area : data:seq<#value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string -> GenericChart
  static member Area : data:seq<#key * #value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string -> GenericChart
  static member Bar : data:seq<#value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string -> GenericChart
  static member Bar : data:seq<#key * #value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string -> GenericChart
  static member BoxPlotFromData : data:seq<#key * #seq<'a2>> * ?Name:string * ?Title:string * ?Color:Color * ?XTitle:string * ?YTitle:string * ?Percentile:int * ?ShowAverage:bool * ?ShowMedian:bool * ?ShowUnusualValues:bool * ?WhiskerPercentile:int -> GenericChart (requires 'a2 :> value)
  static member BoxPlotFromStatistics : data:seq<#key * #value * #value * #value * #value * #value * #value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string * ?Percentile:int * ?ShowAverage:bool * ?ShowMedian:bool * ?ShowUnusualValues:bool * ?WhiskerPercentile:int -> GenericChart
  static member Bubble : data:seq<#value * #value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string * ?BubbleMaxSize:int * ?BubbleMinSize:int * ?BubbleScaleMax:float * ?BubbleScaleMin:float * ?UseSizeForLabel:bool -> GenericChart
  static member Bubble : data:seq<#key * #value * #value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string * ?BubbleMaxSize:int * ?BubbleMinSize:int * ?BubbleScaleMax:float * ?BubbleScaleMin:float * ?UseSizeForLabel:bool -> GenericChart
  static member Candlestick : data:seq<#value * #value * #value * #value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string -> CandlestickChart
  static member Candlestick : data:seq<#key * #value * #value * #value * #value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string -> CandlestickChart
  ...

Full name: FSharp.Charting.Chart
static member Chart.Point : data:seq<#value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Drawing.Color * ?XTitle:string * ?YTitle:string * ?MarkerColor:Drawing.Color * ?MarkerSize:int -> ChartTypes.GenericChart
static member Chart.Point : data:seq<#key * #value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Drawing.Color * ?XTitle:string * ?YTitle:string * ?MarkerColor:Drawing.Color * ?MarkerSize:int -> ChartTypes.GenericChart
Multiple items
type AutoOpenAttribute =
  inherit Attribute
  new : unit -> AutoOpenAttribute
  new : path:string -> AutoOpenAttribute
  member Path : string

Full name: Microsoft.FSharp.Core.AutoOpenAttribute

--------------------
new : unit -> AutoOpenAttribute
new : path:string -> AutoOpenAttribute
val dist : p1:Point -> p2:Point -> float

Full name: 200-kmeans-clustering-example.KMeansHelpers.dist


 Calculates the distance between two points.
val p1 : Point
val p2 : Point
val fold2 : folder:('State -> 'T1 -> 'T2 -> 'State) -> state:'State -> array1:'T1 [] -> array2:'T2 [] -> 'State

Full name: Microsoft.FSharp.Collections.Array.fold2
val acc : float
val e1 : float
val e2 : float
val pown : x:'T -> n:int -> 'T (requires member get_One and member ( * ) and member ( / ))

Full name: Microsoft.FSharp.Core.Operators.pown
val findCentroid : p:Point -> centroids:Point [] -> int

Full name: 200-kmeans-clustering-example.KMeansHelpers.findCentroid


 Assigns a point to the correct centroid, and returns the index of that centroid.
val centroids : Point []
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
val mutable mini : int
val mutable min : float
type Double =
  struct
    member CompareTo : value:obj -> int + 1 overload
    member Equals : obj:obj -> bool + 1 overload
    member GetHashCode : unit -> int
    member GetTypeCode : unit -> TypeCode
    member ToString : unit -> string + 3 overloads
    static val MinValue : float
    static val MaxValue : float
    static val Epsilon : float
    static val NegativeInfinity : float
    static val PositiveInfinity : float
    ...
  end

Full name: System.Double
field float.PositiveInfinity = Infinity
val dist : float
val kmeansLocal : points:Point [] -> centroids:Point [] -> (int * (int * Point)) []

Full name: 200-kmeans-clustering-example.KMeansHelpers.kmeansLocal


 Given a set of points, calculates the number of points assigned to each centroid.
val lens : int []
val sums : float [] []
val point : Point
val cent : int
val sumPoints : pointArr:Point [] -> dim:int -> Point

Full name: 200-kmeans-clustering-example.KMeansHelpers.sumPoints


 Sums a collection of points
val pointArr : Point []
val sum : float []
val divPoint : point:Point -> x:float -> Point

Full name: 200-kmeans-clustering-example.KMeansHelpers.divPoint


 Scalar division of a point
val x : float
val map : mapping:('T -> 'U) -> array:'T [] -> 'U []

Full name: Microsoft.FSharp.Collections.Array.map
val p : float
val KMeansCloudIterate : partitionedPoints:'a * epsilon:'b * centroids:'c * iteration:'d * emit:'e -> 'f

Full name: 200-kmeans-clustering-example.KMeansCloudIterate
val partitionedPoints : 'a
val epsilon : 'b
val centroids : 'c
val iteration : 'd
val emit : 'e
val concat : arrays:seq<'T []> -> 'T []

Full name: Microsoft.FSharp.Collections.Array.concat
Multiple items
module ParStream

from Nessos.Streams

--------------------
type ParStream<'T> =
  private {Impl: ParStreamImpl<'T>;}
  member Apply : collector:ParCollector<'T,'R> -> 'R
  member private Stream : unit -> Stream<'T>
  member DegreeOfParallelism : int
  member private PreserveOrdering : bool
  member private SourceType : SourceType

Full name: Nessos.Streams.ParStream<_>
val ofArray : source:'T [] -> ParStream<'T>

Full name: Nessos.Streams.ParStream.ofArray
val groupBy : projection:('T -> 'Key) -> stream:ParStream<'T> -> ParStream<'Key * seq<'T>> (requires equality)

Full name: Nessos.Streams.ParStream.groupBy
val fst : tuple:('T1 * 'T2) -> 'T1

Full name: Microsoft.FSharp.Core.Operators.fst
val sortBy : projection:('T -> 'Key) -> stream:ParStream<'T> -> ParStream<'T> (requires comparison)

Full name: Nessos.Streams.ParStream.sortBy
val map : f:('T -> 'R) -> stream:ParStream<'T> -> ParStream<'R>

Full name: Nessos.Streams.ParStream.map
val snd : tuple:('T1 * 'T2) -> 'T2

Full name: Microsoft.FSharp.Core.Operators.snd
module Seq

from Microsoft.FSharp.Collections
val map : mapping:('T -> 'U) -> source:seq<'T> -> seq<'U>

Full name: Microsoft.FSharp.Collections.Seq.map
val toArray : source:seq<'T> -> 'T []

Full name: Microsoft.FSharp.Collections.Seq.toArray
val unzip : array:('T1 * 'T2) [] -> 'T1 [] * 'T2 []

Full name: Microsoft.FSharp.Collections.Array.unzip
val sum : array:'T [] -> 'T (requires member ( + ) and member get_Zero)

Full name: Microsoft.FSharp.Collections.Array.sum
val toArray : stream:ParStream<'T> -> 'T []

Full name: Nessos.Streams.ParStream.toArray
val map2 : mapping:('T1 -> 'T2 -> 'U) -> array1:'T1 [] -> array2:'T2 [] -> 'U []

Full name: Microsoft.FSharp.Collections.Array.map2
val max : array:'T [] -> 'T (requires comparison)

Full name: Microsoft.FSharp.Collections.Array.max
Multiple items
type DateTimeOffset =
  struct
    new : dateTime:DateTime -> DateTimeOffset + 5 overloads
    member Add : timeSpan:TimeSpan -> DateTimeOffset
    member AddDays : days:float -> DateTimeOffset
    member AddHours : hours:float -> DateTimeOffset
    member AddMilliseconds : milliseconds:float -> DateTimeOffset
    member AddMinutes : minutes:float -> DateTimeOffset
    member AddMonths : months:int -> DateTimeOffset
    member AddSeconds : seconds:float -> DateTimeOffset
    member AddTicks : ticks:int64 -> DateTimeOffset
    member AddYears : years:int -> DateTimeOffset
    ...
  end

Full name: System.DateTimeOffset

--------------------
DateTimeOffset()
DateTimeOffset(dateTime: DateTime) : unit
DateTimeOffset(ticks: int64, offset: TimeSpan) : unit
DateTimeOffset(dateTime: DateTime, offset: TimeSpan) : unit
DateTimeOffset(year: int, month: int, day: int, hour: int, minute: int, second: int, offset: TimeSpan) : unit
DateTimeOffset(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int, offset: TimeSpan) : unit
DateTimeOffset(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int, calendar: Globalization.Calendar, offset: TimeSpan) : unit
property DateTimeOffset.UtcNow: DateTimeOffset
val KMeansCloud : points:'a * numCentroids:'b * epsilon:'c * emit:'d -> 'e

Full name: 200-kmeans-clustering-example.KMeansCloud
val points : 'a
val numCentroids : 'b
val epsilon : 'c
val emit : 'd
val concat : sources:seq<#seq<'T>> -> seq<'T>

Full name: Microsoft.FSharp.Collections.Seq.concat
val take : count:int -> source:seq<'T> -> seq<'T>

Full name: Microsoft.FSharp.Collections.Seq.take
val mapi : mapping:(int -> 'T -> 'U) -> source:seq<'T> -> seq<'U>

Full name: Microsoft.FSharp.Collections.Seq.mapi
val kmeansTask : MBrace.Runtime.CloudProcess<Point []>

Full name: 200-kmeans-clustering-example.kmeansTask
val ignore : value:'T -> unit

Full name: Microsoft.FSharp.Core.Operators.ignore
member MBrace.Runtime.MBraceClient.CreateProcess : workflow:MBrace.Core.Cloud<'T> * ?cancellationToken:MBrace.Core.ICloudCancellationToken * ?faultPolicy:MBrace.Core.FaultPolicy * ?target:MBrace.Core.IWorkerRef * ?additionalResources:MBrace.Core.Internals.ResourceRegistry * ?taskName:string -> MBrace.Runtime.CloudProcess<'T>
member MBrace.Runtime.MBraceClient.ShowWorkers : unit -> unit
member MBrace.Runtime.MBraceClient.ShowProcesses : unit -> unit
override MBrace.Runtime.CloudProcess.ShowLogs : ?filter:(MBrace.Runtime.CloudLogEntry -> bool) -> unit
member MBrace.Runtime.CloudProcess.ShowInfo : unit -> unit
val centroids : Point []

Full name: 200-kmeans-clustering-example.centroids
property MBrace.Runtime.CloudProcess.Result: Point []
static member Chart.Combine : charts:seq<ChartTypes.GenericChart> -> ChartTypes.GenericChart
namespace System.Drawing
type Color =
  struct
    member A : byte
    member B : byte
    member Equals : obj:obj -> bool
    member G : byte
    member GetBrightness : unit -> float32
    member GetHashCode : unit -> int
    member GetHue : unit -> float32
    member GetSaturation : unit -> float32
    member IsEmpty : bool
    member IsKnownColor : bool
    ...
  end

Full name: System.Drawing.Color
property Drawing.Color.Red: Drawing.Color
type Observation = DateTimeOffset * int * float * Point []

Full name: 200-kmeans-clustering-example.Observation
val watchQueue : obj

Full name: 200-kmeans-clustering-example.watchQueue
member MBrace.Runtime.MBraceClient.RunLocally : workflow:MBrace.Core.Cloud<'T> * ?cancellationToken:MBrace.Core.ICloudCancellationToken * ?memoryEmulation:MBrace.Core.MemoryEmulation -> 'T
val kmeansTask2 : MBrace.Runtime.CloudProcess<Point []>

Full name: 200-kmeans-clustering-example.kmeansTask2
Multiple items
namespace FSharp.Control

--------------------
namespace Microsoft.FSharp.Control
val asyncSeq : AsyncSeq.AsyncSeqBuilder

Full name: FSharp.Control.AsyncSeqExtensions.asyncSeq
val centroidsSoFar : Collections.Generic.List<seq<Point>>
type ResizeArray<'T> = Collections.Generic.List<'T>

Full name: Microsoft.FSharp.Collections.ResizeArray<_>
union case Option.Some: Value: 'T -> Option<'T>
val time : obj
val iteration : obj
val diff : obj
val centroids : seq<Point>
Collections.Generic.List.Add(item: seq<Point>) : unit
val d : (float * float) list
Multiple items
type Async
static member AsBeginEnd : computation:('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit)
static member AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate)
static member AwaitIAsyncResult : iar:IAsyncResult * ?millisecondsTimeout:int -> Async<bool>
static member AwaitTask : task:Task -> Async<unit>
static member AwaitTask : task:Task<'T> -> Async<'T>
static member AwaitWaitHandle : waitHandle:WaitHandle * ?millisecondsTimeout:int -> Async<bool>
static member CancelDefaultToken : unit -> unit
static member Catch : computation:Async<'T> -> Async<Choice<'T,exn>>
static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg:'Arg1 * beginAction:('Arg1 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * beginAction:('Arg1 * 'Arg2 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * arg3:'Arg3 * beginAction:('Arg1 * 'Arg2 * 'Arg3 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromContinuations : callback:(('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T>
static member Ignore : computation:Async<'T> -> Async<unit>
static member OnCancel : interruption:(unit -> unit) -> Async<IDisposable>
static member Parallel : computations:seq<Async<'T>> -> Async<'T []>
static member RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:CancellationToken -> 'T
static member Sleep : millisecondsDueTime:int -> Async<unit>
static member Start : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions * ?cancellationToken:CancellationToken -> Task<'T>
static member StartChild : computation:Async<'T> * ?millisecondsTimeout:int -> Async<Async<'T>>
static member StartChildAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions -> Async<Task<'T>>
static member StartImmediate : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartWithContinuations : computation:Async<'T> * continuation:('T -> unit) * exceptionContinuation:(exn -> unit) * cancellationContinuation:(OperationCanceledException -> unit) * ?cancellationToken:CancellationToken -> unit
static member SwitchToContext : syncContext:SynchronizationContext -> Async<unit>
static member SwitchToNewThread : unit -> Async<unit>
static member SwitchToThreadPool : unit -> Async<unit>
static member TryCancelled : computation:Async<'T> * compensation:(OperationCanceledException -> unit) -> Async<'T>
static member CancellationToken : Async<CancellationToken>
static member DefaultCancellationToken : CancellationToken

Full name: Microsoft.FSharp.Control.Async

--------------------
type Async<'T>

Full name: Microsoft.FSharp.Control.Async<_>
static member Async.Sleep : millisecondsDueTime:int -> Async<unit>
union case Option.None: Option<'T>
Multiple items
module AsyncSeq

from FSharp.Control

--------------------
type AsyncSeq<'T> = IAsyncEnumerable<'T>

Full name: FSharp.Control.AsyncSeq<_>
val toObservable : source:AsyncSeq<'T> -> IObservable<'T>

Full name: FSharp.Control.AsyncSeq.toObservable
type LiveChart =
  static member Area : data:IObservable<#seq<'a1 * 'a2>> * ?Name:string * ?Title:string * ?Color:Color * ?XTitle:string * ?YTitle:string -> GenericChart (requires 'a1 :> key and 'a2 :> value)
  static member Bar : data:IObservable<#seq<'a1 * 'a2>> * ?Name:string * ?Title:string * ?Color:Color * ?XTitle:string * ?YTitle:string -> GenericChart (requires 'a1 :> key and 'a2 :> value)
  static member Bubble : data:IObservable<#seq<'a1 * 'a2 * 'a3>> * ?Name:string * ?Title:string * ?Color:Color * ?XTitle:string * ?YTitle:string * ?BubbleMaxSize:int * ?BubbleMinSize:int * ?BubbleScaleMax:float * ?BubbleScaleMin:float * ?UseSizeForLabel:bool -> GenericChart (requires 'a1 :> key and 'a2 :> value and 'a3 :> value)
  static member BubbleIncremental : data:IObservable<#key * #value * #value> * ?Name:string * ?Title:string * ?Color:Color * ?XTitle:string * ?YTitle:string * ?BubbleMaxSize:int * ?BubbleMinSize:int * ?BubbleScaleMax:float * ?BubbleScaleMin:float * ?UseSizeForLabel:bool -> GenericChart
  static member Candlestick : data:IObservable<#seq<'a1 * 'a2 * 'a3 * 'a4 * 'a5>> * ?Name:string * ?Title:string * ?Color:Color * ?XTitle:string * ?YTitle:string -> CandlestickChart (requires 'a1 :> key and 'a2 :> value and 'a3 :> value and 'a4 :> value and 'a5 :> value)
  static member Candlestick : data:IObservable<seq<#value * #value * #value * #value>> * ?Name:string * ?Title:string * ?Color:Color * ?XTitle:string * ?YTitle:string -> CandlestickChart
  static member CandlestickIncremental : data:IObservable<#key * #value * #value * #value * #value> * ?Name:string * ?Title:string * ?Color:Color * ?XTitle:string * ?YTitle:string -> CandlestickChart
  static member CandlestickIncremental : data:IObservable<'a0 * #value * #value * #value> * ?Name:string * ?Title:string * ?Color:Color * ?XTitle:string * ?YTitle:string -> CandlestickChart (requires 'a0 :> key and 'a0 :> value)
  static member Column : data:IObservable<#seq<'a1 * 'a2>> * ?Name:string * ?Title:string * ?Color:Color * ?XTitle:string * ?YTitle:string * ?ColumnWidth:float -> GenericChart (requires 'a1 :> key and 'a2 :> value)
  static member ColumnIncremental : data:IObservable<#key * #value> * ?Name:string * ?Title:string * ?Color:Color * ?XTitle:string * ?YTitle:string * ?ColumnWidth:float -> GenericChart
  ...

Full name: FSharp.Charting.LiveChart
static member LiveChart.Point : data:IObservable<#seq<'a1 * 'a2>> * ?Name:string * ?Title:string * ?Color:Drawing.Color * ?XTitle:string * ?YTitle:string * ?MarkerColor:Drawing.Color * ?MarkerSize:int -> ChartTypes.GenericChart (requires 'a1 :> key and 'a2 :> value)
val centroids2 : Point []

Full name: 200-kmeans-clustering-example.centroids2
property Drawing.Color.Orange: Drawing.Color
Fork me on GitHub