-
-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Guide: Parsing expressions
This guide explains how to programmatically extract information from an expression or equation.
For example, you have a system of inequalities of a single variable, x. You want to solve the system of inequalities to determine the bounds of that variable.
[^footnote]: The solutions of sympy.solve()
are easier to parse than those of solve_set()
. You may be able to convert a solution of solve_set()
using solvify()
to be similar to the solution of solve()
.
Specifically, you want to solve the system of inequalities 10 * x >= 5, 10 * x <= 7. The solution will be (1/2 <= x) & (x <= 7/10). So the lower bound of x is 1/2, and the upper bound is 7/10.
Solution: sympy.solve()
returns a solution, which you can access in a couple ways depending on what you want to do with it.
(1) If you want to view the solution, you can output it as a string, for example (1/2 <= x) & (x <= 7/10)
.
(2) If you want to process the solution programmatically, you can use its atomic structure to extract information. Atoms are objects, for example x
and 1/2
. By default, the .atoms()
method returns only objects that are truly atomic and cannot be divided into smaller pieces are returned: symbols, numbers, and number symbols like I and pi. It is possible to request atoms of any type, however.
For this situation, you will use SymPy's relational atoms. Relational atoms express the relationship between objects, for example (1/2 <= x)
expresses that 1/2 is less than or equal to x.
These relational atoms can be programmatically accessed to extract desired values. Here's how to do that.
Import SymPy modules
>>> from sympy import parse_expr, solve
Set up inequality strings
>>> inequality_str1 = '10*x >= 5'
>>> inequality_str2 = '10*x <= 7'
Parse the strings into inequalities that SymPy understands:
>>> inequality1 = parse_expr(inequality_str1)
>>> inequality2 = parse_expr(inequality_str2)
Make a system of the inequalities by putting them into a Python list:
>>> inequalities = [inequality1, inequality2]
Call solve()
>>> solve(inequalities)
(1/2 <= x) & (x <= 7/10)
Notice that SymPy has automatically reduced the fraction 5/10 to 1/2.
Save the solution to a variable:
>>> solution = solve(inequalities)
If you want to manually verify the solution, call that variable:
>>> solution
(1/2 <= x) & (x <= 7/10)
You can programatically extract information from the solution using .atoms()
, which returns the set of all the atoms. Without any arguments, .atoms()
returns all indivisible entities (once) in the solution:
>>> solution.atoms()
{x, 7/10, 1/2}
In this case, you want to extract information about the relations, so you extract the relational atoms by using the Relations argument for .atoms()
:[^footnote2]
[^footnote2] We recognize that the atoms
method is poorly named when used for a relational expression such as 1/2 <= x
.
>>> from sympy.core.relational import Relational
>>> relational_atoms = solution.atoms(Relational)
>>> relational_atoms
{1/2 <= x, x <= 7/10}
Now you can extract information about the atoms:
>>> for atom in relational_atoms:
... print(atom)
1/2 <= x
x <= 7/10
A Relational atom, for example 1/2 <= x
, has three named properties of interest:
-
lhs
: the left hand side of the relation, for example 1/2 (value) -
rel_op
: the relational operator, for example '<=' (string). Other options are '<', '>=', and '>'. -
rhs
: the right hand side of the relation, for example x (symbol)
You can extract the three properties of each atom:
>>> for atom in relational_atoms:
... print([atom.lhs, atom.rel_op, atom.rhs])
[1/2, '<=', x]
[x, '<=', 7/10]
For this purpose, you want to extract the values (1/2 and 7/10), not the symbol (x). To ensure the symbol is always on the left side, get the canonical form of each relational atom using .canonical:
>>> for atom in relational_atoms:
... print(atom.canonical)
x >= 1/2
x <= 7/10
Now you can extract the values into a list:
>>> values = []
>>> for atom in relational_atoms:
... values.append(atom.canonical.rhs)
>>> values
[7/10, 1/2]
To determine the lower and upper bounds, use min
and max
as you would on any Python list:
>>> lower_bound = min(values)
>>> lower_bound
1/2
>>> upper_bound = max(values)
>>> upper_bound
7/10
You can also extract atoms of given types, for example:
Numbers:
>>> from sympy import Number
>>> solution.atoms(Number)
{7/10, 1/2}
Symbols:
>>> from sympy import Symbol
>>> solution.atoms(Symbol)
{x}
```py