我在让自动识别功能在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 - &gt; double)函数并获取Diff.ToString()。
对不起,如果这完全模糊或不可能理解。如果不清楚,我会在评论中回答任何问题。
我希望有一个解决方案!提前谢谢,
阿什利
答案 0 :(得分:3)
无法使用现有的C#函数,也没有任何简单的方法可以将其提升到可以对Diff
类型的成员进行操作的函数。一旦编译了函数,它就是不透明的,内部结构是不可用的;你所能做的就是用double参数调用函数并获得双重结果。此外,您的Price
方法会使用您尚未在Diff
课程中定义的操作((\)
和Pow
)。
我不确定它是否适用于您的目的,但一种可能的替代方法是在F#中编写Price
函数的通用内联版本,然后可以在双精度或{{ 1}} s(假设您添加了Diff
和(\)
运算符)。
答案 1 :(得分:3)
您可以重新发明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表达式树的代码一样。也许您可以使用 源代码表达式树转换而不是运算符重载来实现自动区分?
答案 2 :(得分:0)
如果您只需要简单的自动区分,那么以前关于“重新实现类型类”的答案就完全过头了。使用运算符重载,并将派生形式化为数组,如I show in this project。您需要的核心类型是:
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;
}
}
然后实现operator translations shown on Wikipedia,因此Number上的每个运算符也将在Derivatives
数组上进行操作。
您需要将函数定义为对此Number
类型进行操作,但是通过在Number
上定义完整的算术运算符套件,这通常只是更改参数类型,并更改对将静态Math.X
函数转换为相应的Number.X
函数,即Math.Sin(x)
-> x.Sin()
。
一个null / empty数组主要用于原始双精度运算,因此它可能与原始代码的速度非常接近。