Search code examples
pythonparsingsympysymbolic-mathinequality

Reliable way to extract values from (parse) Sympy result


I'm using SymPy, a Python library for symbolic mathematics, to reduce inequalities which are programmatically read in, for example

from sympy.solvers.inequalities import reduce_rational_inequalities

# In reality, these inequalities are read in programmatically
inequality1 = 'x - 0.5 >= 0.0'
inequality2 = '0.49 - x**2 >= 0.0'

listOfInequalities = [sympy.parse_expr(inequality1), sympy.parse_expr (inequality2)]

ReducedInequalities = reduce_rational_inequalities([listOfInequalities], x)

which produces the correct result corresponding to x being between 0.5 and 0.7:

0.5≤𝑥∧𝑥≤0.7

Or, expressed as a string:

strReducedInequalities = str(ReducedInequalities)
\begin{equation*}(0.5 <= x) & (x <= 0.7)\end{equation*}

My question is, is there a reliable way to parse such a result to extract the constants 0.5 and 0.7? I can search the result strReducedInequalities, or possibly use regular expressions, to find the positions of for strings such as '(' and '<= x' to extract the 0.5 for example. But that seems brittle: If SymPy were to change its output formatting, the code could break.

What would be more reliable is a structured way to traverse the output, for example strReducedInequalities could be expressed as

[expression1, joiner, expression2]

where

  • expression1 represents (0.5 <= x)
  • joiner represents &
  • expression2 represents (x <= 0.7)

and then expression1 might be broken down as

[constant, inequality_type, variable]

where

  • constant = 0.5
  • inequality_type represents <= (less than or equal to)
  • variable = x

Solution

Here's how I implemented the excellent solution from @smichr:

from sympy import *
from sympy.core.relational import Relational
x = symbols('x')
eq = solve([x >= 0.5, x <= 0.7])
[(i.rhs, i.rel_op, i.lhs) for i in [i.canonical for i in eq.atoms(Relational)]]

Or using programatically-generated list approach in my original question:

from sympy import *
from sympy.core.relational import Relational
x = symbols('x')

# In reality, these inequalities are read in programmatically
inequality1 = 'x - 0.5 >= 0.0'
inequality2 = '0.49 - x**2 >= 0.0'

listOfInequalities = [parse_expr(inequality1), parse_expr (inequality2)]
eq = solve(listOfInequalities)
[(i.rhs, i.rel_op, i.lhs) for i in [i.canonical for i in eq.atoms(Relational)]]

Result using either approach:

[(0.500000000000000, '>=', x), (0.700000000000000, '<=', x)]


Solution

  • If the Relationals have been solved then there is a symbol on one side and the value on the other and the canonical method will put them in order so the symbol is on the left:

    >>> eq
    (1/2 <= x) & (x <= 7/10)
    >>> [(i.rhs, i.rel_op, i.lhs) for i in [i.canonical for i in eq.atoms(Relational)]]
    [(7/10, '<=', x), (1/2, '>=', x)]