Comments (24)
Ah, sorry I misunderstood. You want an expression that doesn't NameError. Yes. So my notes may not be related at all (I now realize).
from sympy.
Thank you very much for your help, this was useful and is appreciated!
I don't understand everything but I suspect that our use case is not justified - this feature is a "good to have" but at the same time, we can also overwrite it manually when it doesn't work well (or not at all).
In the cases I reported in this issue, the failures introduced in 1.13.0rc1 are all valid and are an improvement to identify what was previously not working.
from sympy.
found hack solution... changed 'strict' :False
It was 'strict':None
That error msg needs to be more precise. It eats up research time, in a terrible fashion.
class CodePrinter(StrPrinter):
_default_settings: dict[str, Any] = {
'order': None,
'full_prec': 'auto',
'error_on_reserved': False,
'reserved_word_suffix': '_',
'human': True,
'inline': False,
'allow_unknown_functions': False,
'strict': False #None # True or False; None => True if human == True
}
from sympy.
It eats up research time, in a terrible fashion.
Development time is eaten up by people opening issues without doing the obvious things like showing some code to reproduce the issue.
from sympy.
https://github.com/nicholasferguson/SympyANDmpmath
from sympy.
@nicholasferguson I authored the error message. The goal of this error message was of course to waste less research time (since sympy used to silently generate broken code), but we (I) might have unintentionally introduced a bug, so thanks for opening an issue (I think we all want the same thing here, and everyone involved are donating their time).
From your repository I could create this reproducer:
import mpmath
from sympy import Symbol, zeta, lambdify, Derivative
s = Symbol('s')
z = zeta(s)
zp = Derivative(z, s)
f = lambdify(s, zp, modules=['mpmath', {'Derivative': lambda expr, z: mpmath.zeta(z, derivative=1)}])
print(f(2))
which fails for the merge commit (b93a982) of gh-25913 (but passes when doing git checkout b93a982~1
).
If you print the docstring of our generated function f
for the older version of sympy it looks like this:
>>> print(f.__doc__)
Created with lambdify. Signature:
func(s)
Expression:
Derivative(zeta(s), s)
Source code:
def _lambdifygenerated(s):
return ( # Not supported in Python with mpmath:
# Derivative
Derivative(zeta(s), s))
Imported modules:
I see that there's a printer setting called "allow_unknown_functions" (which looks like it is being enabled by lambdify) that alters when CodePrinter
calls the method _print_not_supported
:
sympy/sympy/printing/codeprinter.py
Line 458 in 2565eb3
So .is_Function
needs to be True, let's check if that holds for our Derivative
instance:
>>> zp.is_Function
False
So, with a bit of luck it would be enough to change that if-statement to if (expr.is_Function or expr.is_Derivative) ...
.
EDIT: (and if the fix turns out to be more complicated, we could resort to passing strict=False
from lambdify
)
from sympy.
While trying to fix this I realize that this code doesn't make much sense:
>>> f = lambdify(s, zp, modules=['mpmath', {'Derivative': lambda expr, z: mpmath.zeta(z, derivative=1)}])
any Derivative instance would map to mpmath.zeta, an expression containing different derivatives would return non-meaningful results.
The proper fix would be to first replace the particular derivative of interest with a Function
instance (e.g. Function('my_zeta_derivative')(s)
). And then pass this function as a known function to lambdify
. That this ever "worked" is mostly an accident.
Furthermore, the previous generated code performed unnecessary work (it evaluated the primitive function at the point of interest). I have a local patch of codeprinter.py
which generates a "functioning" lambdify callable:
(Pdb) print(f.__doc__)
Created with lambdify. Signature:
func(s)
Expression:
Derivative(zeta(s), s)
Source code:
def _lambdifygenerated(s):
return Derivative(zeta(s), (s, 1))
Imported modules:
but this is rather pointless. If we can come up with a legitimate use of passing 'Derivative'
as a known function, I can use that as a test case for my patch, and submit it as a PR. But the original code snippet to reproduce the issue is not suitable since it doesn't do what you might expect.
The proper way to customize how derivatives are evaluated in the lambdified function would probably be along the lines of:
>>> from sympy import symbols, zeta, lambdify, Function
>>> import mpmath
>>> s = symbols('s')
>>> expr = 42 + 17*zeta(s).diff(s)
>>> zeta_diff = Function('zeta_diff')(s)
>>> expr2 = expr.replace(zeta(s).diff(s), zeta_diff)
>>> expr2
17*zeta_diff(s) + 42
>>> f = lambdify(s, expr2, ['mpmath', {'zeta_diff': lambda arg: mpmath.zeta(arg, derivative=1)}])
>>> f(2)
mpf('26.061679676630654')
from sympy.
That is why Aaron Meurer in his blog, which in repository is a pdf file .... writes
We now use lambdify to convert the SymPy expressions Z and D into functions that are evaluated using mpmath. A technical difficulty here is that the derivative of the zeta function
does not have a closed-form expression. mpmath's zeta can evaluate
but it doesn't yet work with sympy.lambdify (see SymPy issue 11802). So we have to manually define "Derivative" in lambdify, knowing that it will be the derivative of zeta when it is called. Beware that this is only correct for this specific expression where we know that Derivative will be Derivative(zeta(s), s).
from sympy.
This is the issue referenced in the blog post #11802
from sympy.
Thank you @asmeurer, I opened gh-26678. Let me know what you think.
from sympy.
Here is an example that breaks with 1.13.0rc1 and a similar error message:
import sympy
expr_str = "A*heaviside(x-n,0.5)"
expr = sympy.sympify(expr_str)
x = [symbol for symbol in expr.free_symbols if symbol.name == "x"][0]
variables = sympy.symbols([s.name for s in expr.free_symbols])
real_variables = sympy.symbols([s.name for s in variables], real=True)
# Replace symbols by real symbols to help with differentiation
# according to git comment 9 years ago
expr = expr.subs(
{orig: real_ for (orig, real_) in zip(variables, real_variables)}
)
parameters = [var for var in real_variables if var.name != "x"]
eval_expr = expr.evalf()
for p in parameters:
grad_expr = sympy.diff(eval_expr, p)
f_grad = sympy.utilities.lambdify(
real_variables,
grad_expr.evalf(),
modules="numpy",
dummify=False,
)
Error Traceback using bjodah:CodePrinter-customization-point-Derivative
Traceback (most recent call last):
Cell In[1], line 23
f_grad = sympy.utilities.lambdify(
File ~\mambaforge\lib\site-packages\sympy\utilities\lambdify.py:880 in lambdify
funcstr = funcprinter.doprint(funcname, iterable_args, _expr, cses=cses)
File ~\mambaforge\lib\site-packages\sympy\utilities\lambdify.py:1171 in doprint
str_expr = _recursive_to_string(self._exprrepr, expr)
File ~\mambaforge\lib\site-packages\sympy\utilities\lambdify.py:966 in _recursive_to_string
return doprint(arg)
File ~\mambaforge\lib\site-packages\sympy\printing\codeprinter.py:172 in doprint
lines = self._print(expr).splitlines()
File ~\mambaforge\lib\site-packages\sympy\printing\printer.py:331 in _print
return printmethod(expr, **kwargs)
File ~\mambaforge\lib\site-packages\sympy\printing\codeprinter.py:575 in _print_Mul
a_str = [self.parenthesize(x, prec) for x in a]
File ~\mambaforge\lib\site-packages\sympy\printing\codeprinter.py:575 in <listcomp>
a_str = [self.parenthesize(x, prec) for x in a]
File ~\mambaforge\lib\site-packages\sympy\printing\str.py:38 in parenthesize
return self._print(item)
File ~\mambaforge\lib\site-packages\sympy\printing\printer.py:331 in _print
return printmethod(expr, **kwargs)
File ~\mambaforge\lib\site-packages\sympy\printing\str.py:470 in _print_Subs
self._print(expr), self._print(old), self._print(new))
File ~\mambaforge\lib\site-packages\sympy\printing\printer.py:331 in _print
return printmethod(expr, **kwargs)
File ~\mambaforge\lib\site-packages\sympy\printing\codeprinter.py:470 in _print_Derivative
return self._print_not_supported(expr, extra_info=f"\nPrinter {self.__class__.__name__} has no method: {meth_name}")
File ~\mambaforge\lib\site-packages\sympy\printing\codeprinter.py:592 in _print_not_supported
raise PrintMethodNotImplementedError(
PrintMethodNotImplementedError: Unsupported by <class 'sympy.printing.numpy.NumPyPrinter'>: <class 'sympy.core.function.Derivative'>
Printer NumPyPrinter has no method: _print_Derivative_heaviside
Set the printer option 'strict' to False in order to generate partially printed code.
Similar error when replacing expr_str = "A*heaviside(x-n,0.5)"
with expr_str = "A * arctan(k * (x - x0))"
Example using arctan
import sympy
expr_str = "A * arctan(k * (x - x0))"
expr = sympy.sympify(expr_str)
x = [symbol for symbol in expr.free_symbols if symbol.name == "x"][0]
variables = sympy.symbols([s.name for s in expr.free_symbols])
real_variables = sympy.symbols([s.name for s in variables], real=True)
# Replace symbols by real symbols to help with differentiation
# according to git comment 9 years ago
expr = expr.subs(
{orig: real_ for (orig, real_) in zip(variables, real_variables)}
)
parameters = [var for var in real_variables if var.name != "x"]
eval_expr = expr.evalf()
for p in parameters:
grad_expr = sympy.diff(eval_expr, p)
f_grad = sympy.utilities.lambdify(
real_variables,
grad_expr.evalf(),
modules=["numpy"],
dummify=False,
)
Error traceback using bjodah:CodePrinter-customization-point-Derivative
Traceback (most recent call last):
Cell In[1], line 23
f_grad = sympy.utilities.lambdify(
File ~\mambaforge\lib\site-packages\sympy\utilities\lambdify.py:880 in lambdify
funcstr = funcprinter.doprint(funcname, iterable_args, _expr, cses=cses)
File ~\mambaforge\lib\site-packages\sympy\utilities\lambdify.py:1171 in doprint
str_expr = _recursive_to_string(self._exprrepr, expr)
File ~\mambaforge\lib\site-packages\sympy\utilities\lambdify.py:966 in _recursive_to_string
return doprint(arg)
File ~\mambaforge\lib\site-packages\sympy\printing\codeprinter.py:172 in doprint
lines = self._print(expr).splitlines()
File ~\mambaforge\lib\site-packages\sympy\printing\printer.py:331 in _print
return printmethod(expr, **kwargs)
File ~\mambaforge\lib\site-packages\sympy\printing\codeprinter.py:575 in _print_Mul
a_str = [self.parenthesize(x, prec) for x in a]
File ~\mambaforge\lib\site-packages\sympy\printing\codeprinter.py:575 in <listcomp>
a_str = [self.parenthesize(x, prec) for x in a]
File ~\mambaforge\lib\site-packages\sympy\printing\str.py:38 in parenthesize
return self._print(item)
File ~\mambaforge\lib\site-packages\sympy\printing\printer.py:331 in _print
return printmethod(expr, **kwargs)
File ~\mambaforge\lib\site-packages\sympy\printing\str.py:470 in _print_Subs
self._print(expr), self._print(old), self._print(new))
File ~\mambaforge\lib\site-packages\sympy\printing\printer.py:331 in _print
return printmethod(expr, **kwargs)
File ~\mambaforge\lib\site-packages\sympy\printing\codeprinter.py:470 in _print_Derivative
return self._print_not_supported(expr, extra_info=f"\nPrinter {self.__class__.__name__} has no method: {meth_name}")
File ~\mambaforge\lib\site-packages\sympy\printing\codeprinter.py:592 in _print_not_supported
raise PrintMethodNotImplementedError(
PrintMethodNotImplementedError: Unsupported by <class 'sympy.printing.numpy.NumPyPrinter'>: <class 'sympy.core.function.Derivative'>
Printer NumPyPrinter has no method: _print_Derivative_arctan
Set the printer option 'strict' to False in order to generate partially printed code.
from sympy.
Thank you @ericpre, this example is very useful. When I try to dig deeper here I do wonder if the previous generated functions actually worked? I tried to call f_grad
in your example with some real valued arguments but using earlier released versions of sympy, I get NameError
when calling those:
slightly modifed version of ericpre's reproducer
import sympy
def main(expr_str):
expr = sympy.sympify(expr_str)
x = [symbol for symbol in expr.free_symbols if symbol.name == "x"][0]
variables = sympy.symbols([s.name for s in expr.free_symbols])
real_variables = sympy.symbols([s.name for s in variables], real=True)
# Replace symbols by real symbols to help with differentiation
# according to git comment 9 years ago
expr = expr.subs(
{orig: real_ for (orig, real_) in zip(variables, real_variables)}
)
parameters = [var for var in real_variables if var.name != "x"]
eval_expr = expr.evalf()
print(f"{expr_str=} {real_variables}")
print('='*40)
print("")
for p in parameters:
grad_expr = sympy.diff(eval_expr, p)
gef = grad_expr.evalf()
f_grad = sympy.utilities.lambdify(
real_variables,
gef,
modules="numpy",
dummify=False,
)
print(f"{p=}\n {grad_expr=}\n {gef=}")
n = len(real_variables)
res = f_grad(*[i/(n+1) for i in range(n)])
if __name__ == '__main__':
for expr_str in ["A*heaviside(x-n,0.5)", "A * arctan(k * (x - x0))"]:
main(expr_str)
print("\n"*3)
output using e.g. sympy-1.11
expr_str='A*heaviside(x-n,0.5)' [A, n, x]
========================================
p=A
grad_expr=heaviside(-n + x, 0.5)
gef=heaviside(-n + x, 0.5)
p=n
grad_expr=-A*Subs(Derivative(heaviside(_xi_1, 0.5), _xi_1), _xi_1, -n + x)
gef=-A*Subs(Derivative(heaviside(_xi_1, 0.5), _xi_1), _xi_1, -n + x)
Traceback (most recent call last):
File "/home/bjorn/vc/sympy/gh26663_2156407342.py", line 38, in <module>
main(expr_str)
File "/home/bjorn/vc/sympy/gh26663_2156407342.py", line 34, in main
res = f_grad(*[i/(n+1) for i in range(n)])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<lambdifygenerated-2>", line 4, in _lambdifygenerated
NameError: name 'Subs' is not defined
If they did work, do you think you would be able to modify your example so that the function objects can be successfully evaluated with a previous release of sympy?
from sympy.
I can reproduce the error with older version of sympy (1.12), which indicates that this may be a different issue.
This functionality wasn't tested for the two expressions given in the example above, so it is very possible that it hasn't been working for a while. To give an idea if the reproducer is representative of the current usage, I added two expressions (gaussian and polynomial) that is covered in hyperspy test suite (and known to work) and they works fine in the reproducer.
Reproducer updated with example of expression that works
import sympy
def main(expr_str):
expr = sympy.sympify(expr_str)
x = [symbol for symbol in expr.free_symbols if symbol.name == "x"][0]
variables = sympy.symbols([s.name for s in expr.free_symbols])
real_variables = sympy.symbols([s.name for s in variables], real=True)
# Replace symbols by real symbols to help with differentiation
# according to git comment 9 years ago
expr = expr.subs(
{orig: real_ for (orig, real_) in zip(variables, real_variables)}
)
parameters = [var for var in real_variables if var.name != "x"]
eval_expr = expr.evalf()
print(f"{expr_str=} {real_variables}")
print('='*40)
print("")
for p in parameters:
grad_expr = sympy.diff(eval_expr, p)
gef = grad_expr.evalf()
f_grad = sympy.utilities.lambdify(
real_variables,
gef,
modules="numpy",
dummify=False,
)
print(f"{p=}\n {grad_expr=}\n {gef=}")
n = len(real_variables)
res = f_grad(*[i/(n+1) for i in range(n)])
if __name__ == '__main__':
for expr_str in [
"A * (1 / (sigma * sqrt(2*pi))) * exp(-(x - centre)**2 / (2 * sigma**2))",
"a1*x**1+a0",
"A*heaviside(x-n,0.5)",
"A * arctan(k * (x - x0))"
]:
main(expr_str)
print("\n"*3)
@francisco-dlp, any idea when the gradient stopped working with the arctan component in hyperspy?
from sympy.
Here is another example of this failing but only when using cse=True
:
In [8]: import sympy as sm
In [9]: t, a, b = sm.symbols('t, a, b')
In [10]: f = sm.Function('f')(t)
In [11]: expr = a*f.diff(t, 2) + b*f.diff(t) + a*b*f + a**2
In [12]: expr
Out[12]: a**2 + a*b*f(t) + a*Derivative(f(t), (t, 2)) + b*Derivative(f(t), t)
In [13]: func = sm.lambdify((f.diff(t, 2), f.diff(t), f, a, b), expr)
In [14]: func(2.0, 3.0, 4.0, 5.0, 6.0)
Out[14]: 173.0
In [15]: func?
Signature: func(_Dummy_40, _Dummy_41, _Dummy_42, a, b)
Docstring:
Created with lambdify. Signature:
func(arg_0, arg_1, f, a, b)
Expression:
a**2 + a*b*f(t) + a*Derivative(f(t), (t, 2)) + b*Derivative(f(t), t)
Source code:
def _lambdifygenerated(_Dummy_40, _Dummy_41, _Dummy_42, a, b):
return _Dummy_40*a + _Dummy_41*b + _Dummy_42*a*b + a**2
Imported modules:
File: ~/src/sympy/<lambdifygenerated-2>
Type: function
In [16]: func = sm.lambdify((f.diff(t, 2), f.diff(t), f, a, b), expr, cse=True)
---------------------------------------------------------------------------
PrintMethodNotImplementedError Traceback (most recent call last)
Cell In[16], line 1
----> 1 func = sm.lambdify((f.diff(t, 2), f.diff(t), f, a, b), expr, cse=True)
File ~/src/sympy/sympy/utilities/lambdify.py:880, 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)
882 # Collect the module imports from the code printers.
883 imp_mod_lines = []
File ~/src/sympy/sympy/utilities/lambdify.py:1171, in _EvaluatorPrinter.doprint(self, funcname, args, expr, cses)
1168 else:
1169 funcbody.append('{} = {}'.format(self._exprrepr(s), self._exprrepr(e)))
-> 1171 str_expr = _recursive_to_string(self._exprrepr, expr)
1173 if '\n' in str_expr:
1174 str_expr = '({})'.format(str_expr)
File ~/src/sympy/sympy/utilities/lambdify.py:966, in _recursive_to_string(doprint, arg)
963 from sympy.core.basic import Basic
965 if isinstance(arg, (Basic, MatrixBase)):
--> 966 return doprint(arg)
967 elif iterable(arg):
968 if isinstance(arg, list):
File ~/src/sympy/sympy/printing/codeprinter.py:172, in CodePrinter.doprint(self, expr, assign_to)
169 self._not_supported = set()
170 self._number_symbols = set()
--> 172 lines = self._print(expr).splitlines()
174 # format the output
175 if self._settings["human"]:
File ~/src/sympy/sympy/printing/printer.py:331, in Printer._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)
File ~/src/sympy/sympy/printing/str.py:57, in StrPrinter._print_Add(self, expr, order)
55 l = []
56 for term in terms:
---> 57 t = self._print(term)
58 if t.startswith('-') and not term.is_Add:
59 sign = "-"
File ~/src/sympy/sympy/printing/printer.py:331, in Printer._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)
File ~/src/sympy/sympy/printing/codeprinter.py:565, in CodePrinter._print_Mul(self, expr)
563 a_str = [self.parenthesize(a[0], 0.5*(PRECEDENCE["Pow"]+PRECEDENCE["Mul"]))]
564 else:
--> 565 a_str = [self.parenthesize(x, prec) for x in a]
566 b_str = [self.parenthesize(x, prec) for x in b]
568 # To parenthesize Pow with exp = -1 and having more than one Symbol
File ~/src/sympy/sympy/printing/codeprinter.py:565, in <listcomp>(.0)
563 a_str = [self.parenthesize(a[0], 0.5*(PRECEDENCE["Pow"]+PRECEDENCE["Mul"]))]
564 else:
--> 565 a_str = [self.parenthesize(x, prec) for x in a]
566 b_str = [self.parenthesize(x, prec) for x in b]
568 # To parenthesize Pow with exp = -1 and having more than one Symbol
File ~/src/sympy/sympy/printing/str.py:38, in StrPrinter.parenthesize(self, item, level, strict)
36 return "(%s)" % self._print(item)
37 else:
---> 38 return self._print(item)
File ~/src/sympy/sympy/printing/printer.py:331, in Printer._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)
File ~/src/sympy/sympy/printing/codeprinter.py:582, in CodePrinter._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:
585 self._not_supported.add(expr)
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.
from sympy.
Note that it also fails in SymPy 1.12.1 but with a NameError when trying to evaluate the numerical function:
In [7]: func = sm.lambdify((f.diff(t, 2), f.diff(t), f, a, b), expr, cse=True)
In [8]: func(2.0, 3.0, 4.0, 5.0, 6.0)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[8], line 1
----> 1 func(2.0, 3.0, 4.0, 5.0, 6.0)
File <lambdifygenerated-2>:6, in _lambdifygenerated(_Dummy_39, _Dummy_40, _Dummy_41, a, b)
1 def _lambdifygenerated(_Dummy_39, _Dummy_40, _Dummy_41, a, b):
2 x0 = _Dummy_41
3 return ( # Not supported in Python with SciPy and NumPy:
4 # Derivative
5 # Derivative
----> 6 a**2 + a*b*x0 + a*Derivative(x0, (t, 2)) + b*Derivative(x0, t))
NameError: name 'Derivative' is not defined
In [9]: func?
Signature: func(_Dummy_39, _Dummy_40, _Dummy_41, a, b)
Docstring:
Created with lambdify. Signature:
func(arg_0, arg_1, f, a, b)
Expression:
a**2 + a*b*f(t) + a*Derivative(f(t), (t, 2)) + b*Derivative(f(t), t)
Source code:
def _lambdifygenerated(_Dummy_39, _Dummy_40, _Dummy_41, a, b):
x0 = _Dummy_41
return ( # Not supported in Python with SciPy and NumPy:
# Derivative
# Derivative
a**2 + a*b*x0 + a*Derivative(x0, (t, 2)) + b*Derivative(x0, t))
Imported modules:
File: ~/src/sympy/<lambdifygenerated-2>
Type: function
from sympy.
Thanks! So in order to debug this, I'd need some help finding an expression that:
- for an older release of sympy: works with lambdify (i.e. no NameError when evaluated)
- for
master
branch (or sympy-1.13.0rc1): fails withPrintMethodNotImplementedError
I have not yet seen such an expression.
from sympy.
This expression expr = a*f.diff(t, 2) + b*f.diff(t) + a*b*f + a**2
fails with NameError in SymPy 1.12.1 and fails with the new error PrintMethodNotImplementedError in master. But I guess that is a better behavior now. Your new error handling finds the error when calling lambdify()
and gives a more informative error.
The bug is that it works without cse=True
but doesn't with cse=True
. I may have opened a different issue about this because I remember trying to fix it but it would take a bit of surgery.
from sympy.
Thank you @moorepants , the failure with cse=True
should definitely be fixed. I'll see if I can figure out how to patch lambdify.
from sympy.
@moorepants the failure case with cse=True
is hopefully addressed in 8fb5fb6 (in gh-26678)
from sympy.
Thanks for also looking into that. Much appreciated.
from sympy.
Updating the example above, does it make sense that it works with atan
but not arctan
?
Atan versus Arctan
import sympy
def main(expr_str):
expr = sympy.sympify(expr_str)
x = [symbol for symbol in expr.free_symbols if symbol.name == "x"][0]
variables = sympy.symbols([s.name for s in expr.free_symbols])
real_variables = sympy.symbols([s.name for s in variables], real=True)
# Replace symbols by real symbols to help with differentiation
# according to git comment 9 years ago
expr = expr.subs(
{orig: real_ for (orig, real_) in zip(variables, real_variables)}
)
parameters = [var for var in real_variables if var.name != "x"]
eval_expr = expr.evalf()
print(f"{expr_str=} {real_variables}")
print('='*40)
print("")
for p in parameters:
grad_expr = sympy.diff(eval_expr, p)
gef = grad_expr.evalf()
f_grad = sympy.utilities.lambdify(
real_variables,
gef,
modules="numpy",
dummify=False,
)
print(f"{p=}\n {grad_expr=}\n {gef=}")
n = len(real_variables)
res = f_grad(*[i/(n+1) for i in range(n)])
if __name__ == '__main__':
for expr_str in [
"A * atan(k * (x - x0))",
"A * arctan(k * (x - x0))"
]:
main(expr_str)
print("\n"*3)
from sympy.
@ericpre the difference is that "atan" is recognized as the trigonometric function, while "arctan" is not:
>>> srepr(sympify('arctan(x)'))
"Function('arctan')(Symbol('x'))"
>>> srepr(sympify('atan(x)'))
"atan(Symbol('x'))"
parse_expr
can offer more customization:
>>> parse_expr('atan(x)', transformations=(), local_dict={'x': x})
atan(x)
>>> parse_expr('atan(x)', transformations=(), local_dict={'x': x})
...
NameError: name 'arctan' is not defined
EDIT: and atan(x).diff(x)
becomes 1/(x**2 + 1)
, while arctan(x).diff(x)
becomes Derivative(arctan(x), (x, 1))
.
from sympy.
Thank you @bjodah, things are getting slowly more clear and for the case of the arctan
, it seems that it was a correct failure and it possibly never worked before!
With the heaviside
function, it seems that this is similar, the heaviside
wasn't recognised correctly, however, after fixing it (replacing heaviside
with Heaviside
), there is a different error:
import sympy
expr = sympy.sympify('A*Heaviside(x-n,0.5)')
expr_grad_n = expr.evalf().diff("n")
variables = sympy.symbols([s.name for s in expr.free_symbols])
f_grad = sympy.utilities.lambdify(variables, expr_grad_n.evalf(), dummify=False)
n = len(variables)
res = f_grad(1, 2, 3)
gives the following error:
Traceback (most recent call last):
Cell In[1], line 10
res = f_grad(1, 2, 3)
File <lambdifygenerated-1>:2 in _lambdifygenerated
return -A*DiracDelta(-n + x)
NameError: name 'DiracDelta' is not defined
I get the same error with sympy 1.12 and 1.13.0rc1, so this doesn't seem to be a regression. Should I open a separate issue?
from sympy.
@ericpre glad to hear.
Yes, please open new issues if you find a failure mode that was present even before PrintMethodNotImplementedError
. As for DiracDelta
I remember that its support in lambdify has been discussed (you can search for DiracDelta and lambdify, I couldn't immediately find the comment I was thinking of) and it was decided that we should not generate code for it by default.
The rationale is rather simple: people generally want useful (floating point) results when using lambdify, having e.g. a python code printer emit something like (float('inf') if x==0 else 0.0)
is generally not helpful, and it can be quite hard to debug later in the process if expressions are big ("why is my nonlinear solver not converging?").
If this really is something that a user wants, then they can pass this explicitly as a user-known function to lambdify
.
from sympy.
Related Issues (20)
- Feature Request: Add Import/Export Functions for Boolean Expressions in Z3 and BDD Formats HOT 15
- Wrong translation of lerchphi into Mathematica HOT 3
- Add a factors function to get factors without multiplicity HOT 7
- collected_expr.coeff() always return 0 if some symbol was used as a Divisor? HOT 2
- Solution from dsolve_system violates all equations HOT 2
- Sympify broken for latest numpy numbers HOT 4
- Control test fails with numpy 2.0 HOT 15
- Investigation about the forward_jacobian Function HOT 27
- Pretty printer occasionally raises TypeError when displaying vectors from the mechanics module on Jupyter Notebook HOT 7
- ufuncify fails with NumPy 2.0 for specific symbol names HOT 1
- Add a function to convert algebraic numbers to RootOf HOT 1
- Sparse Matrix Multiplication Performance HOT 4
- function_range is not robust with trigonometric functions HOT 3
- Automatic simplification of expressions involving `AlgebraicNumber` HOT 2
- Incorrect result for definite integral related to gaussian integral HOT 1
- SymPy pauli algebra should simplify terms if labels are in a different order?
- Missing Pre-release Tags for 1.13.0rc3 etc HOT 4
- Printing Parsed Mathematica Code exception HOT 1
- npartitions incorrect for n=11160 HOT 2
- Not all time derivatives are replaced when forming Kane's equations HOT 9
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from sympy.