sympy icon indicating copy to clipboard operation
sympy copied to clipboard

Error when parsing ϵ into an expression

Open bgyori opened this issue 1 year ago • 6 comments

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.

bgyori avatar Feb 16 '24 02:02 bgyori

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

bgyori avatar Feb 16 '24 03:02 bgyori

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.

MostlyKIGuess avatar Feb 16 '24 17:02 MostlyKIGuess

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 avatar Feb 16 '24 17:02 MostlyKIGuess

@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.

bgyori avatar Feb 16 '24 20:02 bgyori

The normalization of Unicode characters described here seems relevant https://www.asmeurer.com/python-unicode-variable-names/

bgyori avatar Feb 16 '24 20:02 bgyori

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]: ϵ

bgyori avatar Feb 17 '24 01:02 bgyori