sympy
sympy copied to clipboard
Error when parsing ϵ into an expression
I am running into an issue parsing expressions from strings with the ϵ character appearing in them. The following minimal example reproduces this issue. It is worth noting that while the character appearing in my code is 'ϵ' (\u03f5), the NameError refers to 'ε' (\u03b5), which is a different unicode character.
In [1]: import sympy
In [2]: eps = 'ϵ'
In [3]: sympy.parse_expr(eps, local_dict={eps: sympy.Symbol(eps)})
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
ValueError: Error from parse_expr with transformed code: 'ϵ '
The above exception was the direct cause of the following exception:
NameError Traceback (most recent call last)
Cell In[3], line 1
----> 1 sympy.parse_expr(eps, local_dict={eps: sympy.Symbol(eps)})
File ~/.virtualenvs/py310/lib/python3.10/site-packages/sympy/parsing/sympy_parser.py:1087, in parse_expr(s, local_dict, transformations, global_dict, evaluate)
1085 for i in local_dict.pop(null, ()):
1086 local_dict[i] = null
-> 1087 raise e from ValueError(f"Error from parse_expr with transformed code: {code!r}")
File ~/.virtualenvs/py310/lib/python3.10/site-packages/sympy/parsing/sympy_parser.py:1078, in parse_expr(s, local_dict, transformations, global_dict, evaluate)
1075 code = compile(evaluateFalse(code), '<string>', 'eval') # type: ignore
1077 try:
-> 1078 rv = eval_expr(code, local_dict, global_dict)
1079 # restore neutral definitions for names
1080 for i in local_dict.pop(null, ()):
File ~/.virtualenvs/py310/lib/python3.10/site-packages/sympy/parsing/sympy_parser.py:906, in eval_expr(code, local_dict, global_dict)
900 def eval_expr(code, local_dict: DICT, global_dict: DICT):
901 """
902 Evaluate Python code generated by ``stringify_expr``.
903
904 Generally, ``parse_expr`` should be used.
905 """
--> 906 expr = eval(
907 code, global_dict, local_dict) # take local objects in preference
908 return expr
File <string>:1
NameError: name 'ε' is not defined
I am on sympy 1.12.
A follow-up comment that this seems to come down to eval's behavior:
In [1]: eval('ε', {'ε': 1})
Out[1]: 1
In [2]: eval('ϵ', {'ϵ': 1})
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[2], line 1
----> 1 eval('ϵ', {'ϵ': 1})
File <string>:1
NameError: name 'ε' is not defined
try :
import sympy
eps = 'ϵ'
eps_replaced = eps.replace('ϵ', 'ε')
sympy.parse_expr(eps_replaced, local_dict={eps: sympy.Symbol(eps_replaced)})
It works on my system without doing this aswell.
Should I write a function inside the library and submit a pull request, doesn't seem to be that big of a deal, if any contributor says so I am ready to do it
@MostlyKIGuess thanks for the response. I think it would be good to solve this in sympy, and an ideal solution would preserve the name of the symbol that was originally part of the input string. This can definitely be done using replacements surrounding the call to eval. Though I'm not familiar with the details, presumably there is a more extensive set of similar mappings that Python applies, not just the character in the example above.
The normalization of Unicode characters described here seems relevant https://www.asmeurer.com/python-unicode-variable-names/
I submitted a PR with a more general fix for this, and added a regression test. With the changes on the PR, I get
In [1]: import sympy
In [2]: eps = 'ϵ'
In [3]: sympy.parse_expr(eps, local_dict={eps: sympy.Symbol(eps)})
Out[3]: ϵ