C# 和 F# 中的自动区分

发布于 2024-09-18 10:58:21 字数 1948 浏览 8 评论 0原文

我在 C# 和 F# 之间使用自动微分功能时遇到问题。

在 C# 中,我有一个接受 double 并返回 double 的函数,比如说:

private double Price(double yield)
{
    double price;

    price = 0;

    for (int index = 1; index <= _maturity * _frequency; index++)
    {
        price += (_coupon / _frequency) * _nominal / Math.Pow(1 + (yield / _frequency), index);
    }

    price += _nominal / Math.Pow(1 + (yield / _frequency), _maturity * _frequency);

    return price;
}

我专门选择了这个函数,因为 Math.pow 非常禁止,并且只允许使用 double 或 int 作为其参数。

我想使用自动微分来微分这个函数。我已经在 F# 中编写了该方法:

type Diff(d : double, df : Lazy<Diff>) = class
    member x.d = d
    member x.df = df
    static member (+) (x : Diff, y : Diff) = 
        Diff(x.d + y.d, lazy (x.df.Value + y.df.Value)) 
    static member (-) (x : Diff, y : Diff) = 
        Diff(x.d - y.d, lazy (x.df.Value - y.df.Value))
    static member (*) (x : Diff, a : double) = 
        Diff(x.d * a, lazy (x.df.Value * a))
    static member (*) (x : Diff, y : Diff) = 
        Diff(x.d * y.d, lazy ((x.df.Value * y) + (y.df.Value * x)))
    override x.ToString() =
        x.d.ToString()
end

let rec dZero = Diff(0.0, lazy dZero)

let dConst x = Diff(x, lazy dZero)

let dId x = Diff(x, lazy dConst 1.0)

let Differentiate (x:Diff) = x.df.Value

// Example function
let f (x:Diff) = x*x*x;

// Example usage:
// (f (dId 5)).ToString = "125"
// (Differentiate (f (dId 5))).ToString = "75"
// (Differentiate (Differentate (f (dId 5)))).ToString = "30"

不幸的是,我需要将类型 Diff 提供给 Price(..) 函数以生成类型 Diff,然后将其提供给我的 Differente(..) 函数以返回另一个类型 Diff 。

然而,我的 C# 函数仅适用于双精度(我希望它保持这种方式,因为它在我的 C# 程序的其他地方使用)。

我能想到解决这个问题的唯一方法是将每个函数编写两次,这显然很糟糕:

1)我最好每次都编写一个不同的版本 2)这不是一个可扩展的模型

那么有什么方法可以解决这个问题,或者将我的双函数强制转换为 Diff 函数(最好在 F# 中)。理想情况下,我只想扔一个 (double -> double) 函数并获取 Diff.ToString() 。

抱歉,如果这完全模糊或无法理解。如果不清楚,我会在评论中回答任何问题。

我希望有一个解决方案!预先感谢,

阿什利

I am having a problem getting Automatic Differentiation to work between C# and F#.

In C# I have a function that takes a double and returns a double, say:

private double Price(double yield)
{
    double price;

    price = 0;

    for (int index = 1; index <= _maturity * _frequency; index++)
    {
        price += (_coupon / _frequency) * _nominal / Math.Pow(1 + (yield / _frequency), index);
    }

    price += _nominal / Math.Pow(1 + (yield / _frequency), _maturity * _frequency);

    return price;
}

I picked this function specifically, as the Math.pow is very prohibitive, and only allows a double's or int's for its parameters.

I would like to differentiate this function using Automatic Differentiation. I have written the method for this in F#:

type Diff(d : double, df : Lazy<Diff>) = class
    member x.d = d
    member x.df = df
    static member (+) (x : Diff, y : Diff) = 
        Diff(x.d + y.d, lazy (x.df.Value + y.df.Value)) 
    static member (-) (x : Diff, y : Diff) = 
        Diff(x.d - y.d, lazy (x.df.Value - y.df.Value))
    static member (*) (x : Diff, a : double) = 
        Diff(x.d * a, lazy (x.df.Value * a))
    static member (*) (x : Diff, y : Diff) = 
        Diff(x.d * y.d, lazy ((x.df.Value * y) + (y.df.Value * x)))
    override x.ToString() =
        x.d.ToString()
end

let rec dZero = Diff(0.0, lazy dZero)

let dConst x = Diff(x, lazy dZero)

let dId x = Diff(x, lazy dConst 1.0)

let Differentiate (x:Diff) = x.df.Value

// Example function
let f (x:Diff) = x*x*x;

// Example usage:
// (f (dId 5)).ToString = "125"
// (Differentiate (f (dId 5))).ToString = "75"
// (Differentiate (Differentate (f (dId 5)))).ToString = "30"

Unfortunately, I need to feed a type Diff into my Price(..) function to produce a type Diff, which then gets fed into my Differente(..) function to return another type Diff.

My C# function however works solely on doubles (and I would like it to stay this way, as it is used in other places in my C# program).

The only way I can think to solve this is to write every function twice, which is obviously awful as:

1) I may as well just write a differentiated version each time
2) This isn't a very expandable model

So is there any way I can get around this, or perhaps coerce my double functions into Diff functions (preferably in F#). Ideally I would just like to throw a (double -> double) function in and get a Diff.ToString() out.

Sorry if this totally vague or impossible to understand. I will answer any questions in comments if this is unclear.

I hope there is a solution for this! Thanks in advance,

Ashley

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

假面具 2024-09-25 10:58:21

你可以重新发明 Haskell 类型类:

interface Eq<T>
{
    bool Equal(T a, T b);
    bool NotEqual(T a, T b);
}

interface Num<T> : Eq<T>
{
    T Zero { get; }
    T Add(T a, T b);
    T Subtract(T a, T b);
    T Multiply(T a, T b);
    T Negate(T a);
}

sealed class Int : Num<int>
{
    public static readonly Int Instance = new Int();
    private Int() { }
    public bool Equal(int a, int b) { return a == b; }
    public bool NotEqual(int a, int b) { return a != b; }
    public int Zero { get { return 0; } }
    public int Add(int a, int b) { return a + b; }
    public int Subtract(int a, int b) { return a - b; }
    public int Multiply(int a, int b) { return a * b; }
    public int Negate(int a) { return -a; }
}

然后你可以这样做:

static T F<M, T>(M m, T x) where M : Num<T>
{
    return m.Multiply(x, m.Multiply(x, x));
}

static void Main(string[] args)
{
    Console.WriteLine(F(Int.Instance, 5));  // prints "125"
}

然后用:

class Diff
{
    public readonly double d;
    public readonly Lazy<Diff> df;

    public Diff(double d, Lazy<Diff> df)
    {
        this.d = d;
        this.df = df;
    }
}

class DiffClass : Floating<Diff>
{
    public static readonly DiffClass Instance = new DiffClass();
    private static readonly Diff zero = new Diff(0.0, new Lazy<Diff>(() => DiffClass.zero));
    private DiffClass() { }
    public Diff Zero { get { return zero; } }
    public Diff Add(Diff a, Diff b) { return new Diff(a.d + b.d, new Lazy<Diff>(() => Add(a.df.Value, b.df.Value))); }
    public Diff Subtract(Diff a, Diff b) { return new Diff(a.d - b.d, new Lazy<Diff>(() => Subtract(a.df.Value, b.df.Value))); }
    public Diff Multiply(Diff a, Diff b) { return new Diff(a.d * b.d, new Lazy<Diff>(() => Add(Multiply(a.df.Value, b), Multiply(b.df.Value, a)))); }
    ...
}

你可以这样做:

static T Price<M, T>(M m, T _maturity, T _frequency, T _coupon, T _nominal, T yield) where M : Floating<T>
{
    T price;

    price = m.Zero;

    for (T index = m.Succ(m.Zero); m.Compare(index, m.Multiply(_maturity, _frequency)) <= 0; index = m.Succ(index))
    {
        price = m.Add(price, m.Divide(m.Multiply(m.Divide(_coupon, _frequency), _nominal), m.Power(m.Add(m.Succ(m.Zero), m.Divide(yield, _frequency)), index)));
    }

    price = m.Add(price, m.Divide(_nominal, m.Power(m.Add(m.Succ(m.Zero), m.Divide(yield, _frequency)), m.Multiply(_maturity, _frequency))));

    return price;
}

但这并不是很漂亮。

事实上,它读起来几乎就像创建 LINQ 表达式树的代码。也许你可以使用源代码表达式树变换而不是运算符重载来实现自动微分

You can re-invent Haskell Type Classes:

interface Eq<T>
{
    bool Equal(T a, T b);
    bool NotEqual(T a, T b);
}

interface Num<T> : Eq<T>
{
    T Zero { get; }
    T Add(T a, T b);
    T Subtract(T a, T b);
    T Multiply(T a, T b);
    T Negate(T a);
}

sealed class Int : Num<int>
{
    public static readonly Int Instance = new Int();
    private Int() { }
    public bool Equal(int a, int b) { return a == b; }
    public bool NotEqual(int a, int b) { return a != b; }
    public int Zero { get { return 0; } }
    public int Add(int a, int b) { return a + b; }
    public int Subtract(int a, int b) { return a - b; }
    public int Multiply(int a, int b) { return a * b; }
    public int Negate(int a) { return -a; }
}

Then you can do:

static T F<M, T>(M m, T x) where M : Num<T>
{
    return m.Multiply(x, m.Multiply(x, x));
}

static void Main(string[] args)
{
    Console.WriteLine(F(Int.Instance, 5));  // prints "125"
}

And then with:

class Diff
{
    public readonly double d;
    public readonly Lazy<Diff> df;

    public Diff(double d, Lazy<Diff> df)
    {
        this.d = d;
        this.df = df;
    }
}

class DiffClass : Floating<Diff>
{
    public static readonly DiffClass Instance = new DiffClass();
    private static readonly Diff zero = new Diff(0.0, new Lazy<Diff>(() => DiffClass.zero));
    private DiffClass() { }
    public Diff Zero { get { return zero; } }
    public Diff Add(Diff a, Diff b) { return new Diff(a.d + b.d, new Lazy<Diff>(() => Add(a.df.Value, b.df.Value))); }
    public Diff Subtract(Diff a, Diff b) { return new Diff(a.d - b.d, new Lazy<Diff>(() => Subtract(a.df.Value, b.df.Value))); }
    public Diff Multiply(Diff a, Diff b) { return new Diff(a.d * b.d, new Lazy<Diff>(() => Add(Multiply(a.df.Value, b), Multiply(b.df.Value, a)))); }
    ...
}

You can do this:

static T Price<M, T>(M m, T _maturity, T _frequency, T _coupon, T _nominal, T yield) where M : Floating<T>
{
    T price;

    price = m.Zero;

    for (T index = m.Succ(m.Zero); m.Compare(index, m.Multiply(_maturity, _frequency)) <= 0; index = m.Succ(index))
    {
        price = m.Add(price, m.Divide(m.Multiply(m.Divide(_coupon, _frequency), _nominal), m.Power(m.Add(m.Succ(m.Zero), m.Divide(yield, _frequency)), index)));
    }

    price = m.Add(price, m.Divide(_nominal, m.Power(m.Add(m.Succ(m.Zero), m.Divide(yield, _frequency)), m.Multiply(_maturity, _frequency))));

    return price;
}

But that's not really pretty.

In fact, it almost reads like code that creates a LINQ Expression Tree. Maybe you can use Source code Expression tree transformation instead of Operator overloading to achieve Automatic differentiation?

耳根太软 2024-09-25 10:58:21

无法使用现有的 C# 函数,也没有任何简单的方法将其提升为可以对 Diff 类型的成员进行操作的函数。一旦函数被编译,它是不透明的并且内部结构不可用;您所能做的就是使用双参数调用该函数并获得双结果。此外,您的 Price 方法使用了您甚至还没有在 Diff 类上定义的操作((\)Pow< /代码>)。

我不确定它是否适合您的目的,但一种可能的替代方案是在 F# 中编写 Price 函数的通用内联版本,然后可以对双精度数或 进行操作>Diff(假设您添加了 (\)Pow 运算符)。

There's no way to use your existing C# function, nor is there any easy way to lift it to a function that could operate on members of type Diff. Once the function has been compiled it is opaque and the internal structure is unavaliable; all you can do is call the function with a double argument and get a double result. Furthermore, your Price method uses operations which you haven't even defined on your Diff class anyway ((\) and Pow).

I'm not sure if it would be acceptable for your purposes, but one possible alternative would be to write a generic inline version of your Price function in F#, which could then operate on either doubles or Diffs (assuming that you add the (\) and Pow operators).

时光沙漏 2024-09-25 10:58:21

如果您只需要简单的自动微分,那么前面的“重新实现类型类”答案就完全是矫枉过正了。使用运算符重载并将导数具体化为数组,如我在此项目中所示。您需要的核心类型只是:

public readonly struct Number
{
    public readonly double Magnitude;
    public readonly double[] Derivatives;

    internal Number(double m, params double[] d)
    {
        this.Magnitude = m;
        this.Derivatives = d;
    }
}

然后实现维基百科上显示的运算符翻译,所以Number 上的每个运算符也对 Derivatives 数组进行操作。

您需要将函数定义为对此 Number 类型进行操作,但通过在 Number 上定义全套算术运算符,这通常只是更改参数类型,并更改对静态 Math.X 函数的任何调用都会调用相应的 Number.X 函数,即。 Math.Sin(x) -> x.Sin()

空/空数组主要对原始双精度数进行操作,因此它可能非常接近原始代码的速度。

The previous answers with "re-implementing type classes" are total overkill if you just need simple automatic differentiation. Use operator overloading and reify the derivatives as an array, as I show in this project. The core type you need is just:

public readonly struct Number
{
    public readonly double Magnitude;
    public readonly double[] Derivatives;

    internal Number(double m, params double[] d)
    {
        this.Magnitude = m;
        this.Derivatives = d;
    }
}

Then implement the operator translations shown on Wikipedia, so each operator on Number also operates on the Derivatives array.

You need to define your functions as operating on this Number type, but by defining the full suite of arithmetic operators on Number, this is usually just changing the parameter types, and changing any calls to the static Math.X functions to a corresponding Number.X function, ie. Math.Sin(x) -> x.Sin().

A null/empty array would mostly be operating on raw doubles, so it could be pretty close to the speed of the original code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文