Производные в C / C ++? - PullRequest
       45

Производные в C / C ++?

21 голосов
/ 08 января 2009

У меня есть некоторые выражения, такие как x^2+y^2, которые я хотел бы использовать для некоторых математических вычислений. Одна из вещей, которые я хотел бы сделать, это взять частные производные выражений.

Таким образом, если f(x,y) = x^2 + y^2, то частичное f относительно x будет 2x, частичное y будет 2y.

Я написал функцию Динки, используя метод конечных разностей, но у меня много проблем с точностью с плавающей запятой. Например, я получаю 1.99234 вместо 2. Существуют ли библиотеки, которые поддерживают символическую дифференциацию? Любые другие предложения?

Ответы [ 11 ]

12 голосов
/ 08 января 2009

Я реализовал такие библиотеки на нескольких разных языках, но, к сожалению, не на C. Если вы имеете дело только с полиномами (суммами, продуктами, переменными, константами и степенями), это довольно просто. Триггерные функции тоже не так уж плохи. Что-нибудь более сложное, и вам, вероятно, будет лучше, если вы освоите чужую библиотеку.

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

  • Используйте неизменяемые структуры данных (чисто функциональные структуры данных) для представления выражений.

  • Используйте Сборщик мусора Ханса Бема для управления памятью.

  • Чтобы представить линейную сумму, используйте конечную карту (например, двоичное дерево поиска), чтобы сопоставить каждую переменную с ее коэффициентом.

Если вы готовы встроить Lua в свой код C и выполнять там свои вычисления, я поместил свой код Lua в http://www.cs.tufts.edu/~nr/drop/lua. Одна из приятных особенностей заключается в том, что он символическое выражение, дифференцируйте его и скомпилируйте результаты в Lua. Вы, конечно, не найдете никакой документации: - (

6 голосов
/ 12 июня 2009

Если вы проводите численное дифференцирование («оцените производную от f (x) при x = x0 ») и вы знаете, Уравнения заранее (т.е. не пользовательский ввод), то я бы порекомендовал FADBAD ++ . Это библиотека шаблонов C ++ для решения числовых производных с использованием Автоматическое дифференцирование . Это очень быстро и точно.

6 голосов
/ 08 января 2009

Получить правильное числовое дифференцирование (в смысле минимизации ошибок) может быть довольно сложно. Чтобы начать, вы можете взглянуть на раздел «Числовые рецепты» на числовые производные .

Для бесплатных символических пакетов по математике, вы должны посмотреть GiNaC . Вы также можете проверить SymPy , самодостаточный символьный математический пакет на чистом питоне. Вы обнаружите, что SymPy гораздо проще исследовать, поскольку вы можете использовать его в интерактивном режиме из командной строки Python.

В коммерческих целях и Mathematica, и Maple имеют C API. Вам нужна установленная / лицензионная версия программы, чтобы использовать библиотеки, но обе легко могут выполнить тот тип символического различия, который вам нужен.

4 голосов
/ 13 марта 2013

Вы можете повысить точность вашего численного дифференцирования двумя простыми способами

  1. Используйте меньшую дельту. Вы, кажется, использовали значение около 1e-2. Начните с 1e-8 и проверьте, не причиняет ли вам боль или помогает. Очевидно, вы не можете подойти слишком близко к точности станка - около 1e-16 для двойной.

  2. Используйте центральные различия, а не прямые (или обратные) различия. т.е. df_dx =(f(x+delta) - f(x-delta)) / (2.0*delta) По причинам, связанным с отменой более высоких условий усечения, ошибка в оценке центральных различий имеет порядок delta^2, а не дельта прямых разниц. См http://en.wikipedia.org/wiki/Finite_difference

1 голос
/ 28 апреля 2017

Я создал такую ​​библиотеку в C ++, вы можете увидеть пример здесь: https://github.com/MartinKosicky/auto_diff/blob/master/sample/main.cpp

Использование довольно просто, оно также поддерживает матрицы. Я использовал его для создания рекуррентной нейронной сети ... Мне нужно было бы добавить умножение матрицы GPU, хотя

1 голос
/ 08 января 2009

Конечно, было бы легче использовать существующий пакет, чем писать свой, но если вы полны решимости написать свой собственный и готовы потратить некоторое время на изучение некоторых темных углов C ++, вы можете использовать Boost.Proto из Повышение для создания собственной библиотеки.

По сути, Boost.Proto позволяет преобразовывать любое допустимое выражение C ++, такое как x * x + y * y, в шаблон выражения - в основном это представление дерева разбора этого выражения с использованием вложенных struct s. - а затем выполнить любое произвольное вычисление над этим деревом разбора, вызвав для него proto::eval(). По умолчанию proto::eval() используется для оценки дерева, как если бы оно было запущено напрямую, хотя нет никаких причин, по которым вы не могли бы изменить поведение каждой функции или оператора, чтобы получить вместо него символьную производную.

Хотя это было бы чрезвычайно сложным решением вашей проблемы, тем не менее это было бы намного проще, чем пытаться свернуть свои собственные шаблоны выражений с использованием методов метапрограммирования шаблонов C ++.

1 голос
/ 08 января 2009

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

Если вы определяете интерфейс для математических методов, представляющих интерес (например, добавить / sub / mul / div / дифференцировать / интегрировать), вы смотрите на шаблон GoF Composite. И Term, и Polynomial будут реализовывать этот интерфейс. Полином будет просто повторяться в каждом Термине в своей Коллекции.

0 голосов
/ 02 декабря 2015

Извините, что поднял этот вопрос через 6 лет. Однако я искал такую ​​библиотеку для своего проекта и видел, что @eduffy предлагает FADBAD ++ . Я прочитал документацию и вернулся к вашему вопросу. Я чувствую, что мой ответ будет полезным, поэтому следующий код предназначен для вашего случая.

#include <iostream>
#include "fadiff.h"

using namespace fadbad;

F<double> func(const F<double>& x, const F<double>& y)
{
    return x*x + y*y;
}

int main()
{
    F<double> x,y,f;     // Declare variables x,y,f
    x=1;                 // Initialize variable x
    x.diff(0,2);         // Differentiate with respect to x (index 0 of 2)
    y=1;                 // Initialize variable y
    y.diff(1,2);         // Differentiate with respect to y (index 1 of 2)
    f=func(x,y);         // Evaluate function and derivatives

    double fval=f.x();   // Value of function
    double dfdx=f.d(0);  // Value of df/dx (index 0 of 2)
    double dfdy=f.d(1);  // Value of df/dy (index 1 of 2)

    std::cout << "    f(x,y) = " << fval << std::endl;
    std::cout << "df/dx(x,y) = " << dfdx << std::endl;
    std::cout << "df/dy(x,y) = " << dfdy << std::endl;

    return 0;
}

Выход

    f(x,y) = 2
df/dx(x,y) = 2
df/dy(x,y) = 2

Еще один пример, скажем, нас интересует первая производная от sin(). Аналитически, это cos. Это здорово, потому что нам нужно сравнить истинную производную данной функции и ее числовой аналог, чтобы вычислить истинную ошибку.

#include <iostream>
#include "fadiff.h"

using namespace fadbad;

F<double> func(const F<double>& x)
{
    return sin(x);
}



int main()
{
    F<double> f,x;
    double dfdx;
    x = 0.0;
    x.diff(0,1);
    f = func(x);
    dfdx=f.d(0);


    for (int i(0); i < 8; ++i ){
        std::cout << "       x: " << x.val()        << "\n"
                  << "    f(x): " << f.x()          << "\n" 
                  << " fadDfdx: " << dfdx           << "\n"
                  << "trueDfdx: " << cos(x.val())   << std::endl;
        std::cout << "=========================="   << std::endl;

        x += 0.1;
        f = func(x);
        dfdx=f.d(0);
    }


    return 0;
}

Результат

       x: 0
    f(x): 0
 fadDfdx: 1
trueDfdx: 1
==========================
       x: 0.1
    f(x): 0.0998334
 fadDfdx: 0.995004
trueDfdx: 0.995004
==========================
       x: 0.2
    f(x): 0.198669
 fadDfdx: 0.980067
trueDfdx: 0.980067
==========================
       x: 0.3
    f(x): 0.29552
 fadDfdx: 0.955336
trueDfdx: 0.955336
==========================
       x: 0.4
    f(x): 0.389418
 fadDfdx: 0.921061
trueDfdx: 0.921061
==========================
       x: 0.5
    f(x): 0.479426
 fadDfdx: 0.877583
trueDfdx: 0.877583
==========================
       x: 0.6
    f(x): 0.564642
 fadDfdx: 0.825336
trueDfdx: 0.825336
==========================
       x: 0.7
    f(x): 0.644218
 fadDfdx: 0.764842
trueDfdx: 0.764842
==========================
0 голосов
/ 06 марта 2015

Посмотрите на Theano, он поддерживает символическую дифференциацию (в контексте нейронных сетей). Проект с открытым исходным кодом, поэтому вы должны увидеть, как они это делают.

0 голосов
/ 01 июня 2012

Вычислить только первый порядок производных довольно тривиально для реализации. Но это искусство делать это быстро. Вам нужен класс, который содержит

  • значение
  • массив производных значений против независимых переменных

Затем вы должны написать операторы для сложения и вычитания и т. Д. И функции, такие как sin (), которые реализуют основные и хорошо известные правила для этой операции.

Для расчета производных высшего порядка следует использовать усеченные ряды Тейлора. Вы также можете применить вышеупомянутый класс к себе - тип значения и производные значения должны быть аргументом шаблона. Но это означает расчет и хранение производных более одного раза.

усеченная серия Тейлора - для этого есть две библиотеки:

http://code.google.com/p/libtaylor/

http://www.foelsche.com/ctaylor

...