Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Preprocess strings containing relational operators in sympify #1378

Closed
wants to merge 1 commit into from

8 participants

@Krastanov
Collaborator

Now this works:

In [1]: sympify('a<b')
Out[1]: a < b

In [2]: sympify('a==b')
Out[2]: a = b

In [3]: sympify('sin(a)==b')
Out[3]: sin(a) = b

etc...

Fixes issue 1625

sympy/parsing/sympy_parser.py
@@ -167,3 +168,12 @@ def parse_expr(s, local_dict=None, rationalize=False, convert_xor=False):
return expr.xreplace({C.Symbol(kern): 1})
except (TypeError, AttributeError):
return expr
+
+
+def _parse_relationals(expr_string):
+ """Transform strings containing relational operators into strings calling Rel"""
+ match = re.match(r'(?P<lhs>.*?)(?P<rel>>=|>|<=|<|==|!=)(?P<rhs>.*)', expr_string)
@smichr Collaborator
smichr added a note

Should "<>" be added, too? (It's equivalent to !=)

@Krastanov Collaborator

I was unaware of it. I will add it now.

@asmeurer Owner

It's been dropped in Python 3 though.

@Krastanov Collaborator

I was thinking that too, however Relational supports it and I preferred to keep it consistent with it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Krastanov Krastanov Preprocess strings containing relational operators in sympify
Now this works:

```
In [1]: sympify('a<b')
Out[1]: a < b

In [2]: sympify('a==b')
Out[2]: a = b

In [3]: sympify('sin(a)==b')
Out[3]: sin(a) = b
```
etc...

Fixes issue 1625
c45ea55
@vext01

Thanks for working on this Krastanov. For ages now my solution has been:

if "=" in f and "<=" not in f and ">=" not in f:
    (lhs, rhs) = f.split("=")
    expr = sympy.Eq(sympy.sympify(lhs), sympy.sympify(rhs))
else:
    expr = sympy.sympify(f)

And boy am I glad to see the end of this!

@Krastanov
Collaborator

@vext01, the community is very open to contributions so do not shy from sending fixes for stuff that causes you headaches.

@Krastanov
Collaborator

SymPy Bot Summary: :red_circle: There were test failures.

@Krastanov: Please fix the test failures.

Test results html report: http://reviews.sympy.org/report/agZzeW1weTNyDAsSBFRhc2sYnuwdDA

Interpreter: /opt/pym32/bin/python2.5 (2.5.6-final-0)
Architecture: Linux (32-bit)
Cache: yes
Test command: setup.py test
master hash: 804ddef
branch hash: c45ea55

Automatic review by SymPy Bot.

@Krastanov
Collaborator

SymPy Bot Summary: :red_circle: There were test failures.

@Krastanov: Please fix the test failures.

Test results html report: http://reviews.sympy.org/report/agZzeW1weTNyDAsSBFRhc2sYx9wdDA

Interpreter: /usr/bin/python2.7 (2.7.3-candidate-2)
Architecture: Linux (64-bit)
Cache: yes
Test command: setup.py test
master hash: 804ddef
branch hash: c45ea55

Automatic review by SymPy Bot.

@Krastanov
Collaborator

SymPy Bot Summary: :red_circle: There were test failures.

@Krastanov: Please fix the test failures.

Test results html report: http://reviews.sympy.org/report/agZzeW1weTNyDAsSBFRhc2sYwrUdDA

Interpreter: /usr/bin/python3 (3.2.3-candidate-2)
Architecture: Linux (64-bit)
Cache: yes
Test command: setup.py test
master hash: 804ddef
branch hash: c45ea55

Automatic review by SymPy Bot.

@rlamy
Collaborator

I'm not sure about this. It causes string sympification to become seriously inconsistent with builtin eval(). Currently, sympify(string) is basically the same as sympify(eval(string, some_magic_namespace)). With this change, the string x == y gives Eq(x, y) with the first method and False with the second.

@Krastanov
Collaborator
@asmeurer
Owner

Maybe add a flag that does this, but leave it off by default. I think that will keep things from breaking, but should work for those people that want this feature.

And it may be a good idea from a design point of view to try to separate those things that are "additions" to the Python eval, including the factorial parser, and maybe the ^ to ** thing too. But we don't really have as much parsing yet as we ought to, so it's hard to see what will work best at this point.

@catchmrbharath

+1 for this change. Plotting implicit equations will be quite easy It will be simply,
plot_implicit(y==x, (x, -3, 3), (y, -3, 3)) instead of plot_implicit(Eq(y, x), (x, -3, 3), (y, -3, 3))

@Krastanov
Collaborator
@catchmrbharath

oh. Yes.

@certik
Owner

I am +1 to this change. It's a simple change that is easy to maintain and it provides a useful fix to many people.

@Krastanov
Collaborator

@asmeurer, @rlamy, a few people agreed on merging this. You were a bit more reserved. I dislike the idea to add a flag only for this (a flag I believe should include also ^ to ** etc, however refactoring will take more time).

So how about a more general flag permit_strings that simply removes the possibility to use strings and will imply parsing when set to True?

@asmeurer
Owner

There is already a flag for ^.

@Krastanov
Collaborator

Then I will use the same flag (and maybe rename it). Is this ok?

@asmeurer
Owner

I think it's better to keep them separate. Also, it wouldn't make sence if ^ replacement is on by default but == replacement is off by default.

@rlamy
Collaborator

This can't be on by default because it would break backwards compatibility. And -1 for adding yet another flag to sympify() which would slow down every call, and add a very specialised argument to the signature of this fundamental function.

@Krastanov
Collaborator
@smichr
Collaborator
@rlamy
Collaborator

@Krastanov: +1 to your plan.

@rlamy
Collaborator

@smichr AFAIR, str is rather special (in part because it's the fallback) and can't use the converter mechanism unless sympify() is refactored with some changes in its behaviour.

@asmeurer
Owner

That also sounds like a good plan to me. Like I said elsewhere, we really need a lot more parsing implemented to really see what the best design is for this sort of thing.

@Krastanov
Collaborator

SymPy Bot Summary: :red_circle: Failed after merging Krastanov/sympify_relational (c45ea55) into master (a184841).
@Krastanov: Please fix the test failures.
:red_circle: Python 2.5.6-final-0: fail
:red_circle: Python 2.7.3-candidate-2: fail
:red_circle: Python 3.2.3-candidate-2: fail
:red_circle:Sphinx 1.1.3: fail

@jrioux
Collaborator

SymPy Bot Summary: :red_circle: Failed after merging Krastanov/sympify_relational (c45ea55) into master (c49dca1).
@Krastanov: Please fix the test failures.
:red_circle: Python 2.7.2-final-0: fail
:red_circle: Python 3.2.1-final-0: fail
:eight_spoked_asterisk:Sphinx 1.1.3: pass
Docs build command: make clean && make html-errors && make clean && make latex && cd _build/latex && xelatex -interaction=nonstopmode sympy-*.tex

@Krastanov
Collaborator

I am closing this as the consensus was that this is not the correct way to implement it.

Check #1541 for the version that does not mess with sympify.

@Krastanov Krastanov closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jun 22, 2012
  1. @Krastanov

    Preprocess strings containing relational operators in sympify

    Krastanov authored
    Now this works:
    
    ```
    In [1]: sympify('a<b')
    Out[1]: a < b
    
    In [2]: sympify('a==b')
    Out[2]: a = b
    
    In [3]: sympify('sin(a)==b')
    Out[3]: sin(a) = b
    ```
    etc...
    
    Fixes issue 1625
This page is out of date. Refresh to see the latest.
Showing with 21 additions and 0 deletions.
  1. +11 −0 sympy/core/tests/test_sympify.py
  2. +10 −0 sympy/parsing/sympy_parser.py
View
11 sympy/core/tests/test_sympify.py
@@ -4,6 +4,7 @@
from sympy.abc import x, y
from sympy.core.sympify import sympify, _sympify, SympifyError
from sympy.core.decorators import _sympifyit
+from sympy.core.relational import Rel
from sympy.utilities.pytest import XFAIL, raises
from sympy.utilities.decorator import conserve_mpmath_dps
from sympy.geometry import Point, Line
@@ -403,3 +404,13 @@ def clean(s):
s = '-1 - 2*(-(-x + 1/x)/(x*(x - 1/x)**2) - 1/(x*(x - 1/x)))'.replace('x', '_kern')
ss = S(s)
assert ss != 1 and ss.simplify() == -1
+
+def test_relational():
+ assert S('sin(x)*x/5*5! <= y') == Rel(24*sin(x)*x, y, '<=')
+ assert S('sin(x)*x/5*5! != y') == Rel(24*sin(x)*x, y, '!=')
+ assert S('sin(x)*x/5*5! <> y') == Rel(24*sin(x)*x, y, '<>')
+ assert S('sin(x)*x/5*5! == y') == Rel(24*sin(x)*x, y, '==')
+ assert S('sin(x)*x/5*5! >= y') == Rel(24*sin(x)*x, y, '>=')
+ assert S('sin(x)*x/5*5! > y') == Rel(24*sin(x)*x, y, '>')
+ assert S('sin(x)*x/5*5! < y') == Rel(24*sin(x)*x, y, '<')
+
View
10 sympy/parsing/sympy_parser.py
@@ -159,6 +159,7 @@ def parse_expr(s, local_dict=None, rationalize=False, convert_xor=False):
hit = kern in s
code = _transform(s.strip(), local_dict, global_dict, rationalize, convert_xor)
+ code = _parse_relationals(code)
expr = eval(code, global_dict, local_dict) # take local objects in preference
if not hit:
@@ -167,3 +168,12 @@ def parse_expr(s, local_dict=None, rationalize=False, convert_xor=False):
return expr.xreplace({C.Symbol(kern): 1})
except (TypeError, AttributeError):
return expr
+
+
+def _parse_relationals(expr_string):
+ """Transform strings containing relational operators into strings calling Rel"""
+ match = re.match(r'(?P<lhs>.*?)(?P<rel>==|<>|>=|<=|>|<|!=)(?P<rhs>.*)', expr_string)
+ if match:
+ return "relational.Rel(%(lhs)s, %(rhs)s, '%(rel)s')" % match.groupdict()
+ else:
+ return expr_string
Something went wrong with that request. Please try again.