Search code examples
matlabsymbolic-mathderivative

Transforming Symbolic Derivatives into Actual Symbols


I need to take derivatives in Matlab of a lot of equations w.r.t. generic functions, which will provide me with generic derivatives, of the type:

diff(f(x,y),x)

or

D([1],f(x,y)).

What I need is to transform these derivatives into actual symbolic variables, in order to be able to use solve, etc. What I am doing now, but which is highly inefficient, is brute force string replacement. Here is a minimal working example of what I am doing:

syms x y
f(x,y) = sym('f(x,y)')
jacobian(f)
first_d = jacobian(f)
strrep(char(first_d),'D([1], f)(x, y)','fx')

In my real application, I have lots of derivatives to take from lots of equations, so looping such replacements is not the smartest thing to do. Can anybody shed some light into a more efficient solution?


Solution

  • Note: I'm using R2014b. Symbolic Math functionality has changed greatly in recent versions and continues to do so. Users on different versions may need to do slightly different things to achieve the results below, which relies on accessing undocumented functionality.

    First, since this is about performance, it is sufficient to simply declare

    syms f(x,y)
    

    which also defines x and y as symbolic variables.

    As I mention in my comments above, Matlab/MuPAD's symbolic math is all about manipulating strings. Doing this more directly and and adding in you own knowledge of the problem can help speed things up. You want to avoid unnecessary conversions between strings and the sym/symfun types.

    1. The first thing to do is investigate how a particular symbolic math function is handling input and output and what lower level private functions it is calling. In the case of your jacobian function example, type edit jacobian in your command window to view the code in the Editor. Much of what you see may be confusing, but you should see this line:

    res = mupadmex('symobj::jacobian',Fsym.s,v.s);
    

    This calls the low level 'symobj::jacobian' function and passes in string versions of the function and variables. To call this yourself, you can do (this also assumes you know your variables are x and y):

    syms f(x,y)
    first_d = mupadmex('symobj::jacobian',char(f),char([x,y]))
    

    This returns [ diff(f(x, y), x), diff(f(x, y), y)]. The undocumented mupadmex function is a direct way of calling MuPAD function from within Matlab – there are others, which are documented.

    2. You'll notice that that the first_d output above is symfun class. We actually don't want want the output to be converted back to a symbolic function. To avoid this, we can pass an addition argument to mupadmex:

    syms f(x,y)
    first_d = mupadmex('symobj::jacobian',char(f),char([x,y]),0)
    

    with now returns the string matrix([[diff(f(x, y), x), diff(f(x, y), y)]]). (I only know this trick of adding the additional 0 argument from having browsed through a lot of Symbolic Math toolbox code.)

    3. From this string, we can now find and replace various patterns for partial derivatives with simple variables. The strrep function that you're using is generally a good choice for this. It is much faster than regexprep. However, if you have a large number of different, but similar, patterns to replace, you might do a performance comparison between the two. That would probably be the subject of a separate question.

    I'm not sure what your overall goal is or the full extent of your problem, but here is my final code for your example:

    syms f(x,y)
    first_d = mupadmex('symobj::jacobian',char(f),char([x,y]),0)
    first_d = strrep(first_d(9:end-2),'diff(f(x, y), x)','fx');
    first_d = sym(strrep(first_d,'diff(f(x, y), y)','fy'));
    

    This returns the symbolic vector [ fx, fy]. If you want a symfun, you'll need to modify the last line slightly. In some simple testing, this basic example is about 10% faster than calling jacobian and converting the result back to a string. If you directly specify the inputs as strings instead of allocating a symbolic function, the result is about 30% faster then your original:

    first_d = mupadmex('symobj::jacobian','f(x,y)','[x,y]',0)
    first_d = strrep(first_d(9:end-2),'diff(f(x, y), x)','fx');
    first_d = sym(strrep(first_d,'diff(f(x, y), y)','fy'));
    

    Using subs, as in this answer, while convenient, is the slowest approach. Converting back and forth to and from strings is costly.