2

I write a dot product function that takes 2 lists:

let inline dot a b =
    List.zip a b
    |> List.map (fun (a, b) -> a * b)
    |> List.reduce (+)

Is there a better way to calculate dot product without using List.zip?

MiP
  • 5,846
  • 3
  • 26
  • 41

3 Answers3

6

One shorter way would be to use List.map2:

let inline dot a b = List.map2 (*) a b |> List.sum

Another would be to use List.fold2:

let inline dot a b = List.fold2 (fun state x y -> state + x * y) LanguagePrimitives.GenericZero a b
Erik Schierboom
  • 16,301
  • 10
  • 64
  • 81
  • The `inline` in the second function will not help since the `0` forces it to have return type `int`. – Soldalma Jul 19 '17 at 21:03
  • If the `inline` is to make the function generalizable, using `LanguagePrimitives.GenericZero` instead of `0` would work. If it's just to tell the compiler "don't generate a function call here", then it's fine as-is. – rmunn Jul 20 '17 at 03:16
3

I had the same need for doing neural networks with F# with matrixes, but you could probably use Vectors.

I used MathNet Numerics which has among many other functions dot product.

Make sure you get both the core and the F# extensions.

If you are doing neural networks and will use MathNet Numerics with Matrices then you will probably want the Sigmoid function

MathNet Raise Scalar by a Matrix

Here is a backpropagation example related to neural networks using MathNet Numerics matrix in F#.

Guy Coder
  • 24,501
  • 8
  • 71
  • 136
3

Among the three proposed ones, I believe the one using List.fold2 is the fastest:

let inline dot1 a b =
    List.zip a b
    |> List.map (fun (a, b) -> a * b)
    |> List.reduce (+)

let inline dot2 a b = List.map2 (*) a b |> List.sum

// Modified with 0.0 instead of 0 and no 'inline'
let dot3 a b = List.fold2 (fun state x y -> state + x * y) 0.0 a b

let xs = [0.0..1000000.0]

> dot1 xs xs;;
Real: 00:00:00.242,

> dot2 xs xs;;
Real: 00:00:00.070

> dot3 xs xs;;
Real: 00:00:00.003

Zipping the two lists is probably quite expensive. The map2-sum solution is faster but iterates twice through the list. The fold2 solution only goes once through the list.

Soldalma
  • 4,636
  • 3
  • 25
  • 38
  • Why did you use `0.0` and not `LanguagePrimitives.GenericZero` to make `dot3` a generic function? I beilive not forcing generics could make functions evaluate faster, right? – MiP Jul 20 '17 at 04:07
  • @MiP - I am not sure. I tried again with `LanguagePrimitives.GenericZero` instead of `0.0` and the speed did not change appreciably. – Soldalma Jul 20 '17 at 12:12