F#的简单包装器,用于执行矩阵运算

时间:2009-12-15 14:08:48

标签: .net matlab f# functional-programming numerical

这是一篇相对较长的帖子。 F#现在具有matrixvector类型(在PowerPack中不在Core中)。这很棒!即使是Python的数值计算能力也来自第三部分。

但是那里提供的函数仅限于矩阵和向量算法,所以要进行反演,分解等,我们仍然需要使用另一个库。我现在正在使用最新的dnAnalytics,它正在合并到Math.Net项目中。但Math.Net项目现在已经有一年多没有对公众进行更新了,我不知道他们是否有继续计划。

我做了以下包装器,这个包装器使用类似Matlab的函数来做简单的线性代数。由于我是F#和FP的新手,您能否提出一些改进包装和代码的建议?谢谢!

#r @"D:\WORK\tools\dnAnalytics_windows_x86\bin\dnAnalytics.dll"
#r @"FSharp.PowerPack.dll"

open dnAnalytics.LinearAlgebra
open Microsoft.FSharp.Math
open dnAnalytics.LinearAlgebra.Decomposition

// F# matrix -> ndAnalytics DenseMatrix
let mat2dnmat (mat:matrix) = 
    let m = new DenseMatrix(mat.ToArray2D())
    m

// ndAnalytics DenseMatrix -> F# matrix 
let dnmat2mat (dnmat:DenseMatrix) = 
    let n = dnmat.Rows
    let m = dnmat.Columns
    let mat = Matrix.create n m 0.
    for i=0 to n-1 do
        for j=0 to m-1 do
            mat.[i,j] <- dnmat.Item(i,j)
    mat

// random matrix
let randmat n m =
    let r = new System.Random()
    let ranlist m = 
        [ for i in 1..m do yield r.NextDouble() ]
    matrix ([1..n] |> List.map (fun x-> ranlist m))

// is square matrix
let issqr (m:matrix) =
    let n, m = m.Dimensions
    n = m

// is postive definite 
let ispd m =
    if not (issqr m) then false
    else 
        let m1 = mat2dnmat m
        let qrsolver = dnAnalytics.LinearAlgebra.Decomposition.Cholesky(m1)
        qrsolver.IsPositiveDefinite()

// determinant
let det m =
    let m1 = mat2dnmat m
    let lusolver = dnAnalytics.LinearAlgebra.Decomposition.LU(m1)
    lusolver.Determinant ()

// is full rank
let isfull m =
    let m1 = mat2dnmat m
    let qrsolver = dnAnalytics.LinearAlgebra.Decomposition.GramSchmidt(m1)
    qrsolver.IsFullRank()

// rank
let rank m =
    let m1 = mat2dnmat m
    let svdsolver = dnAnalytics.LinearAlgebra.Decomposition.Svd(m1, false)
    svdsolver.Rank()

// inversion by lu
let inv m =
    let m1 = mat2dnmat m
    let lusolver = dnAnalytics.LinearAlgebra.Decomposition.LU(m1)
    lusolver.Inverse()

// lu
let lu m =
    let m1 = mat2dnmat m
    let lusolver = dnAnalytics.LinearAlgebra.Decomposition.LU(m1)
    let l = dnmat2mat (DenseMatrix (lusolver.LowerFactor ()))
    let u = dnmat2mat (DenseMatrix (lusolver.UpperFactor ()))
    (l,u)

// qr 
let qr m =
    let m1 = mat2dnmat m
    let qrsolver = dnAnalytics.LinearAlgebra.Decomposition.GramSchmidt(m1)
    let q = dnmat2mat (DenseMatrix (qrsolver.Q()))
    let r = dnmat2mat (DenseMatrix (qrsolver.R()))
    (q, r)

// svd 
let svd m =
    let m1 = mat2dnmat m
    let svdsolver = dnAnalytics.LinearAlgebra.Decomposition.Svd(m1, true)
    let u = dnmat2mat (DenseMatrix (svdsolver.U()))
    let w = dnmat2mat (DenseMatrix  (svdsolver.W()))
    let vt = dnmat2mat (DenseMatrix (svdsolver.VT()))
    (u, w, vt.Transpose)

和测试代码

(* todo: read matrix market format   ref: http://math.nist.gov/MatrixMarket/formats.html *)
let readmat (filename:string) = 
    System.IO.File.ReadAllLines(filename) |> Array.map (fun x-> (x |> String.split [' '] |> List.toArray |> Array.map float))
    |> matrix

let timeit f str= 
    let watch = new System.Diagnostics.Stopwatch()
    watch.Start()
    let res = f()
    watch.Stop()
    printfn "%s Needed %f ms" str watch.Elapsed.TotalMilliseconds
    res

let test() = 
    let testlu() = 
        for i=1 to 10 do
            let a,b = lu (randmat 1000 1000)
            ()
        ()
    let testsvd() =
        for i=1 to 10 do
            let u,w,v = svd (randmat 300 300)
            ()
        ()
    let testdet() =
        for i=1 to 10 do
            let d = det (randmat 650 650)
            ()
        ()
    timeit testlu "lu" 
    timeit testsvd "svd"
    timeit testdet "det"

我也比较了matlab

t = cputime; for i=1:10, [l,u] = lu(rand(1000,1000)); end; e = cputime-t
t = cputime; for i=1:10, [u,w,vt] = svd(rand(300,300)); end; e = cputime-t
t = cputime; for i=1:10, d = det(rand(650,650)); end; e = cputime-t

时间安排(Duo Core 2.0GH,2GB内存,Matlab 2009a)

f#:
lu Needed 8875.941700 ms
svd Needed 14469.102900 ms
det Needed 2820.304600 ms
matlab:
  lu 3.7752
  svd 5.7408
  det 1.2636

matlab的速度提高了约两倍。这是合理的,因为原生R也有similar results

1 个答案:

答案 0 :(得分:3)

我认为使用dnmat2mat可以简化randmatMatrix.init

let dnmat2mat (dnmat : DenseMatrix) =
  Matrix.init (dnmat.Rows) (dnmat.Columns) (fun i j -> dnmat.[i,j])

let randmat n m =
  let r = System.Random()
  Matrix.init n m (fun _ _ -> r.NextDouble())