que - tipos de variables en c++ y su significado
Derivados en C/C++? (11)
Tengo algunas expresiones como x^2+y^2
que me gustaría usar para algunos cálculos matemáticos. Una de las cosas que me gustaría hacer es tomar derivados parciales de las expresiones.
Entonces, si f(x,y) = x^2 + y^2
entonces el parcial de f
con respecto a x
sería 2x
, el parcial con respecto a y
sería 2y
.
Escribí una función pequeña usando un método de diferencias finitas, pero estoy teniendo muchos problemas con la precisión de coma flotante. Por ejemplo, termino con 1.99234
lugar de 2
. ¿Hay bibliotecas que respalden la diferenciación simbólica? ¿Cualquier otra sugerencia?
Implementé estas bibliotecas en varios idiomas, pero lamentablemente no en C. Si solo manejas polinomios (sumas, productos, variables, constantes y potencias), es bastante fácil. Las funciones Trig tampoco son tan malas. Algo más complicado y probablemente será mejor que se tome el tiempo para dominar la biblioteca de otra persona.
Si decides lanzar el tuyo, tengo algunas sugerencias que simplificarán tu vida:
Use estructuras de datos inmutables (estructuras de datos puramente funcionales) para representar expresiones.
Use el recolector de basura de Hans Boehm para administrar la memoria por usted.
Para representar una suma lineal, utilice un mapa finito (por ejemplo, un árbol de búsqueda binario) para asignar cada variable a su coeficiente.
Si está dispuesto a incrustar Lua en su código C y hacer sus cálculos allí, he puesto mi código Lua en http://www.cs.tufts.edu/~nr/drop/lua . Una de las características más agradables es que puede tomar una expresión simbólica, diferenciarla y compilar los resultados en Lua. Por supuesto, no encontrará documentación alguna :-(
Obtener la diferenciación numérica "a la derecha" (en el sentido de minimizar los errores) puede ser bastante complicado. Para comenzar, es posible que desee echar un vistazo a la sección de Recetas Numéricas en derivadas numéricas .
Para paquetes matemáticos simbólicos gratuitos, debe consultar GiNaC . También puede consultar SymPy , un paquete simbólico matemático autónomo de pura pitón. Descubrirá que SymPy es mucho más fácil de explorar, ya que puede usarlo de forma interactiva desde la línea de comando de Python.
En el aspecto comercial, tanto Mathematica como Maple tienen API C. Necesita una versión instalada / licenciada del programa para usar las bibliotecas, pero ambas pueden hacer fácilmente el tipo de diferenciación simbólica que busca.
Esto es algo aparte ya que se aplica a Lisp y no a C / C ++, pero podría ayudar a otros a buscar tareas similares o podría obtener algunas ideas para implementar algo similar en C / C ++ por su cuenta. SICP tiene algunas conferencias sobre el tema para lisp:
En Lisp, es bastante sencillo (y en otros lenguajes funcionales con patrones potentes de coincidencia y tipos polimórficos). En C, es probable que tenga que utilizar enums y estructuras para obtener el mismo poder (sin mencionar asignación / desasignación). Uno podría codificar definitivamente lo que necesita en ocaml en menos de una hora - Diría que la velocidad de tipeo es el factor limitante. Si necesita C, puede llamar a ocaml desde C (y viceversa).
Si este es realmente el tipo de función que desea utilizar, sería bastante fácil escribir una biblioteca de clase. Comience con un solo Término, con un coeficiente y un exponente. Tener un polinomio que consistiría en una colección de términos.
Si define una interfaz para los métodos matemáticos de interés (por ejemplo, add / sub / mul / div / differentiate / integrate), está viendo un patrón compuesto GoF. Tanto Término como Polinomio implementarían esa interfaz. El polinomio simplemente iteraría sobre cada término en su colección.
Sin duda sería más fácil aprovechar un paquete existente que escribir uno propio, pero si estás decidido a escribir el tuyo propio, y estás dispuesto a pasar algún tiempo aprendiendo sobre algunos rincones oscuros de C ++, puedes usar Boost.Proto. de Boost para diseñar su propia biblioteca.
Básicamente, Boost.Proto le permite convertir cualquier expresión de C ++ válida, como x * x + y * y
a una plantilla de expresión , básicamente una representación del árbol de análisis sintáctico de esa expresión utilizando struct
anidadas, y luego realizar cualquier arbitraria cómputo sobre ese árbol de análisis en un momento posterior llamando a proto::eval()
sobre él. De forma predeterminada, proto::eval()
se usa para evaluar el árbol como si se hubiera ejecutado directamente, aunque no hay ninguna razón por la que no se pueda modificar el comportamiento de cada función u operador para tomar una derivada simbólica.
Aunque esta sería una solución extremadamente compleja para su problema, sin embargo sería mucho más fácil que intentar desplegar sus propias plantillas de expresión usando técnicas de metaprogramación de plantillas C ++.
Si está haciendo una diferenciación numérica ("evalúa la derivada de f (x) en x = x0 ") y sabe que tiene ecuaciones por adelantado (es decir, que no ingresó el usuario), entonces le recomendaría FADBAD ++ . Es una biblioteca de plantillas de C ++ para resolver derivados numéricos utilizando la diferenciación automática . Es muy rápido y preciso.
Para calcular solo el primer orden de derivados es bastante trivial de implementar. Pero es un arte hacerlo rápido. Necesitas una clase, que contiene
- el valor
- una matriz de valores derivados frente a variables independientes
Luego, escribiría operadores para sumar y restar y así sucesivamente y funciones como sin () que implementan las reglas básicas y conocidas para esta operación.
Para calcular derivadas de orden superior se debe hacer utilizando series de taylor truncadas. También puede aplicar la clase mencionada anteriormente a sí mismo: el tipo para el valor y los valores derivados debe ser un argumento de plantilla. Pero esto significa el cálculo y el almacenamiento de derivados más de una vez.
serie taylor truncada: hay dos bibliotecas disponibles para esto:
Puede mejorar la precisión de su diferenciación numérica de dos maneras simples
Usa un delta más pequeño. Parece que usaste un valor de alrededor de
1e-2
. Comience con1e-8
y pruebe si le duele algo más pequeño o si le ayuda. Obviamente, no puede acercarse demasiado a la precisión de la máquina, alrededor de1e-16
para el doble.Use las diferencias centrales en lugar de las diferencias hacia delante (o hacia atrás). es decir,
df_dx =(f(x+delta) - f(x-delta)) / (2.0*delta)
Por razones relacionadas con la cancelación de términos de truncamiento superiores, el error en la estimación de diferencias centrales es del ordendelta^2
lugar de que el delta de las diferencias hacia delante. Ver http://en.wikipedia.org/wiki/Finite_difference
Eche un vistazo a Theano, admite la diferenciación simbólica (en el contexto de Redes neuronales). El proyecto es de código abierto, por lo que debería poder ver cómo lo hacen.
Perdón por mencionar esto después de 6 años. Sin embargo, estaba buscando una biblioteca de este tipo para mi proyecto y he visto que @eduffy sugiere FADBAD ++ . He leído la documentación y volví a tu pregunta. Siento que mi respuesta será beneficiosa, por lo tanto, el siguiente código es para su caso.
#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;
}
El resultado es
f(x,y) = 2
df/dx(x,y) = 2
df/dy(x,y) = 2
Otro ejemplo, digamos que estamos interesados en la primera derivada de sin()
. Analíticamente, es cos
. Esto es genial porque necesitamos comparar la derivada verdadera de una función dada y su contraparte numérica para calcular el error verdadero.
#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;
}
El resultado es
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
==========================
Creé una biblioteca de este tipo en C ++, puede ver la muestra aquí: https://github.com/MartinKosicky/auto_diff/blob/master/sample/main.cpp
El uso es bastante simple, también admite matrices. Lo usé para crear una red neuronal recurrente ... Tendría que agregar la multiplicación de matrices GPU aunque