3
votes

I am trying to calculate covariance matrix

The operator 'expr.[idx]' has been used on an object of indeterminate type based on information prior to this program point. Consider adding further type constraints

CovMatrix.[a,b]

How to fix this and why it happens?

let FirstSeq = {1.0..10.0}
let SecondSeq = {20.0..30.0}

let All = seq {yield (0,FirstSeq); yield (1,SecondSeq)}

let cov(first:seq<float>)(second:seq<float>) =
    Seq.map2 (*) first second
    |> Seq.average
    |> fun x -> x - Seq.average first * Seq.average second

let CreateCovMatrix(strangeList:seq<int * seq<float>>) = 
    let Size = Seq.length strangeList
    let CovMatrix = Array2D.init Size Size
    let CalculateCovMatrix = 
        for (a, i) in strangeList do
            for (b, j) in strangeList do
                    CovMatrix.[a,b]=cov(i)(j)
    CalculateCovMatrix

let result = CreateCovMatrix(All)
printf "%A" result
2

2 Answers

5
votes

The function Array2D.init takes 3 parameters, you're missing the last one, which is the initialization function.

You can quick-fix it by writing:

let CovMatrix = Array2D.init Size Size (fun _ _ -> 0.)

Then you'll discover the next problem: assignment for mutable references is the <- operator, not = which is for comparison and let bindings.

So your function will look like this:

let CreateCovMatrix(strangeList:seq<int * seq<float>>) = 
    let Size = Seq.length strangeList
    let CovMatrix = Array2D.init Size Size (fun _ _ -> 0.)
    let CalculateCovMatrix = 
        for (a, i) in strangeList do
            for (b, j) in strangeList do
                    CovMatrix.[a,b] <- cov(i)(j)
    CalculateCovMatrix

But instead of specifying a function that ignores the inputs and return always zero, you can use the function Array2D.zeroCreate :

let CovMatrix = Array2D.zeroCreate Size Size
3
votes

This kind of math is where functional programming excels, although you're currently residing in the imperative paradigm. Gustavo handled your warnings, but even so your function won't give the desired result. Mixing functional and imperative concepts CreateCovMatrix now returns unit.

Good imperative:

let createCovMatrix(strangeList:seq<int * seq<float>>) = 
    let size = Seq.length strangeList
    let covMatrix = Array2D.zeroCreate size size
    for (a, i) in strangeList do
        for (b, j) in strangeList do
            covMatrix.[a,b]<-cov(i)(j)
    covMatrix

Going functional:

let createCovMatrix strangeList = 
    strangeList 
    |> Seq.map (fun (a, i) -> Seq.map (fun (b, j) -> cov i j) strangeList)
    |> array2D

Multi functional:

let createCovMatrix strangeList = 
    let mapper f = Seq.map f strangeList
    mapper (fun (a, i) -> mapper (fun (b, j) -> cov i j))
    |> array2D