pythonsympyderivativelambdify

Lambdify doesn't work on the derivative of a conjugate


I have very ugly symbolic functions on which I would like to do some operations, then lambdify them. Among others, I need to take the derivative of the conjugate. My issue: the derivative and the conjugate work separately, but when I try to lambdify the derivative of the conjugate, it throws me the following error:

    PrintMethodNotImplementedError: Unsupported by <class 'sympy.printing.numpy.SciPyPrinter'>: <class 'sympy.core.function.Derivative'>
    Set the printer option 'strict' to False in order to generate partially printed code.

Details + working/not working examples

Here is the basic function:

import sympy as sp

a, b = sp.symbols('a b')
def myfunction(x,y):
    return x + 1j * y

myfunctionval = myfunction(a,b)
myfunction_lam = sp.lambdify((a,b), myfunctionval)
print(myfunction_lam(1,2))
>> (1+2j)

Here is the conjugate:

def myfunctionConjugate(x,y):
    return sp.conjugate(x + 1j * y)

myfunctionConjugateval = myfunctionConjugate(a,b)
myfunctionConjugate_lam = sp.lambdify((a,b), myfunctionConjugateval)
print(myfunctionConjugate_lam(1,2))
>> (1-2j)

Here is the derivative:

def myfunctionDerivative(x,y):
    return sp.diff(x + 1j * y, x)

myfunctionDerivativeval = myfunctionDerivative(a,b)
myfunctionDerivative_lam = sp.lambdify((a,b), myfunctionDerivativeval)
print(myfunctionDerivative_lam(1,2))
>> 1

Here is the conjugate of the derivative, which works:

def myfunctionDerivativeConjugate(x,y):
    return sp.conjugate(sp.diff(x + 1j * y,x))

myfunctionDerivativeConjugateval = myfunctionDerivativeConjugate(a,b)
myfunctionDerivativeConjugate_lam = sp.lambdify((a,b), myfunctionDerivativeConjugateval)
print(myfunctionDerivativeConjugate_lam(1,2))
>> 1

Here is the derivative of the conjugate, which DOESNT work:

def myfunctionConjugateDerivative(x,y):
    return sp.diff(sp.conjugate(x + 1j * y),x)

myfunctionConjugateDerivativeval = myfunctionConjugateDerivative(a,b)
myfunctionConjugateDerivative_lam = sp.lambdify((a,b), myfunctionConjugateDerivativeval)
myfunctionConjugateDerivative_lam(1,2)

And throws me this error :

---------------------------------------------------------------------------
PrintMethodNotImplementedError            Traceback (most recent call last)
/tmp/ipykernel_13914/1657192206.py in <module>
      3 
      4 myfunctionConjugateDerivativeval = myfunctionConjugateDerivative(a,b)
----> 5 myfunctionConjugateDerivative_lam = sp.lambdify((a,b), myfunctionConjugateDerivativeval)
      6 myfunctionConjugateDerivative_lam(1,2)

~/anaconda3/lib/python3.9/site-packages/sympy/utilities/lambdify.py in lambdify(args, expr, modules, printer, use_imps, dummify, cse, docstring_limit)
    878     else:
    879         cses, _expr = (), expr
--> 880     funcstr = funcprinter.doprint(funcname, iterable_args, _expr, cses=cses)
    881 
    882     # Collect the module imports from the code printers.

~/anaconda3/lib/python3.9/site-packages/sympy/utilities/lambdify.py in doprint(self, funcname, args, expr, cses)
   1169                 funcbody.append('{} = {}'.format(self._exprrepr(s), self._exprrepr(e)))
   1170 
-> 1171         str_expr = _recursive_to_string(self._exprrepr, expr)
   1172 
   1173         if '\n' in str_expr:

~/anaconda3/lib/python3.9/site-packages/sympy/utilities/lambdify.py in _recursive_to_string(doprint, arg)
    964 
    965     if isinstance(arg, (Basic, MatrixBase)):
--> 966         return doprint(arg)
    967     elif iterable(arg):
    968         if isinstance(arg, list):

~/anaconda3/lib/python3.9/site-packages/sympy/printing/codeprinter.py in doprint(self, expr, assign_to)
    170         self._number_symbols = set()
    171 
--> 172         lines = self._print(expr).splitlines()
    173 
    174         # format the output

~/anaconda3/lib/python3.9/site-packages/sympy/printing/printer.py in _print(self, expr, **kwargs)
    329                 printmethod = getattr(self, printmethodname, None)
    330                 if printmethod is not None:
--> 331                     return printmethod(expr, **kwargs)
    332             # Unknown object, fall back to the emptyPrinter.
    333             return self.emptyPrinter(expr)

~/anaconda3/lib/python3.9/site-packages/sympy/printing/codeprinter.py in _print_not_supported(self, expr)
    580     def _print_not_supported(self, expr):
    581         if self._settings.get('strict', False):
--> 582             raise PrintMethodNotImplementedError("Unsupported by %s: %s" % (str(type(self)), str(type(expr))) + \
    583                              "\nSet the printer option 'strict' to False in order to generate partially printed code.")
    584         try:

PrintMethodNotImplementedError: Unsupported by <class 'sympy.printing.numpy.SciPyPrinter'>: <class 'sympy.core.function.Derivative'>
Set the printer option 'strict' to False in order to generate partially printed code.


Solution

  • That occurs when you are trying to generate code (lambdify — the print referred here is the printing of code) with some unsolved derivative in it (that is, once simplification and cse has done their job, there are still derivative in the result)

    In your case, this occurs, because conjugate of a+ib is conjugate(a) - 1.0*I*conjugate(b). And derivative of that (for example of conjugate of a) is not computable, without any information on a trajectory.

    (It is different from "conjugate of derivative", because derivative of a+ib is just 1, what ever a trajectory. So, derivative is replaced by a very computable 1, while dz¯/dz is not)

    So, not saying that this is perfectly normal (it should raise another error that one that seems an internal one). But you asked for an impossible task.

    But the more profound reason for that, is probably (I am surmising here) that you decided to explicitly make your functions parameters a+ib, to explicitly represent real and imaginary parts. So, your intents, I guess, was that a and b are reals (and a+ib is complex). So that conjugate(a+ib) is just (a-ib), and its derivative (with a) is also 1 (note that, if a+ib is a complex, and you compute derivative of f(a+ib) with respect to a, then, you fix a "trajectory" (you approach a+ib horizontally); so this is really not the same as computing derivative of f(z) with respect to z, or f(a+ib) with respect to a+ib. Which you are doing — I think involuntary, because not being aware that your a and b are also complex, with their own imaginary parts, as you wrote them).

    So, said otherwise, if I surmise correctly, and your intents were to have a+ib a complex made of a real part (a) and an imaginary part (b), then you should have said that.

    Short solution if so: create your symbols with

    a, b = sp.symbols('a b', real=True)
    

    And then, everything works.

    (again, I am not 100% sure this is you want, since it is then an easier problem. On another hand, without specifying that a and b are real, the problem is not just harder, but impossible)