2010-09-08 2 views
0

У меня возникла проблема с автоматическим дифференцированием для работы между C# и F #.Автоматическое дифференцирование в C# и F #

В C# У меня есть функция, которая принимает двойную и возвращает двойные, скажет:

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 очень запретительный, и только позволяет дубль или 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 в моей Цена функция (..) для получения типа Diff, которая затем подается в мой Differente (..), чтобы возвращать другой тип Diff.

Моя функция C#, однако, работает исключительно на двухместных (и я бы хотел, чтобы она оставалась таким образом, поскольку она используется в других местах моей программы на C#).

Единственный способ, которым я могу думать, чтобы решить эту проблему, чтобы написать каждую функцию в два раза, что, очевидно, ужасно, как:

1) Я могу также просто написать дифференцированной версию каждый раз 2) Это не очень расширяемая модель

Так можно ли обойти это, или, возможно, принудить мои двойные функции к функциям Diff (желательно в F #). В идеале я просто хотел бы бросить (double -> double) функцию и получить Diff.ToString().

Извините, если это полностью расплывчато или невозможно понять. Я отвечу на любые вопросы в комментариях, если это неясно.

Надеюсь, что есть решение! Спасибо заранее,

Эшли

+0

Я не уверен, что понимаю, что вы просите здесь. Если я правильно прочитал ваш вопрос, кажется, вы хотите использовать свой метод «Цена» (немодифицированный), поскольку вы используете свою примерную функцию 'f'. Но 'f' использует ваши пользовательские операторы, а метод C#, работающий в двойных рядах, всегда будет использовать операторы' double'. – dtb

+0

Заметим, что если вас интересует только численное значение производной и нет символьного термина, просто используйте формулу аппроксимации: 'f '(x) = (f (x + h) -f (x))/h' для небольшого 'h' – Dario

+0

@dtb Да, вы точно верны. В идеале я хотел бы использовать transform/coerce для параметров функций, чтобы получить их от двойного до Diff. – Ash

ответ

2

Вы можете повторно изобрести 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. Возможно, вы можете использовать Исходный код Преобразование дерева выражений вместо Перегрузка оператора для достижения Автоматическая дифференциация?

3

Там нет никакого способа, чтобы использовать существующую C# функцию, и не существует какой-либо простой способ поднять его в функцию, которая может работать на членах типа Diff. Как только функция была скомпилирована, она непрозрачна, а внутренняя структура недоступна; все, что вы можете сделать, это вызвать функцию с двойным аргументом и получить двойной результат. Кроме того, ваш метод Price использует операции, которые вы даже не определили в своем классе Diff ((\) и Pow).

Я не уверен, было ли это приемлемо для ваших целей, но одной из возможных альтернатив было бы написать общую встроенную версию вашей функции Price в F #, которая затем могла бы работать либо с удвоенными, либо с Diff с (при условии, что вы добавляете операторы (\) и Pow).