Change 'vector' and 'matrix' builder function in F# to take sequences instead of lists


(Albert Pang) #1

Hi all, the current ‘vector’ and ‘matrix’ functions in the F# extension modules take lists as input and create a DenseVector/DenseMatrix respectively. How about we change them to take sequences like so:

let inline vector (s: seq<'T>) = DenseVector.ofSeq s

and for matrix:

let inline matrix (s: seq<#seq<'T>>) = DenseMatrix.ofRowSeq s

F# takes sequence as anything that implements the IEnumerable interface, that includes lists, arrays, sequence expressions as well. So making the above changes will not affect existing functionality but then will open a lot more possibilities like the below:

It will still work for the existing case using list.

> open MathNet.Numerics.LinearAlgebra;;
> let v0 = vector [1.0; 2.0; 3.0];;

val v0 : Vector

It will even work for vector itself because vector also implements IEnumerable.

> let v1 = vector v0;;

val v1 : Vector

v1;;
val it : Vector = seq [1.0; 2.0; 3.0]

If you have an array of floats, it will also work:

> let a0 = [|1.0; 2.0; 3.0|];;

val a0 : float [] = [|1.0; 2.0; 3.0|]

> let v2 = vector a0;;

val v2 : Vector

> v2;;

val it : Vector = seq [1.0; 2.0; 3.0]

Using a sequence expression:

> seq { for i in [0.0…5.0] -> i * 2.0 } |> vector;;
val it : Vector = seq [0.0; 2.0; 4.0; 6.0; …]

For matrix, the existing case using lists still works:

> matrix [[1.0; 2.0; 3.0]; [0.1; 0.2; 0.3]];;
val it : Matrix =
DenseMatrix 2x3-Double
1 2 3
0.1 0.2 0.3

{ColumnCount = 3;
 Item = ?;
 RowCount = 2;
 Storage = MathNet.Numerics.LinearAlgebra.Storage.DenseColumnMajorMatrixStorage`1[System.Double];
 Values = [|1.0; 0.1; 2.0; 0.2; 3.0; 0.3|];}

Or if you like you can do array of lists and it will work:

> matrix [| [1.0; 2.0; 3.0]; [0.1; 0.2; 0.3] |];;
val it : Matrix =
DenseMatrix 2x3-Double
1 2 3
0.1 0.2 0.3

{ColumnCount = 3;
 Item = ?;
 RowCount = 2;
 Storage = MathNet.Numerics.LinearAlgebra.Storage.DenseColumnMajorMatrixStorage`1[System.Double];
 Values = [|1.0; 0.1; 2.0; 0.2; 3.0; 0.3|];}

You can also use list of arrays:

> matrix [[|1.0; 2.0; 3.0|]; [|0.1; 0.2; 0.3|]];;
val it : Matrix =
DenseMatrix 2x3-Double
1 2 3
0.1 0.2 0.3

{ColumnCount = 3;
 Item = ?;
 RowCount = 2;
 Storage = MathNet.Numerics.LinearAlgebra.Storage.DenseColumnMajorMatrixStorage`1[System.Double];
 Values = [|1.0; 0.1; 2.0; 0.2; 3.0; 0.3|];}

And then you can also plug in a list of vectors and it will work too (because, again, vector implements IEnumerable)

> matrix [v2; v2];;
val it : Matrix =
DenseMatrix 2x3-Double
1 2 3
1 2 3

{ColumnCount = 3;
 Item = ?;
 RowCount = 2;
 Storage = MathNet.Numerics.LinearAlgebra.Storage.DenseColumnMajorMatrixStorage`1[System.Double];
 Values = [|1.0; 1.0; 2.0; 2.0; 3.0; 3.0|];}

The last case using a list/seq/array of vectors to create a matrix seem very natural to me and it will save us a lot of toList conversions.

Does anybody like this? Or there might be a reason why they only take lists to begin with, please feel free to share/comment

Cheers,
Albert


(Christoph Rüegg) #2

This sounds like a very reasonable proposal. I agree that it would make it more flexible and usable in more scenarios. I do not remember why it has been typed for lists explicitly back then - maybe simply because that was the typical use case in interactive scripting.


(Christoph Rüegg) #3

I wonder whether changing the signature would qualify as a breaking change on binary level.


(Albert Pang) #4

It most probably will be a change in the binary level. But then I have tested it on my own project - I only needed to rebuild/relink with the new version and it did not break anything. Because anyone using the existing versions must be passing in a list for vector or a list of lists for matrix, which will work the same after the change. Its like like we are changing the function to take a superclass of list.

Am I getting you correctly or it is something else that is on your mind?


(Christoph Rüegg) #5

Yes - if a library compiled against the current version will no longer work with the newer version without recompilation, then this is a breaking change and we’ll have to increment the major version.


(Albert Pang) #6

Ah, got it, versioning is where you are coming from.

How do we manage this? Want me to send a pull request now and we merge until we are 4 point something or not at all for now?


(Christoph Rüegg) #7

I propose I create a v4 branch. You can then send me the PR against this branch, instead of master.

We can do a jump to v4, but when we do this we should also clean up all the members marked as obsolete, and e.g. review the F# modules for other functionality where there would be better options but realizing them would be breaking.


(Albert Pang) #8

Sure, sounds good to me.