C/C++ 的衍生物?
我有一些表达式,例如 x^2+y^2
,我想将其用于一些数学计算。 我想做的一件事是对表达式求偏导数。
因此,如果 f(x,y) = x^2 + y^2
则 f
相对于 x
的部分将为 >2x
,相对于 y
的部分将为 2y
。
我使用有限差分法编写了一个很小的函数,但我遇到了很多浮点精度问题。 例如,我最终得到 1.99234
而不是 2
。 有没有支持符号微分的库? 还有其他建议吗?
I have some expressions such as x^2+y^2
that I'd like to use for some math calculations. One of the things I'd like to do is to take partial derivatives of the expressions.
So if f(x,y) = x^2 + y^2
then the partial of f
with respect to x
would be 2x
, the partial with respect to y
would be 2y
.
I wrote a dinky function using a finite differences method but I'm running into lots of problems with floating point precision. For example, I end up with 1.99234
instead of 2
. Are there any libraries that support symbolic differentiation? Any other suggestions?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
仅计算一阶导数的实现起来非常简单。
但让它变得更快是一门艺术。
您需要一个类,其中包含
然后您将编写用于加法和减法等的运算符以及诸如 sin() 之类的函数,它们实现了此操作的基本且众所周知的规则。
要计算高阶导数,应使用截断泰勒级数来完成。
您还可以将上述类应用到其自身 - 值和派生值的类型应该是模板参数。
但这意味着不止一次计算和存储导数。
截断的泰勒级数 - 有两个库可用于此目的:
http://code.google.com/p /libtaylor/
http://www.foelsche.com/ctaylor
To calculate only the first order of derivatives is quite trivial to implement.
But it is an art to make it fast.
You need a class, which contains
You would then write operators for addition and subtraction and so on and functions like sin() which implement the basic and well known rules for this operation.
To calculate higher order derivatives should be done using truncated taylor series.
You could also apply above mentioned class to itself -- the type for the value and derivative values should be a template argument.
But this means calculation and storing of derivatives more than once.
truncated taylor series -- there are two libraries available for this:
http://code.google.com/p/libtaylor/
http://www.foelsche.com/ctaylor
看看 Theano,它支持符号微分(在神经网络的背景下)。 该项目是开源的,所以您应该能够看到他们是如何做到的。
Have a look at Theano, it supports symbolic differentiation (in the context of Neural Nets). The project is open source though so you should be able to see how they do it.
我已经用几种不同的语言实现了这样的库,但不幸的是不是 C。如果您只处理多项式(和、乘积、变量、常量和幂),那么这非常简单。 三角函数也不错。 任何更复杂的事情,你可能最好花时间去掌握别人的库。
如果您决定自己开发,我有一些建议可以简化您的生活:
使用不可变数据结构(纯函数数据结构)来表示表达式。
使用Hans Boehm 的垃圾收集器来为您管理内存。< /p>
要表示线性和,请使用有限映射(例如二叉搜索树)将每个变量映射到其系数。
要表示
如果您愿意将 Lua 嵌入到您的 C 代码中并在那里进行计算,我已经将我的 Lua 代码放入其中在 http://www.cs.tufts.edu/~nr/drop/lua< /a>. 更好的功能之一是它可以采用符号表达式,对其进行微分,然后将结果编译到 Lua 中。 您当然不会找到任何文档:-(
I've implemented such libraries in several different languages, but unfortunately not C. If you are dealing only with polynomials (sums, products, variables, constants, and powers) it is pretty easy. Trig functions are also not too bad. Anything more complicated and you will probably be better off taking the time to master somebody else's library.
If you decide to roll your own, I have a few suggestions that will simplify your life:
Use immutable data structures (purely functional data structures) to represent expressions.
Use Hans Boehm's garbage collector to manage the memory for you.
To represent a linear sum, use a finite map (e.g., a binary search tree) to map each variable to its coefficient.
If you're willing to embed Lua into your C code and do your computations there, I have put my Lua code at http://www.cs.tufts.edu/~nr/drop/lua. One of the nicer features is that it can take a symbolic expression, differentiate it, and compile the results into Lua. You will of course find no documentation whatever :-(
如果您正在进行数值微分(“计算 f(x) 在 x = x0 处的导数”)并且您知道自己在提前方程式(即,不是用户输入),那么我推荐 FADBAD++。 它是一个 C++ 模板库,用于使用自动微分求解数值导数。 它非常快,而且准确。
If you're doing numerical differentiation ("evaluate the derivative of f(x) at x = x0") and you know you're equations in advance (ie, not user input), then I'd recommend FADBAD++. It's a C++ template library for solving numeric derivatives using Automatic differentiation. It's very quick, and accurate.
获得“正确”的数值微分(在最小化误差的意义上)可能相当棘手。 首先,您可能需要查看数值方法上的数值食谱部分。衍生品。
对于免费的符号数学包,您应该查看 GiNaC。 您还可以查看 SymPy,这是一个独立的纯 Python 符号数学包。 您会发现 SymPy 更容易探索,因为您可以从 Python 命令行交互地使用它。
在商业端,Mathematica 和 Maple 都有 C API。 您需要安装/许可的程序版本才能使用这些库,但是两者都可以轻松地执行您想要的符号微分类型。
Getting numerical differentiation "right" (in the sense of minimizing errors) can be quite tricky. To get started, you may want to take a look at the Numerical Recipies' section on numerical derivatives.
For free symbolic math packages, you should look at GiNaC. You could also check out SymPy, a self-contained, pure-python symbolic math package. You will find that SymPy is much easier to explore, since you can use it interactively from the Python command line.
On the commercial end, both Mathematica and Maple have C APIs. You need an installed/licensed version of the program to use the libraries, but both can easily do the type of symbolic differentiation you're after.
您可以通过两种简单的方法提高数值微分的准确性
使用较小的增量。 您似乎使用了大约
1e-2
的值。 从1e-8
开始,并测试是否有任何较小的伤害或帮助。 显然,您不能太接近机器精度 - 对于 double 约为1e-16
。使用中心差异而不是前向(或后向)差异。
即 df_dx =(f(x+delta) - f(x-delta)) / (2.0*delta)
由于与取消较高截断项有关的原因,中心差异估计中的误差为
delta^2
量级,而不是前向差异的增量。 请参阅 http://en.wikipedia.org/wiki/Finite_differenceYou can improve the accuracy of your numerical differentiation in two simple ways
Use a smaller delta. You appear to have used a value of around
1e-2
. Start with1e-8
, and test if getting any smaller hurts or helps. Obviously you can't get too close to the machine precision - about1e-16
for double.Use central differences rather than forward (or backwards) differences.
i.e.
df_dx =(f(x+delta) - f(x-delta)) / (2.0*delta)
For reasons to do with cancellation of higher truncation terms, the error in the central differences estimate is of the order
delta^2
rather than the delta of forward differences. See http://en.wikipedia.org/wiki/Finite_difference如果这确实是您想要使用的函数,那么编写一个类库就很容易了。 从一个带有系数和指数的项开始。 有一个由项集合组成的多项式。
如果您为感兴趣的数学方法定义了一个接口(例如,add/sub/mul/div/Differiate/integrate),您就会看到GoF Composite 模式。 Term 和 Polynomial 都会实现该接口。 多项式将简单地迭代其集合中的每个项。
If this really is the kind of function you want to use, it'd be easy enough to write a class library. Start with a single Term, with a coefficient and an exponent. Have a Polynomial that would consist of a Collection of Terms.
If you define an interface for the mathematical methods of interest (e.g., add/sub/mul/div/differentiate/integrate), you're looking at a GoF Composite pattern. Both Term and Polynomial would implement that interface. The Polynomial would simply iterate over each Term in its Collection.
利用现有的包肯定比编写自己的包更容易,但如果您决定编写自己的包,并且准备花一些时间学习 C++ 的一些阴暗角落,则可以使用 Boost.Proto 来自 Boost 来设计您自己的库。
基本上,Boost.Proto 允许您将任何有效的 C++ 表达式(例如
x * x + y * y
)转换为 表达式模板 -- 基本上是使用嵌套结构的该表达式的解析树的表示 -- 然后执行任意任意操作稍后通过调用 proto::eval() 对该解析树进行计算。 默认情况下,proto::eval()
用于评估树,就好像它直接运行一样,尽管您没有理由不能修改每个函数或运算符的行为以获取相反,符号导数。尽管对于您的问题来说,这将是一个极其复杂的解决方案,但它仍然比尝试使用 C++ 模板元编程技术滚动您自己的表达式模板要容易得多。
It would certainly be easier to leverage an existing package than to write your own, but if you're determined to write your own, and you are prepared to spend some time learning about some dark corners of C++, you can use the Boost.Proto from Boost to engineer your own library.
Basically, Boost.Proto allows you to convert any valid C++ expression, such as
x * x + y * y
to an expression template -- basically a representation of the parse tree of that expression using nestedstruct
s -- and then perform any arbitrary computation over that parse tree at a later time by callingproto::eval()
on it. By default,proto::eval()
is used to evaluate the tree as though it had been run directly, though there's no reason why you couldn't modify the behaviour of each function or operator to take a symbolic derivative instead.Although this would be an extremely complex solution to your problem, it would nevertheless be much easier than trying to roll your own expression templates using C++ template metaprogramming techniques.
很抱歉六年后才提起这个问题。 但是,我一直在为我的项目寻找这样的库,并且我看到 @eduffy 建议 FADBAD++ 。 我已阅读文档并回到您的问题。 我觉得我的回答会很有帮助,因此,以下代码适合您的情况。
输出是
另一个例子,假设我们对
sin()
的一阶导数感兴趣。 解析一下,就是cos
。 这很棒,因为我们需要比较给定函数的真实导数及其对应的数值来计算真实误差。结果是
Sorry for bringing this up after 6 years. However, I was looking for such a library to my project and I've seen @eduffy suggests FADBAD++. I've read the documentation and came back to your question. I feel my answer will be beneficial, therefore, the following code is for your case.
The output is
Another example, let's say that we are interested in the first derivative of
sin()
. Analytically, it iscos
. This is great because we need to compare the true derivative of a given function and its numerical counterpart to compute the true error.The result is
这是一种旁白,因为它适用于 Lisp 而不是 C/C++,但它可以帮助其他人寻找类似的任务,或者您可能会得到一些关于自己在 C/C++ 中实现类似的东西的想法。 SICP 有一些关于 lisp 主题的讲座:
在 Lisp 中,它非常简单(在其他具有强大模式匹配和多态类型的函数式语言中)。 在 C 中,您可能必须大量利用枚举和结构才能获得相同的功能(更不用说分配/解除分配)。 人们绝对可以在一小时内用 ocaml 编写出所需的代码——我想说打字速度是限制因素。 如果您需要 C,您实际上可以从 C 调用 ocaml(反之亦然)。
This is kind of an aside since it applies to Lisp and not C/C++, but it could help others looking for similar tasks or you might get some ideas on implementing something similar in C/C++ on your own. SICP has some lectures on the subject for lisp:
In Lisp, it's pretty straight forward (and in other functional languages with powerful pattern matching and polymorphic types). In C, you would probably have to heavily utilize enums and structs to get the same power (not to mention allocation/deallocation). One could definitly code what you need in ocaml in under an hour --I'd say typing speed is the limiting factor. If you need C, you can actually call ocaml from C (and vice versa).