Автоматическое дифференцирование в C # и F # - PullRequest
0 голосов
/ 08 сентября 2010

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

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

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

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

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

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

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

Я надеюсь, что есть решение для этого!Заранее спасибо,

Эшли

Ответы [ 2 ]

3 голосов
/ 09 сентября 2010

Вы можете заново изобрести классы типов 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 голосов
/ 09 сентября 2010

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

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

...