【问题标题】:F# Generic Math: how to write function with op_GreaterThanF# 通用数学:如何使用 op_GreaterThan 编写函数
【发布时间】:2014-11-27 22:02:57
【问题描述】:

在 F# 中,如何编写通用数学步进函数?

一个 (Oliver) Heaviside 阶跃函数是一个函数,如果 x 为负数则返回零,否则返回一。

这是我迄今为止的尝试总结:

// attempt 1:
let inline stepFct1< ^T when ^T : (static member op_GreaterThan: ^T * float -> bool) 
    >     (x:^T) : ^T = 
    //if (^T : (static member op_GreaterThan) (x 0.0) ) then x  //ouch fails also
    if  (>) x 0.0 then x
    else 0.0

编译器说:错误FS0001:类型参数缺少约束'当^T:比较'

// attempt 2:
let inline stepFct2<^T when ^T : (static member (>): ^T * ^T -> bool) > (x:^T) : ^T = 
    match x with 
    | x when x > 0.0 -> 1.0
    | 0.0

FSC 说:错误 FS0010:模式中出现意外的中缀运算符

动机:

我正在尝试重写 Ian 的 Cumulative-Normal 和 Black-Scholes 函数 here 以使用自动微分 (DiffSharp)。 Ian 的累积法线适用于浮点数,我想要一个适用于 any 数字类型的通用版本,包括 AutoDiff.DualG。 累积法线函数包含“大于”语句。

编辑:Gustavo,谢谢,我已接受您的回答 - 现在可以编译简单的 step 函数。

但它似乎对累积正常情况没有帮助。 鉴于此代码:

// Cumulative Normal Distribution Function - attempt to write a generic version
let inline CDF(x:^T) : ^T = 
    let (b1,b2,b3)  = (0.319381530, -0.356563782, 1.781477937)
    let (b4,b5)     = (-1.821255978, 1.330274429)
    let (p , c )    = (0.2316419  ,  0.39894228)
    let (zero, one) = (LanguagePrimitives.GenericZero, LanguagePrimitives.GenericOne)
    if x > zero then
        let t = one / (one + p * x) 
        (one - c * exp( -x * x / 2.0)* t * (t*(t*(t*(t*b5+b4)+b3)+b2)+b1)) 
    else
        let t = 1.0 / (one - p * x) 
        (c * exp( -x * x / 2.0)* t * (t*(t*(t*(t*b5+b4)+b3)+b2)+b1))

FSI 说:

C:\stdin(116,32): warning FS0064: This construct causes code to be less generic 
than indicated by the type annotations. 
The type variable 'T has been constrained to be type 'float'.

val inline CDF : x:float -> float
> CDF 0.1M;;
CDF 0.1M;;
----^^^^
C:\stdin(122,5): error FS0001: This expression was expected to have type
    float
but here has type
    decimal
>

有人知道如何使 CDF 通用吗?

【问题讨论】:

  • 您的更新确实是一个新问题,但您错过了答案的重点。如果您在任何地方都有浮点文字,则代码不再是通用的。您将需要找到构造常量的通用方法。
  • @JohnPalmer 是对的,这更具挑战性。我提到的库提供了一种使用有理数作为输入并将它们转换为目标类型的方法。请参阅答案中的更新。

标签: generics math f# diffsharp


【解决方案1】:

使用 LanguagePrimitives.GenericZero / GenericOne 并让类型推断完成剩下的工作

// attempt 1:
let inline stepFct1 x =
    let zero = LanguagePrimitives.GenericZero 
    if x > zero then x
    else zero

我查看了您发送的带有您要实现的功能的链接。 FSharpPlus (F#+) 可以帮助您编写通用数学代码,因为它包含专用的 Generic Numbers module。或者至少你可以从那里掌握一些技巧。

更新

关于您更新的问题,这将复杂性提升到更高的水平,这是使用最新版本的F#+ 项目的解决方案:

let inline CDF(x:^T) : ^T = 
    let num x = fromRational (x </ratio/> 1000000000I)
    let (b1,b2,b3)  = (num 319381530I   , num -356563782I  , num 1781477937I)
    let (b4,b5)     = (num -1821255978I , num 1330274429I)
    let (p , c )    = (num  0231641900I , num 0398942280I)
    let (zero, one, two) = 0G, 1G, 2G
    if x > zero then
        let t = one / (one + p * x) 
        (one - c * exp( -x * x / two)* t * (t*(t*(t*(t*b5+b4)+b3)+b2)+b1)) 
    else
        let t = one / (one - p * x) 
        (c * exp( -x * x / two)* t * (t*(t*(t*(t*b5+b4)+b3)+b2)+b1))

不幸的是,此时我意识到一些函数在库中被标记为内部函数,因此没有公开,但我在一个工作示例here 中重新创建了它们,这样你就可以测试你的函数,它可以很好地与@987654329 配合使用@ 和 float32

新版本将在今年年底之前发布,但同时您可以对其进行分支、删除内部结构并编译它,或者只是像我在链接示例中所做的那样重新创建函数。

如果您对通用数学感兴趣,请随时提供代码或用例。

【讨论】:

  • 参见上面的“编辑”,Gustavo 的方法适用于简单的步进函数。已接受答案,但不允许编写通用 CDF。
  • 也感谢上面的 GenericMath 链接。这是我看到的第一个通用数学库 - 我明天会尝试编译它
  • 嗨 Gustavo - 我刚刚运行了你的 CumulativeNormal 版本,我有两个 cmets:1) 它很糟糕 - 所有浮点常量都被巨大的整数替换。 2)这太棒了。到目前为止,我看到的所有数值库要么针对单一类型(双精度),要么作为 C++ 头文件实现。您的想法允许使用通用库 - 可能是革命性的!
  • @Piga-fetta 谢谢。输入表示为两个代表一个有理数的大整数,这样输入中没有精度损失,但是在计算之前它们会立即转换为目标类型。我很高兴知道这个 GenericMath 模块对你有用,我会花更多时间在上面,但我需要一些示例代码。如果您有 Github 帐户,让我们在项目页面继续讨论,非常感谢您的反馈。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-10
  • 1970-01-01
相关资源
最近更新 更多