【问题标题】:Assignment to mutable variable interferes with type inference of otherwise valid calculation对可变变量的赋值会干扰其他有效计算的类型推断
【发布时间】:2016-07-10 22:53:16
【问题描述】:

我正在尝试一些 Newton Raphson 更新。这是一段编译运行的代码(警告:无限循环)。

let thetam = [|beta; sigSq|] |> DenseVector
let mutable gm = grad yt xt betah sigSqh   // returns DenseVector
let hm = hess yt xt betah sigSqh   // return Matrix<float>

while gm*gm > 0.0001 do
    gm <- grad yt xt betah sigSqh
    thetam - (hess yt xt betah sigSqh).Inverse() * gm // unassigned compiles

但是,只要我将最后一个值分配给可变变量thetam,如下所示...

while gm*gm > 0.0001 do
    gm <- grad yt xt betah sigSqh
    thetam <- thetam - (hess yt xt betah sigSqh).Inverse() * gm // gm here has problems

gm 下出现一条弯曲的红线,编译器报错The type 'Vector&lt;float&gt;' is not compatible with the type 'DenseVector'

但是,函数 grad 被明确告知返回 DenseVector 并且通常按预期工作。

let grad (yt : Vector<float>) (xt : Vector<float>) (beta : float) (sigSq : float) =
    let T = (float yt.Count)
    let gradBeta = (yt - beta * xt)*xt / sigSq
    let gradSigSq = -0.5*T/sigSq + 0.5/sigSq**2.*(yt - beta * xt)*(yt - beta * xt)
    [|gradBeta; gradSigSq|] |> DenseVector

为什么分配给thetam 会导致问题?有没有一种神奇的方法来执行不可变的更新?

这是完整的脚本:

open System
open System.IO
open System.Windows.Forms
open System.Windows.Forms.DataVisualization
open FSharp.Data
open FSharp.Charting
open FSharp.Core.Operators
open MathNet.Numerics
open MathNet.Numerics.LinearAlgebra
open MathNet.Numerics.LinearAlgebra.Double
open MathNet.Numerics.Random
open MathNet.Numerics.Distributions
open MathNet.Numerics.Statistics

let beta, sigSq = 3., 9.
let xt = DenseVector [|23.; 78.; 43.; 32.; 90.; 66.; 89.; 34.; 72.; 99.|]
let T = xt.Count

let genProc () =
    beta * xt + DenseVector [|for i in 1 .. T do yield Normal.Sample(0., Math.Sqrt(sigSq))|]

let llNormal (yt : Vector<float>) (xt : Vector<float>) (beta : float) (sigSq : float) =
    let T = (float yt.Count)
    let z = (yt - beta * xt) / Math.Sqrt(sigSq)
    -0.5 * log (2. * Math.PI) - 0.5 * log (sigSq) - z*z/2./T/sigSq

let grad (yt : Vector<float>) (xt : Vector<float>) (beta : float) (sigSq : float) =
    let T = (float yt.Count)
    let gradBeta = (yt - beta * xt)*xt / sigSq
    let gradSigSq = -0.5*T/sigSq + 0.5/sigSq**2.*(yt - beta * xt)*(yt - beta * xt)
    [|gradBeta; gradSigSq|] |> DenseVector

let hess (yt : Vector<float>) (xt : Vector<float>) (beta : float) (sigSq : float) = 
    let T = (float yt.Count)
    let z = yt - beta * xt
    let h11 = -xt*xt/sigSq
    let h22 = T*0.5/sigSq/sigSq - z*z/sigSq/sigSq/sigSq
    let h12 = -1./sigSq**2.*((yt - beta * xt)*xt)
    array2D [[h11;h12];[h12;h22]] |> DenseMatrix.ofArray2

let yt = genProc()

// until convergence
let mutable thetam = [|beta; sigSq|] |> DenseVector
let mutable gm = grad yt xt beta sigSq

while gm*gm > 0.0001 do
    gm <- grad yt xt beta sigSq
    // 'gm' here is complaining upon equation being assigned to thetam
    thetam <- thetam - (hess yt xt beta sigSq).Inverse() * gm 

【问题讨论】:

  • 是否可以通过一些数据获得工作代码?跟踪实际类型有点困难。如果您的类型实际上是正确的,您可能需要说 [|gradBeta; gradSigSq|] |&gt; DenseVector.OfArray,因为 mathnet 有时会就地更改内容,有时会复制内容。
  • @s952163 我认为您对 DenseVector.OfArray 是正确的。请作为答案发布,我会打勾。
  • 这很快。你在哪个时区?你能投票给 matnet/mathdotnet 标签alias 吗?谢谢!
  • 我在纽约。刚刚尝试投票,但它没有接受,因为我没有达到该标签上要求的分数。感谢所有帮助顺便说一句。非常感谢。
  • (hess yt xt beta sigSq).Inverse() -> ((hess yt xt beta sigSq).Inverse() :?&gt; DenseMatrix)

标签: f# math.net mathnet-numerics


【解决方案1】:

您应该至少将let mutable thetam = [|beta; sigSq|] |&gt; DenseVector 更改为
let mutable thetam = [|beta; sigSq|] |&gt; DenseVector.ofArray(可能还有其他DenseVector 引用)。出于性能原因,Mathnet 会进行就地更改,因此如果您使用可变引用,它可能会绊倒您:

DenseVector(Double[] storage)

创建一个直接绑定到原始数​​组的新密集向量。数组 不用复制直接使用。非常有效,但改变了 数组和向量会相互影响。

对比:

DenseVector OfArray(Double[] 数组)

创建一个新的密集向量作为给定数组的副本。这个新 向量将独立于数组。一个新的内存块将是 分配用于存储向量。

事实上,当Exponential.Samples 以类似的方式表现时,我们已经在您的previous question 中看到了这种行为。

API 文档(虽然不是超级用户友好)是here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多